/* Brixwood Builders — shared stylesheet
   Tokens transcribed from the finalized design (tokens.css / CLAUDE.md).
   Colour is themed via [data-theme]; type, space, radius and motion are not. */

/* ------------------------------------------------------------------ fonts */
/* Raleway and Montserrat, self-hosted variable fonts (latin), OFL 1.1.
   Licence text: assets/fonts/OFL-Raleway.txt, assets/fonts/OFL-Montserrat.txt */
@font-face {
  font-family: "Raleway";
  src: url("../fonts/raleway-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/montserrat-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ------------------------------------------------------------------ tokens */
/* The light set is also reachable by attribute, so a page that has no business
   having a dark mode can ask for it on its own body and keep the light values
   whatever the site theme is — see services.html. Written once either way: the
   attribute does not restate a colour, it re-applies this block further down
   the tree, where plain inheritance beats whatever :root was given. */
:root,
[data-theme="light"] {
  /* colour — light */
  --bg: #E9E9E9;
  --ink: #17181A;
  --ink-2: #4A4A46;
  --ink-3: #6E6E68;

  --surf: #17181A;
  --surf-2: #1C1C1A;
  --on-surf: #E4E4DE;
  --on-surf-2: #8E8E86;
  --on-surf-3: #B9B9B3;

  --hair: #B4B4AE;
  --hair-2: #A9A9A3;

  --accent: #204860;
  --accent-hover: #2F6789;
  --accent-ink: #E4E4DE;

  --glow: #E4E4DE;

  /* How much --surf is laid over a services photograph, so the copy on top of
     it stays readable. The tint is the same ink as the striped plate behind
     it, so a photograph and a placeholder sit in the same key. One number,
     turned up to sink the photo further back, down to let it come forward.
     Deliberately not restated under dark: one strength everywhere. */
  --photo-tint: 75%;

  /* dot field (enquire page) */
  --dot: var(--hair-2);
  --dot-size: 1.2px;
  --dot-opacity: 0.62;

  /* control surfaces (enquire page): opaque on grey */
  --fill: var(--bg);
  --fill-edge: var(--accent);
  --lift:
    0 1px 1px rgba(23, 24, 26, 0.10),
    0 3px 8px rgba(23, 24, 26, 0.09),
    0 10px 22px rgba(23, 24, 26, 0.07);
  --lift-2:
    0 2px 2px rgba(23, 24, 26, 0.12),
    0 6px 14px rgba(23, 24, 26, 0.13),
    0 18px 38px rgba(23, 24, 26, 0.11);
  --lift-3:
    0 2px 6px color-mix(in srgb, var(--ink) 10%, transparent),
    0 14px 32px color-mix(in srgb, var(--ink) 12%, transparent),
    0 44px 88px color-mix(in srgb, var(--ink) 14%, transparent);

  /* typography */
  --font-display: "Raleway", "Helvetica Neue", Arial, sans-serif;
  --font-heading: "Raleway", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Montserrat", "Helvetica Neue", Arial, sans-serif;

  --weight-display: 800;
  --weight-heading: 600;
  --weight-eyebrow: 500;
  --weight-body: 450;
  --weight-secondary: 350;

  /* frame */
  --page-pad-x: 56px;
  --page-pad-x-fluid: clamp(24px, 6vw, 56px);
  --edge-pad-x: 28px;
  --logo-top: 20px;
  --nav-top: 16.2px;
  --nav-height: 36px;
  --nav-gap: 21.6px;
  --nav-gap-toggle: 31px;
  --content-max: 1240px;
  --measure-flow: 720px;
  --grid-cell: 52px;
  --progress-track-w: 220px;

  /* line, radius, target */
  --track-w: 1.5px;
  --radius: 15px;
  --radius-cover: 22px;
  --radius-pill: 999px;
  --tap-min: 44px;
  --cta-height: 48px;

  /* motion */
  --ease: cubic-bezier(.22, .61, .36, 1);
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --dur-enter: 420ms;
  --dur-enter-delay: 90ms;
}

:root[data-theme="dark"] {
  --bg: #081117;
  --ink: #E4E4DE;
  --ink-2: #AFBCC3;
  --ink-3: #8C9AA2;

  --surf: #16303D;
  --surf-2: #1A3846;
  --on-surf: #E4E4DE;
  --on-surf-2: #7E939E;
  --on-surf-3: #9FB0B9;

  --hair: #2A4756;
  --hair-2: #33586A;

  --accent: #5E9CC2;
  --accent-hover: #7BB2D3;
  --accent-ink: #08222F;

  --glow: #33586A;


  --dot: var(--hair);
  --dot-size: 1.1px;
  --dot-opacity: 0.6;

  --fill: var(--bg);
  /* Dark carries the same accent border hierarchy as light — the tray at 2px
     outranking its options at 1px. The shadows stay off: --lift is built from
     --ink, which inverts to a pale glow on a dark ground, so it would be
     wasted code here. Border weight alone carries the hierarchy. */
  --fill-edge: var(--accent);
  --lift: none;
  --lift-2: none;
}

/* ------------------------------------------------------------------ reset */
* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  color: var(--ink);
}

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--ink-3); }
img { max-width: 100%; }
::selection { background: var(--surf); color: var(--bg); }

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------------ nav row */
/* Bare type, top-centre: Home / Services / Enquire + theme toggle.
   Colour follows the ground it sits on, set per page via a modifier class. */
.nav-row {
  position: relative;
  margin-top: var(--nav-top);
  display: flex;
  align-items: center;
  gap: var(--nav-gap-toggle);
  height: var(--nav-height);
}
.nav-row nav {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
}
.nav-link {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 15.1px;
  color: var(--ink-2);
  transition: color var(--dur-enter) var(--ease);
}
.nav-link:hover { color: var(--ink); }
.nav-link[aria-current="page"] { color: var(--ink); }

/* on a dark surface (services stage) the row reads from the on-surf ramp */
.nav-row--on-surf .nav-link { color: var(--on-surf-2); }
.nav-row--on-surf .nav-link:hover,
.nav-row--on-surf .nav-link[aria-current="page"] { color: var(--on-surf); }

/* theme toggle: solid disc, inverted against its ground */
.theme-toggle {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: -11px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.theme-toggle > span {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--bg);
  transition: background 240ms var(--ease);
}
.theme-toggle:hover > span { background: var(--ink-2); }
.nav-row--on-surf .theme-toggle > span { background: var(--on-surf); color: var(--surf); }
.nav-row--on-surf .theme-toggle:hover > span { background: var(--on-surf-2); }
.theme-toggle svg { overflow: visible; }
.theme-toggle .sun { transform-origin: 12.4px 12.76px; transition: transform 700ms var(--ease); }
.theme-toggle .sun path {
  stroke-dasharray: 1;
  transition: stroke-dashoffset 300ms var(--ease) 380ms;
}
.theme-toggle .moon {
  transform-origin: 12.3px 12.5px;
  stroke-dasharray: 1;
  transition: transform 700ms var(--ease), stroke-dashoffset 300ms var(--ease) 0ms;
}
/* light: sun shown, moon hidden */
.theme-toggle .sun { transform: scale(1); }
.theme-toggle .sun path { stroke-dashoffset: 0; }
.theme-toggle .moon { transform: scale(0); stroke-dashoffset: 1; }
/* dark: moon draws in over the last 300ms (380ms delay), sun un-draws at once */
:root[data-theme="dark"] .theme-toggle .sun { transform: scale(0); }
:root[data-theme="dark"] .theme-toggle .sun path {
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 300ms var(--ease) 0ms;
}
:root[data-theme="dark"] .theme-toggle .moon {
  transform: scale(1);
  stroke-dashoffset: 0;
  transition: transform 700ms var(--ease), stroke-dashoffset 300ms var(--ease) 380ms;
}

/* ------------------------------------------------------------------ logo */
.brand-logo-link {
  position: absolute;
  left: var(--edge-pad-x);
  top: var(--logo-top);
  display: inline-flex;
  color: var(--ink);
}
.brand-logo-link--hero {
  transition: color var(--dur-enter) var(--ease);
  animation: logoRise var(--dur-enter) var(--ease) var(--dur-enter-delay) both;
}
.brand-logo-link--hero:hover { color: var(--accent); }
@keyframes logoRise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ------------------------------------------------------- the bar that stays */
/* Each page's top container is `position: sticky` — declared in that page's own
   stylesheet, which loads after this one and would otherwise win the cascade.
   Sticky rather than fixed on purpose: it stays in the flow, so the bar pins
   itself without leaving a hole every page would have to patch. `nav-stick.js`
   puts `is-scrolled` on <html> once there is actually something underneath it;
   everything below is CSS reacting to that.

   On desktop the wordmark belongs to the top of the page rather than to the
   bar. At its finalized size it would crowd the plate, and there is no strip up
   there to hold it off the text — so it steps aside as soon as the page moves,
   leaving the plate alone. The phone does the opposite: see the phone block. */
@media (min-width: 721px) {
  .brand-logo-link { transition: opacity 260ms var(--ease); }
  .brand-logo-link--hero {
    transition: color var(--dur-enter) var(--ease), opacity 260ms var(--ease);
  }
  .is-scrolled .brand-logo-link {
    opacity: 0;
    pointer-events: none;
    /* the hero wordmark's entrance animation fills forwards, and an animated
       value outranks a declared one — without this it would hold opacity at 1 */
    animation: none;
  }
}

/* The quiet route to the enquiry that sits in the phone bar. Off everywhere by
   default; the phone block is the only thing that turns it on. */
.top-enquire { display: none; }

/* ------------------------------------------------------------------ buttons */
.btn-cta {
  font-size: 18px;
  letter-spacing: 0.02em;
  background-image: linear-gradient(90deg, var(--accent) 0%, var(--accent-hover) 50%, var(--accent) 100%);
  background-size: 200% 100%;
  background-position: 0 0;
  color: var(--accent-ink);
  padding: 18px 33px;
  border-radius: var(--radius);
  transition: background-position 620ms var(--ease);
}
.btn-cta:hover { background-position: 100% 0; color: var(--accent-ink); }

.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  letter-spacing: 0.02em;
  color: var(--accent);
  background: transparent;
  border: 1px solid transparent;
  padding: 17px 30px;
  border-radius: var(--radius);
  transition: border-color 520ms var(--ease), color 520ms var(--ease);
}
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }
.btn-ghost .arrow {
  display: inline-block;
  font-size: 28px;
  line-height: 1;
  transform: translate(0, -2px);
  transition: transform 220ms var(--ease);
}
.btn-ghost:hover .arrow { transform: translate(4px, -2px); }

/* The hero pair, 20% smaller on desktop. Desktop only — the phone block near
   the foot of this file sets its own sizes and is left alone. Type, padding
   and the arrow all come down by the same fifth, so the pair keeps the same
   proportions it had at the larger size. */
@media (min-width: 721px) {
  .btn-cta {
    font-size: 14.4px;
    padding: 14.4px 26.4px;
  }
  .btn-ghost {
    font-size: 14.4px;
    padding: 13.6px 24px;
  }
  .btn-ghost .arrow { font-size: 22.4px; }
}


/* ----------------------------------------------------------- site menu (phone) */
/* Below 720px every page's inline nav row collapses to one button, top-right,
   opening a full-bleed inverted panel (markup per page, wired by menu.js).
   Adapted by hand from a GSAP component: the plus icon spins to a cross, an
   --accent "gate" sweeps in ahead of the --surf surface, the links rise in
   staggered. No library, no hover effects. Motion rides the site's two easing
   curves. Nothing here renders above 720px — the button is display:none and the
   panel is [hidden]. The button and panel sit together in each page's top-bar
   container so their z-index composes against that page's stack. */
.site-menu-toggle {
  display: none;                 /* shown in the 720 block below */
  position: absolute;
  top: 4px;
  right: var(--page-pad-x-fluid);
  z-index: 82;                   /* above the panel, so + -> x reads as one move */
  place-items: center;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  pointer-events: auto;          /* services' nav wrap sets pointer-events:none */
}
.site-menu-toggle__icon {
  width: 20px;
  height: 20px;
  transition: transform 700ms var(--ease-out), color 300ms var(--ease);
}
.site-menu-toggle.is-open .site-menu-toggle__icon {
  transform: rotate(315deg);
  color: var(--on-surf);
}

.site-menu {
  position: fixed;
  inset: 0;
  z-index: 80;
  pointer-events: auto;
}
.site-menu__panel {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.site-menu__gate,
.site-menu__surface {
  position: absolute;
  inset: 0;
  transform: translateX(100%);
  will-change: transform;
}
.site-menu__gate {
  background: var(--accent);
  transition: transform 500ms var(--ease-out);
}
.site-menu__surface {
  background: var(--surf);
  transition: transform 560ms var(--ease-out) 80ms;
}
.site-menu.is-open .site-menu__gate,
.site-menu.is-open .site-menu__surface { transform: translateX(0); }

.site-menu__content {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 40px;
  padding: 96px var(--page-pad-x-fluid) 48px;
}

/* the wordmark, sat where the page's own logo sits — but a plain mark: no link,
   no hover, one fixed --on-surf colour. It borrows the panel's entrance (fade +
   10px rise) so it arrives with the links rather than being pinned there cold. */
.site-menu__brand {
  position: absolute;
  top: 4px;
  /* centred, landing exactly where the bar's wordmark sits behind it — the menu
     button moved to the left gutter, so a left-aligned mark here now collides
     with its own close cross. Centred with the box rather than a transform,
     because the transform is carrying the entrance rise. */
  left: 0;
  right: 0;
  /* same 44px centred box as the top row's wordmark, so the mark lands on the
     line it holds elsewhere rather than hard against the top */
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: var(--tap-min);
  color: var(--on-surf);
  line-height: 0;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
}
.site-menu.is-open .site-menu__brand {
  opacity: 1;
  transform: none;
  transition-delay: 220ms;
}

.site-menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-menu__list li { overflow: hidden; }
.site-menu__link {
  display: flex;
  align-items: center;
  min-height: 60px;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: clamp(30px, 8.5vw, 42px);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--on-surf);
}
.site-menu__link > span {
  display: block;
  transform: translateY(115%) rotate(6deg);
  opacity: 0;
  transition: transform 620ms var(--ease-out), opacity 500ms var(--ease-out);
}
.site-menu.is-open .site-menu__link > span {
  transform: none;
  opacity: 1;
}
.site-menu.is-open li:nth-child(1) .site-menu__link > span { transition-delay: 300ms; }
.site-menu.is-open li:nth-child(2) .site-menu__link > span { transition-delay: 360ms; }
.site-menu.is-open li:nth-child(3) .site-menu__link > span { transition-delay: 420ms; }
.site-menu.is-open li:nth-child(4) .site-menu__link > span { transition-delay: 480ms; }
.site-menu.is-open li:nth-child(5) .site-menu__link > span { transition-delay: 540ms; }

/* the toggle relocated into the panel, labelled so it reads as a theme switch
   and sized up a touch so it holds its own beside the links. In light mode
   --ink and --surf are the same hex, so the disc reads from the on-surface ramp
   instead. Enters last, on the panel's fade + rise. */
.site-menu__theme-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: -8px;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
}
.site-menu.is-open .site-menu__theme-row {
  opacity: 1;
  transform: none;
  transition-delay: 520ms;
}
.site-menu__theme { margin: 0; }
.site-menu__theme > span,
.site-menu__theme:hover > span { background: var(--on-surf); color: var(--surf); }
.site-menu__theme > span { width: 28px; height: 28px; }
.site-menu__theme svg { width: 16px; height: 16px; }
.site-menu__theme-label {
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--on-surf);
}

/* The panel above is written dark-on-dark, which was right while it only ever
   appeared over the dark site. On the light set it lands as a dark slab in a
   light page, so here the light theme takes the page's own grey and the ink
   ramp instead — same panel, same motion, same positions, colour only. Dark is
   untouched: it keeps --surf / --on-surf above. Two selectors because light
   arrives two ways — on :root from theme.js, and pinned on services' own body,
   which stays light however the toggle is pressed. The wordmark and the cross
   are currentColor, so the colour change carries them; there is no second
   asset to swap. */
:root[data-theme="light"] .site-menu__surface,
body[data-theme="light"] .site-menu__surface { background: var(--bg); }

:root[data-theme="light"] .site-menu-toggle.is-open .site-menu-toggle__icon,
body[data-theme="light"] .site-menu-toggle.is-open .site-menu-toggle__icon,
:root[data-theme="light"] .site-menu__brand,
body[data-theme="light"] .site-menu__brand,
:root[data-theme="light"] .site-menu__link,
body[data-theme="light"] .site-menu__link,
:root[data-theme="light"] .site-menu__theme-label,
body[data-theme="light"] .site-menu__theme-label { color: var(--ink); }

/* the disc was lifted onto the on-surface ramp only because --ink and --surf
   are the same hex in light; on a light panel it goes back to the page default */
:root[data-theme="light"] .site-menu__theme > span,
:root[data-theme="light"] .site-menu__theme:hover > span,
body[data-theme="light"] .site-menu__theme > span,
body[data-theme="light"] .site-menu__theme:hover > span {
  background: var(--ink);
  color: var(--bg);
}

/* -------------------------------------------------- nav lamp (desktop nav) */
/* TRIAL — delete this block and the nav-lamp.js tag to revert. A sliding
   indicator behind the desktop nav links, adapted from a React/framer-motion
   component; the geometry below is that component's, read off its Tailwind
   classes. nav-lamp.js only measures and moves the span.

   Desktop only — the phone has the hamburger instead. Every colour reads from
   a token, so dark needs no rules of its own; only the services row does, and
   that override sits at the end of this block.

   The span is injected on every page, so it is inert by default and only
   switched on inside the scoped block below — otherwise it would sit in the
   nav's flex row as a blank item and pad it out. */
.nav-lamp { display: none; }

@media (min-width: 721px) {
  /* The pill is the whole row, not just the links, so the theme toggle rides
     inside it as the last item. Doing it this way rather than moving the
     button into <nav> keeps the markup alone, which is what leaves dark mode
     and the services row untouched.

     The spacing scales with the window rather than stepping at a breakpoint:
     roomy on a full desktop, tight enough at 721 that the pill still clears
     the wordmark, which floats over the centred row. */
  .nav-row {
    height: auto;
    gap: clamp(6px, 0.9vw, 12px);
    padding: 4px;
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    /* the original tints the bar with its own accent at 5% over whatever it
       floats on; our page is opaque, so the same tint is mixed into the page
       colour instead of laid over it as an alpha */
    background: color-mix(in srgb, var(--accent) 3%, var(--bg));
    box-shadow: var(--lift);
  }
  .nav-row nav {
    position: relative;          /* the lamp is measured against this */
    gap: clamp(6px, 0.9vw, 12px);
  }

  /* the toggle, sized up to sit level with the links. No label — the disc
     says it. It keeps its full 44px hit area and gives the difference back as
     margin, so the artwork grows while the target does not shrink. */
  .nav-row .theme-toggle {
    margin: -5px;
    flex: 0 0 auto;
  }
  .nav-row .theme-toggle > span { width: 28px; height: 28px; }
  .nav-row .theme-toggle svg { width: 16px; height: 16px; }

  .nav-row .nav-link {
    position: relative;
    z-index: 1;                  /* the text sits over the lamp */
    padding: 8px clamp(12px, 1.7vw, 22px);
    border-radius: var(--radius);
  }
  .nav-row .nav-link:hover,
  .nav-row .nav-link[aria-current="page"] {
    color: var(--accent);
  }

  /* the lamp itself: the tinted plate behind one link, and the bar above it.
     Width and translateX are set by the script; everything else is here. */
  .nav-row .nav-lamp {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    border-radius: var(--radius);
    /* The tube's light, falling downward. This is a background on the plate
       rather than a shadow on the bar: a shadow's blur is a gaussian whose
       tail reached ~5px above the bar however far it was offset down, while a
       background is clipped by the plate, so nothing can appear above it.
       Both stops are opaque mixes, so there is no alpha here either. */
    background:
      radial-gradient(56px 30px at 50% 0%,
        color-mix(in srgb, var(--accent) 26%, var(--bg)),
        color-mix(in srgb, var(--accent) 7%, var(--bg)) 72%);
    opacity: 0;
    pointer-events: none;
    transition:
      transform 420ms var(--ease-out),
      width 420ms var(--ease-out),
      opacity 200ms var(--ease);
  }
  .nav-row nav.has-lamp .nav-lamp { opacity: 1; }

  /* the tube: a 32x4 bar riding above the plate. The original stacks three
     blurred blobs behind it to build the falloff; a two-layer shadow in the
     same colour does the same job, and a shadow is an alpha this site already
     sanctions. */
  /* Services' row reads from the on-surface ramp, because outside the bar it
     sat bare on a photograph. The bar does not follow it there — it is the
     same bar as every other page. Services pins its body to the light set, so
     that is the one it gets whatever the toggle says. These two put the links
     and the toggle back on the ink ramp, which the on-surf rules further up
     would otherwise override inside the bar. */
  .nav-row--on-surf .nav-link { color: var(--ink-2); }
  .nav-row--on-surf .theme-toggle > span { background: var(--ink); color: var(--bg); }
  .nav-row--on-surf .theme-toggle:hover > span { background: var(--ink-2); }

  .nav-row .nav-lamp::before {
    content: "";
    position: absolute;
    top: -8px;
    left: 50%;
    width: 32px;
    height: 4px;
    margin-left: -16px;
    border-radius: var(--radius-pill) var(--radius-pill) 0 0;
    background: var(--accent);
    /* no glow of its own — the light it casts is the plate's gradient above */
  }
}

/* ------------------------------------------------------------------ footer */
.site-footer {
  position: relative;
  background: var(--bg);
  color: var(--ink);
  padding: 0 var(--page-pad-x);
}
.site-footer__inner {
  position: relative;
  margin: 0 auto;
  border-top: 1px solid var(--hair);
  padding: 64px 0 44px;
}
.site-footer__glow {
  position: absolute;
  top: -1px;
  left: 50%;
  transform: translateX(-50%);
  width: 33%;
  height: 1px;
  background: var(--glow);
  filter: blur(1.5px);
  pointer-events: none;
}
/* Brand hard left, the two link columns hard right, with the slack between
   them rather than spread through the link tracks. The old minmax(0,1fr) /
   minmax(0,2fr) split gave each link column ~369px for ~120px of text, so the
   right-hand block floated mid-page and stopped short of the right edge. */
.site-footer__grid {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: space-between;
  gap: 72px;
  align-items: start;
}
.site-footer__brand { display: flex; flex-direction: column; gap: 20px; }
.site-footer__brand p {
  margin: 0;
  max-width: 300px;
  font-weight: 450;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-2);
  text-wrap: pretty;
}
.site-footer__cols {
  display: grid;
  grid-template-columns: auto auto;
  gap: 88px;
}
.site-footer h3 {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.site-footer ul {
  list-style: none;
  margin: 18px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
.site-footer ul a {
  font-size: 14px;
  white-space: nowrap;
  color: var(--ink-2);
}
.site-footer ul a:hover { color: var(--ink); }
.site-footer__phone { font-weight: 450; }
.site-footer__more {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  font-family: var(--font-heading);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--accent) !important;
}
.site-footer__more:hover { color: var(--accent-hover) !important; }
.site-footer__more span { font-size: 11px; letter-spacing: 0; }
.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: 56px;
  padding-top: 22px;
  border-top: 1px solid var(--hair);
}
.site-footer__legal span:first-child { font-weight: 450; font-size: 13px; color: var(--ink-2); }
.site-footer__legal span:last-child {
  font-weight: 450;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* footer stagger reveal — per-element delay set inline via --d */
.foot-reveal {
  filter: blur(4px);
  transform: translateY(-8px);
  opacity: 0;
  transition:
    filter 0.8s ease var(--d, 0s),
    transform 0.8s ease var(--d, 0s),
    opacity 0.8s ease var(--d, 0s);
}
.foot-reveal.is-in { filter: blur(0); transform: none; opacity: 1; }

/* ------------------------------------------------------------- band field */
/* A row of columns rising out of the bottom edge of the window,
   each fading to nothing well before it reaches the copy. Twelve background
   layers on one empty element — no markup per column, nothing animating.
   Shortest in the middle, where the column of text sits, tallest out at the
   margins where there is nothing to read over. Each hairline takes the height
   of the taller column it touches, so the rules trace the stepped top edge
   rather than running the full height as a grid.

   A page opts in by dropping in <div class="page-bands" aria-hidden="true">
   and giving its own main a position and a z-index above it. The count is
   baked into the layer list and CSS cannot loop, so the phone version further
   down is a second list rather than the same one scaled.

   One low-opacity wash, which is a third use of alpha beyond the two CLAUDE.md
   sanctions. */
.page-bands {
  display: block;
  position: fixed;
  inset: auto 0 0 0;
  height: 40vh;
  z-index: 0;
  pointer-events: none;
  /* Light and dark need their own strength. --accent is a deep navy: over the
     light grey page it reads as a darkening rather than a colour until it is
     carried further, while on the near-black page the same value glares. Same
     colour token either way — only how much of it. */
  opacity: 0.38;

  --band-ink: var(--ink-2);
  --bar: linear-gradient(to top, var(--band-ink), transparent);
  --rule: linear-gradient(to top,
    var(--band-ink) 0%, var(--band-ink) 55%, transparent 100%);

  background-repeat: no-repeat;
  background-image:
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--rule),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar),
    var(--bar);
  background-size:
    1px 100%,
    1px 100%,
    1px 80%,
    1px 60%,
    1px 44%,
    1px 32%,
    1px 24%,
    1px 32%,
    1px 44%,
    1px 60%,
    1px 80%,
    1px 100%,
    1px 100%,
    8.3333% 100%,
    8.3333% 80%,
    8.3333% 60%,
    8.3333% 44%,
    8.3333% 32%,
    8.3333% 24%,
    8.3333% 24%,
    8.3333% 32%,
    8.3333% 44%,
    8.3333% 60%,
    8.3333% 80%,
    8.3333% 100%;
  background-position:
    0.0000% 100%,
    8.3333% 100%,
    16.6667% 100%,
    25.0000% 100%,
    33.3333% 100%,
    41.6667% 100%,
    50.0000% 100%,
    58.3333% 100%,
    66.6667% 100%,
    75.0000% 100%,
    83.3333% 100%,
    91.6667% 100%,
    100.0000% 100%,
    0.0000% 100%,
    9.0909% 100%,
    18.1818% 100%,
    27.2727% 100%,
    36.3636% 100%,
    45.4545% 100%,
    54.5455% 100%,
    63.6364% 100%,
    72.7273% 100%,
    81.8182% 100%,
    90.9091% 100%,
    100.0000% 100%;
}
/* Dark takes the accent, where the blue is the point, and less of it: the page
   around it is already dark, so the same strength glares. On the light grey
   ground a navy wash reads as a tint of the paper rather than as texture, so
   light takes the neutral above. */
:root[data-theme="dark"] .page-bands {
  --band-ink: var(--accent);
  opacity: 0.2;
}

/* the copy and the top bar sit over it */

/* ------------------------------------------------------------- phone layout */
/* 720px is the site's one phone breakpoint. The finalized design is desktop
   first, so everything below re-composes for a tall, narrow screen rather than
   scaling the desktop layout down: the centred nav becomes logo-left and
   links-right, fixed 56px gutters become the fluid token, and every control
   grows to the 44px tap minimum without moving visually. */
@media (max-width: 720px) {
  /* --- top row: wordmark centred, menu button left, Enquire right --- */
  /* The menu button and the Enquire link are both absolutely positioned, so the
     wordmark is the row's only flow child — centring it is the whole job. Each
     page repeats the justify-content in its own stylesheet, which loads last. */
  .hero-top,
  .enq-top {
    justify-content: center;
    align-items: center;
    padding: 4px var(--page-pad-x-fluid) 0;
    gap: 12px;
  }
  .brand-logo-link--hero { padding-right: 0; }

  /* The icon's centre sits on the gutter line rather than its box edge: half of
     the 44px tap box is pulled back past the gutter, so the plus reads as
     sitting at the margin instead of indented by its own hit area. */
  .site-menu-toggle {
    right: auto;
    left: calc(var(--page-pad-x-fluid) - var(--tap-min) / 2);
  }

  /* A fifth smaller than the phone CTA pair and carrying no accent, border or
     fill — the hero's "Ring us" is still the loud one and this only has to be
     findable. The offset names its CENTRE, not an edge, so the label can change
     without moving the anchor. It hangs off the menu button's centre line so
     the two corners read as one row. */
  .top-enquire {
    display: inline-flex;
    align-items: center;
    position: absolute;
    z-index: 5;
    top: 26px;                          /* the menu button's centre: 4px + 44/2 */
    right: 50px;
    transform: translate(50%, -50%);
    font-size: 14.28px;
    letter-spacing: 0.02em;
    color: var(--ink-2);
    transition: color 520ms var(--ease);
  }
  .top-enquire:hover { color: var(--ink); }
  /* The type is well under the 44px minimum, so the hit area is grown behind
     the label rather than by inflating the label. */
  .top-enquire::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: var(--tap-min);
    transform: translateY(-50%);
  }

  /* --- the strip --- */
  /* Solid, never see-through: text has to disappear cleanly under the bar. It
     is absent at the very top of a page, where nothing is underneath it yet, so
     each page opens as clean as it ever was. The hairline stops the strip
     reading as floating once copy is sliding beneath it. Services paints its
     own, on the dark surface — see services.css. */
  /* The hairline is a shadow rather than a border on purpose: a border would
     add its pixel to the bar's height and push every page's content down by
     one, whereas a shadow costs no layout at all. */
  .hero-top,
  .enq-top,
  .abt-top,
  .pp-top {
    transition: background-color 220ms var(--ease), box-shadow 220ms var(--ease);
  }
  .is-scrolled .hero-top,
  .is-scrolled .enq-top,
  .is-scrolled .abt-top,
  .is-scrolled .pp-top {
    background: var(--bg);
    box-shadow: 0 1px 0 var(--hair);
  }

  /* Six columns, not twelve. At phone width a twelfth of the screen is about
     32px, and that many bands read as a fine texture rather than as blocks.
     The count is baked into the layer list and CSS cannot loop, so this is a
     second list — same valley, half the columns. */
  .page-bands {
    background-image:
      var(--rule),
      var(--rule),
      var(--rule),
      var(--rule),
      var(--rule),
      var(--rule),
      var(--rule),
      var(--bar),
      var(--bar),
      var(--bar),
      var(--bar),
      var(--bar),
      var(--bar);
    background-size:
      1px 100%,
      1px 100%,
      1px 60%,
      1px 26%,
      1px 60%,
      1px 100%,
      1px 100%,
      16.6667% 100%,
      16.6667% 60%,
      16.6667% 26%,
      16.6667% 26%,
      16.6667% 60%,
      16.6667% 100%;
    background-position:
      0.0000% 100%,
      16.6667% 100%,
      33.3333% 100%,
      50.0000% 100%,
      66.6667% 100%,
      83.3333% 100%,
      100.0000% 100%,
      0.0000% 100%,
      20.0000% 100%,
      40.0000% 100%,
      60.0000% 100%,
      80.0000% 100%,
      100.0000% 100%;
  }

  /* every page's inline nav row (links + its toggle) collapses into the
     full-screen menu; the button takes its place, top-right */
  .hero-top .nav-row,
  .enq-top .nav-row,
  .abt-top .nav-row,
  .pp-top .nav-row,
  .svc-nav-wrap .nav-row { display: none; }
  .site-menu-toggle { display: grid; }

  .brand-logo-link,
  .brand-logo-link--hero {
    position: static;
    left: auto;
    top: auto;
    flex: 0 0 auto;
  }
  .brand-logo-link svg { height: 26px !important; }
  /* the wordmark is the bar's only flow child at this width, so it is what
     gives the bar its height — the same 44px centred box on every page, which
     is also the tap minimum and the line the menu button and Enquire link are
     centred against. Without it the wordmark sits hard against the top and the
     strip ends above them. */
  .brand-logo-link--hero,
  .enq-top .brand-logo-link,
  .abt-top .brand-logo-link,
  .pp-top .brand-logo-link,
  .svc-nav-wrap .brand-logo-link {
    min-height: 44px;
    align-items: center;
  }
  .brand-logo-link--hero { padding-right: 4px; }

  /* 44px tap height, absorbed by the negative margin so the row keeps its
     visual position and the links sit where they did. */
  .nav-row {
    gap: 10px;
    margin-top: 8px;
    height: 44px;
  }
  .nav-row nav { gap: 2px; }
  .nav-link {
    font-size: 14px;
    padding: 14px 7px;
    margin: -14px 0;
  }

  /* The hero CTAs were enlarged ~27% for desktop only. These two classes are
     used nowhere but the hero and had no phone rule of their own, so the
     desktop bump would have followed them down here — pinned back to the
     sizes the phone layout was built and verified against. */
  .btn-cta { font-size: 14px; padding: 14px 26px; }
  .btn-ghost { font-size: 14px; padding: 13px 24px; }
  .btn-ghost .arrow { font-size: 22px; }

  /* --- footer: one column, and let the long email wrap rather than spill --- */
  .site-footer { padding: 0 var(--page-pad-x-fluid); }
  .site-footer__inner { padding: 48px 0 36px; }
  .site-footer__grid { grid-template-columns: 1fr; gap: 40px; }
  /* the desktop footer now sizes these tracks to their content and pins them
     right; the phone keeps the even 1fr split it was built against */
  .site-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; }
  .site-footer__brand p { max-width: none; }
  .site-footer ul { margin-top: 14px; gap: 2px; }
  .site-footer ul a {
    white-space: normal;
    display: inline-block;
    padding: 13px 0;
    overflow-wrap: anywhere;
  }
  .site-footer__more { min-height: 44px; align-items: center; }
}

/* The footer's 1fr / 2fr split squeezes the two link columns to ~220px well
   before the phone breakpoint, and the email address is wider than that. */
@media (max-width: 900px) {
  .site-footer__grid { grid-template-columns: 1fr; gap: 40px; }
  .site-footer ul a { white-space: normal; overflow-wrap: anywhere; }
}

/* Touch tablets sit above the phone breakpoint but still need real tap targets.
   Scoped to coarse pointers under 1024px so a touch-screen laptop keeps the
   desktop nav rhythm exactly as designed. Every rule pairs its padding with a
   matching negative margin, so nothing moves — only the hit area grows. */
@media (pointer: coarse) and (min-width: 721px) and (max-width: 1024px) {
  .nav-link { padding: 14px 0; margin: -14px 0; }
  .site-footer ul a,
  .site-footer__more { display: inline-block; padding: 13px 0; margin: -13px 0; }
  .enq-talk__inner a { display: inline-flex; align-items: center; min-height: 44px; }
  .brand-logo-link--hero { padding: 9px 4px; margin: -9px -4px; }
}

/* Very small phones (320px): the top row runs out of room, so the wordmark and
   the links both step down one notch rather than colliding. */
@media (max-width: 380px) {
  .site-footer__cols { grid-template-columns: 1fr; gap: 28px; }

  .hero-top,
  .enq-top { padding-left: 16px; padding-right: 16px; gap: 8px; }
  .brand-logo-link svg { height: 22px !important; }
  .nav-row { gap: 8px; }
  .nav-row nav { gap: 0; }
  .nav-link { font-size: 13px; padding: 15px 5px; margin: -15px 0; }

  /* the menu button, its panel wordmark and the link column follow the top
     row in to the 16px gutter */
  .site-menu-toggle { right: 16px; }
  .site-menu__brand svg { height: 22px !important; }
  .site-menu__content { padding-left: 16px; padding-right: 16px; }
}
