/**
 * Header and footer.
 *
 * Ported from components/nav/Nav.module.css. The header is the one place on this site
 * where the 360px viewport was actually broken once — the action cluster overran the
 * viewport and scrolled the page sideways. The rules that stop that recurring are
 * marked below.
 */

/* ── Header ───────────────────────────────────────────────────────────────── */

/**
 * The bar's height and the mark's width, as animatable properties.
 *
 * Registered rather than plain custom properties for one reason: an unregistered custom
 * property has no type, so it can only ever flip between two values at the midpoint of
 * an animation. `syntax: '<length>'` makes them interpolate, which is what lets the bar
 * CONDENSE with the scroll rather than snap at a threshold — no scroll listener, no
 * class toggle, no JavaScript at all (see .ss-header--sticky, below).
 *
 * Everything the bar's height depends on is expressed in terms of these two: the bar
 * itself, the mark, and the padding that makes a navigation link fill the bar. Before
 * this each carried its own number in three places and two media queries, and a header
 * that shrinks would have needed all of them kept in step by hand.
 */
@property --ss-header-height {
  syntax: '<length>';
  inherits: true;
  initial-value: 80px;
}

@property --ss-header-logo {
  syntax: '<length>';
  inherits: true;
  initial-value: 160px;
}

.ss-header {
  --ss-header-height: 80px;
  --ss-header-logo: 160px;

  border-bottom: var(--hairline);
  background: var(--surface-base);
}

.ss-header__inner {
  max-width: var(--layout-max);
  margin-inline: auto;
  padding-inline: var(--layout-gutter);
  gap: var(--space-6);

  /**
   * 80px, and no vertical padding.
   *
   * SiteHeader.jsx sets `height: 80px` and lets the items centre in it. This had
   * `min-height: 72px` — the reference's number — plus `--space-4` top and bottom, and
   * the logo is 57px tall, so the bar measured 89px on staging: taller than either the
   * design or the reference, by a rule that was meant to be the reference's.
   *
   * The padding has to go rather than shrink, because the navigation links fill the bar
   * (see .ss-header__nav a) so that the active section's 2px rule sits on the header's
   * bottom edge. Vertical padding here would hold them off it.
   *
   * The 80px is --ss-header-height's initial value now, because the bar condenses on
   * scroll and three other rules have to follow it down.
   */
  min-height: var(--ss-header-height);

  /**
   * The skip link is the header's first child, so core's flow layout treats the bar as
   * the SECOND and gives it `margin-block-start: var(--wp--style--block-gap)`. The
   * header has no padding or border of its own, so that 32px collapsed out through
   * .wp-site-blocks and <body> and pushed the entire document down 32px — a strip of
   * blank page above the header on every route, at every width.
   */
  margin-block-start: 0;
}

.ss-header__brand a,
.ss-header__brand {
  font-family: var(--font-display);
  font-size: var(--type-h4-size);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-display);
  color: var(--text-heading);
  text-decoration: none;
  white-space: nowrap;
}

.ss-header__actions {
  gap: var(--space-4);
  flex-shrink: 0;

  /**
   * The 360px fix. The action cluster — brand, navigation toggle, language switcher,
   * CTA — overran the viewport and scrolled the whole page sideways. See docs/STATUS.md.
   *
   * min-width: 0 lets the flex child shrink below its content width instead of forcing
   * the track open; the header's own overflow is then the toggle's problem, not the
   * document's.
   */
  min-width: 0;
}

/**
 * The design pushes the telephone, the language switch and the CTA to the far edge and
 * leaves the sections beside the logo. Before this the navigation was inside this
 * cluster and the whole lot sat together in the middle.
 *
 * Doubled through .ss-header because WordPress emits `body .is-layout-flex > * { margin:
 * 0 }` for every flex container — an element plus a class, which out-specifies a single
 * class. Written singly the auto margin computed to 0, and the cluster only LOOKED right
 * at 1440, where the content happens to fill the row to within four pixels.
 */
.ss-header .ss-header__actions {
  margin-inline-start: auto;
}

.ss-header__nav {
  font-family: var(--font-body);
  font-size: var(--type-body-sm-size);
  min-width: 0;
}

/**
 * PERFORMANCE has no page of its own, so its title is not a link.
 *
 * `wp ss pages` writes that section's submenu with no URL, and core — with
 * `openSubmenusOnClick` off, as the design's hover panels need — renders the item's
 * content as an anchor with no `href`. So every rule here still reaches it: the bar
 * spacing, the hover, the panel, and the mark on the section you are reading. Nothing
 * extra is needed, which is why nothing extra is written.
 */
.ss-header__nav a {
  color: var(--text-body);
  text-decoration: none;
  font-size: 15px;
  line-height: 22px;
  white-space: nowrap;
}

/* One row. Wrapping the primary navigation is never the right answer here — the
   overlay menu takes over below 1024px, which is what the responsive design does. */
.ss-header__nav .wp-block-navigation__container {
  gap: var(--space-5);
  flex-wrap: nowrap;
}

/**
 * A top-level section fills the bar.
 *
 * SiteHeader.jsx gives each one `padding: 29px 0` inside an 80px header so that the
 * active section's 2px rule lands on the header's bottom edge rather than hugging the
 * word. 22px of line, 28px above and below, 2px of border: 80.
 *
 * Which is why the padding is calculated rather than written: 24 is the line and the
 * border, and the rest is split above and below whatever height the bar currently has.
 * A hard 28px would have left the active section's rule floating 20px below a condensed
 * bar — the one mark on the header that has to touch its bottom edge.
 *
 * The border is declared transparent on every item, not only on the active one, so that
 * marking a section does not move the row by two pixels.
 *
 * Scoped by `__container >` rather than by depth, because the sub-menu's list carries
 * `__submenu-container` — a different class — so this cannot reach inside a panel.
 */
.ss-header__nav .wp-block-navigation__container > .wp-block-navigation-item > a {
  display: block;
  padding-block: calc((var(--ss-header-height) - 24px) / 2);
  border-bottom: 2px solid transparent;
  transition: color var(--motion-hover) var(--ease-standard),
    border-color var(--motion-hover) var(--ease-standard);
}

/**
 * The section you are reading.
 *
 * `current-menu-item` and `current_page_item` were styled here from the start and never
 * matched anything: core marks a navigation item current by comparing the queried post
 * ID against the item's `id` attribute, and these items are custom links with no id. So
 * no item was ever marked, on any page. SS\Routes::mark_current_nav_item() adds
 * `current-menu-ancestor` by path — which is also what makes a section stay marked while
 * you read one of the pages inside it.
 *
 * Doubled through .ss-header for a second reason. Core doubles a class ON PURPOSE —
 * `.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content
 * { color: inherit }` — which is three classes, so `.ss-header__nav .current-menu-item > a`
 * lost the colour even where the class did appear. The hover state was losing it too, so
 * the primary navigation had no hover feedback either.
 */
.ss-header .ss-header__nav a:hover,
.ss-header .ss-header__nav a[aria-current="page"],
.ss-header .ss-header__nav .current-menu-item > a,
.ss-header .ss-header__nav .current-menu-ancestor > a,
.ss-header .ss-header__nav .current_page_item > a {
  color: var(--ss-blue);
  border-bottom-color: var(--ss-blue);
}

/* ── The sub-menu panel ───────────────────────────────────────────────────────
   SiteHeader.jsx: 280px minimum, flush under the bar and 16px to the left of the
   section it belongs to, a hairline all round and a 2px --ss-ink rule along the top.

   The block library's stylesheet loads AFTER the theme's, so a tie on specificity goes
   to core. Its panel rules run to three and four classes
   (`.wp-block-navigation .has-child .wp-block-navigation__submenu-container`, and the
   same again for `__item__content`), so each selector below is written one class longer
   than the rule it has to beat — otherwise the panel keeps core's 200px minimum, its
   grey 15%-alpha border and its 0.5em/1em padding. */
.ss-header .ss-header__nav.wp-block-navigation .wp-block-navigation__submenu-container {
  inset-inline-start: calc(var(--space-4) * -1);
  background: var(--surface-base);
  border: var(--hairline);
  border-top: 2px solid var(--ss-ink);
  border-radius: 0;
  padding-block: var(--space-3);
}

.ss-header .ss-header__nav.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
  padding: 10px var(--space-4);
  border-bottom: 0;
}

/**
 * 280px minimum, which is the design's figure.
 *
 * Core carries its own 200px, and not on the panel — on the two rules that OPEN the
 * panel, `.has-child:not(.open-on-click):hover > …` and the `:focus-within` twin, which
 * are five classes each. So this has to name the state as well to reach six, and there
 * is no shorter honest way to out-specify a rule that specific.
 *
 * Above 1024 only. Below it the panel is not a panel: core lays the sub-menu out static
 * and indented inside the overlay, where 280px plus that indent plus a 24px gutter is
 * the whole of a 360px viewport.
 */
@media (width >= 1025px) {
  .ss-header .ss-header__nav.wp-block-navigation .has-child:hover > .wp-block-navigation__submenu-container,
  .ss-header .ss-header__nav.wp-block-navigation .has-child:focus-within > .wp-block-navigation__submenu-container {
    min-width: 280px;
  }
}

/* The detail number, in mono, ahead of the name — "Sub-menu items carry detail numbers
   in mono" (SiteHeader.prompt.md). Written into the label by `wp ss pages`, which is
   why it survives: core runs a navigation label through wp_kses_post(). */
.ss-nav__ref {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: var(--tracking-mono);
  color: var(--ss-blue);
  margin-inline-end: 10px;
}

/**
 * ── THE HEADER STAYS ────────────────────────────────────────────────────────
 *
 * "The header is the only fixed element on desktop" — DESIGN-SYSTEM.md §Layout, and
 * SiteHeader.jsx defaults `sticky` to true. The reference does it in one line
 * (Nav.module.css `.header { position: sticky }`).
 *
 * This build had that line too, on .ss-header--sticky, and the class was on the element,
 * and the header still scrolled away on every route at every width. Measured on staging:
 * `position: sticky` computed, and the bar's top going to −1500 at scrollY 1500.
 *
 * The reason is one element up. `wp:template-part` wraps the part in a <header
 * class="wp-block-template-part">, so the styled header is not a child of .wp-site-blocks
 * — it is a child of a box that is EXACTLY AS TALL AS ITSELF. A sticky element travels
 * within its containing block and no further, so with zero travel available it does
 * nothing at all, silently and with the property computing exactly as authored. Which is
 * why reading the CSS could never find this and measuring it found it immediately.
 *
 * So the wrapper is what sticks. Selected by `:has()` rather than by adding a class to
 * the seven templates that include the part: the marker belongs on the header, and seven
 * copies of it is seven chances for one to be missed.
 */
header.wp-block-template-part:has(> .ss-header--sticky) {
  position: sticky;
  top: 0;
  z-index: 50;
}

/**
 * ── AND CONDENSES ───────────────────────────────────────────────────────────
 *
 * At the client's request, and a deliberate divergence: the design system draws one
 * header height and says "nothing else floats, sticks or parallaxes". An 80px bar pinned
 * to the top of a 900px viewport is 9% of the screen held permanently, and these are
 * long pages — a detail page runs to about 4,000px.
 *
 * So the bar is the design's 80px where the design is looked at, and 60px once the
 * reader has gone 160px down. The page a specifier lands on is the approved one; the bar
 * they read past is the small one.
 *
 * No JavaScript, no scroll listener, no class toggled at a threshold — a scroll-driven
 * animation over the two registered properties above, so the browser interpolates it off
 * the main thread and the bar tracks the scroll rather than snapping. `@supports` is the
 * whole fallback: a browser without scroll-driven animations keeps an 80px sticky
 * header, which is the design, not a broken version of it.
 *
 * The range is absolute (0–160px) rather than a percentage on purpose. A shrinking
 * in-flow header makes the document shorter, and a percentage range would recompute
 * against that new length every frame — the header would chase its own progress.
 *
 * Below 769 nothing happens: the bar is already 60px there, and the phone's fixed
 * element is the contact bar at the foot rather than this.
 */
@keyframes ss-header-condense {
  to {
    --ss-header-height: 60px;
    --ss-header-logo: 132px;
  }
}

@supports (animation-timeline: scroll()) {
  @media (width >= 769px) {
    .ss-header--sticky {
      animation-name: ss-header-condense;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: scroll(root block);
      animation-range: 0 160px;
    }
  }
}

@media (width <= 768px) {
  .ss-header__inner {
    padding-inline: var(--layout-gutter-mobile);
  }

  /* The CTA duplicates the sticky mobile contact bar at the foot of the screen.
     Two of the same action on a 360px viewport is one too many. */
  .ss-header__cta {
    display: none;
  }
}

/* ── Footer ───────────────────────────────────────────────────────────────── */

.ss-footer {
  border-top: var(--hairline);
  background: var(--surface-section);
  padding: var(--space-10) var(--layout-gutter) var(--space-8);
}

.ss-footer__columns {
  gap: var(--space-8);
}

.ss-footer__brand {
  font-family: var(--font-display);
  font-size: var(--type-h4-size);
  font-weight: var(--weight-bold);
  color: var(--text-heading);
}

.ss-footer__note,
.ss-footer__colophon {
  font-size: var(--type-body-sm-size);
  line-height: var(--type-body-sm-line);
  color: var(--text-secondary);
  margin-top: var(--space-3);
}

.ss-footer__nav {
  display: block;
  margin-top: var(--space-4);
  font-size: var(--type-body-sm-size);
}

.ss-footer__nav a {
  font-size: 15px;
  line-height: 22px;
  color: var(--ss-ink);
  text-decoration: none;
}

.ss-footer__nav a:hover {
  color: var(--ss-blue);
}

/**
 * ── THE STANDING GATE STATEMENT ───────────────────────────────────────────
 *
 * §0.2 requires that the absence of the held figures be STATED rather than left to be
 * noticed. This is the one element present on every page, so it is where the statement
 * belongs — a specifier landing deep in the site from a search result reads it before
 * concluding a figure is missing by oversight.
 *
 * Set in --text-pending, matching every other held-figure treatment on the site, so the
 * visual language is consistent: grey means "we have not published this", everywhere.
 */
.ss-footer__gate-note {
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: var(--hairline);
  font-family: var(--font-mono);
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-line);
  letter-spacing: var(--tracking-mono);
  color: var(--text-pending);
  max-width: 88ch;
}

.ss-footer__colophon {
  margin-top: var(--space-6);
}

@media (width <= 768px) {
  .ss-footer {
    padding-inline: var(--layout-gutter-mobile);
  }
}

@media print {
  .ss-header,
  .ss-footer__columns {
    display: none;
  }

  /* The gate statement survives onto paper. A printed detail sheet that shows no fire
     rating and does not say why is the exact ambiguity §0.2 exists to remove. */
  .ss-footer {
    border-top: 0.5pt solid #999; /* stylelint-disable-line color-no-hex -- paper has no theme */
    padding: 0;
  }
}

/* ── Header: logo, phone, CTA ─────────────────────────────────────────────────
   Ported from components/nav/Nav.module.css. The header used to be a text site
   title and an outlined "Contact" button; the design has a logo mark, the
   technical department's number in mono, EN/ES, and a blue "Request a quote". */

/* On the wrapper, not the <img>.
   WordPress's own `.wp-block-site-logo img { width: 100% }` ties on specificity and
   wins on source order, so sizing the image directly does nothing. And without
   flex-shrink:0 the header's flex row squeezed the mark to 147px — the reference sets
   the same property on .logo for the same reason. */
.ss-header__logo {
  flex-shrink: 0;
  width: var(--ss-header-logo);
}

.ss-header__logo img,
.ss-header__logo .custom-logo {
  width: 100%;
  height: auto;
  display: block;
}

/* The number itself comes from patterns/contact-phone.php, which reads SSCompany —
   the header and footer are static template parts and cannot. It emits one class,
   `ss-phone`, and each context sizes it. */
.ss-header__actions .ss-phone {
  font-family: var(--font-mono);

  /* 13px, which is what SiteHeader.jsx sets. The footer's copy of the number is the
     larger one; in the header it sits beside 15px navigation and reads as a label. */
  font-size: 13px;
  letter-spacing: var(--tracking-mono);
  color: var(--ss-ink);
  white-space: nowrap;
  margin: 0;
}

.ss-header__actions .ss-phone a {
  color: inherit;
  text-decoration: none;
}

/**
 * The header CTA is the design's SMALL button.
 *
 * design/components/core/Button.jsx: `sm` is 14px on 24px of line with 8px/14px of
 * padding; this had it at 15px with the site-wide button padding, which made it 162px
 * wide. At 1440 the header's content then measured 1,297px against 1,280px of content
 * width, so the cluster finished 63px from the edge instead of 80 — the header alone
 * breaking the gutter every other element on the page keeps. The design's own size fits.
 */
.ss-header__cta .wp-block-button__link {
  background: var(--ss-blue);
  color: var(--text-inverse);
  border: 1px solid var(--ss-blue);
  font-size: 14px;
  line-height: 24px;
  padding: 8px 14px;
  white-space: nowrap;
}

/**
 * …and its hover state, which the rule above was swallowing.
 *
 * theme.json gives every button `:hover { background: --action-primary-bg-hover }`, and
 * WordPress emits element styles inside `:where()` — one class. `background` set here is
 * two, so it won in both states and the header's CTA was the one button on the site that
 * did not respond to the pointer at all. "A pressed control changes colour, not
 * geometry" (patterns.css) — it has to change something.
 */
.ss-header__cta .wp-block-button__link:hover {
  background: var(--action-primary-bg-hover);
  border-color: var(--action-primary-bg-hover);
}

.ss-header__cta .wp-block-button__link:active {
  background: var(--action-primary-bg-press);
  border-color: var(--action-primary-bg-press);
}

/* ── Footer ──────────────────────────────────────────────────────────────────
   Five columns, a gate statement, and a bottom row carrying the legal links. */

.ss-footer__logo img,
.ss-footer__logo .custom-logo {
  width: 150px;
  height: auto;
  display: block;
}

.ss-footer__brand-column .ss-phone {
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: var(--tracking-mono);
  color: var(--ss-ink);
  margin: 0;
}

.ss-footer__brand-column .ss-phone a {
  color: inherit;
  text-decoration: none;
}

.ss-footer__column-title {
  margin-bottom: var(--space-4);
}

.ss-footer__column-title + .wp-block-navigation {
  margin-bottom: var(--space-7);
}

.ss-footer__nav .wp-block-navigation__container {
  gap: var(--space-3);
}

.ss-footer__bottom {
  border-top: var(--hairline);
  margin-top: var(--space-9);
  padding-top: var(--space-5);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--tracking-mono);
  color: var(--text-secondary);
}

.ss-footer__bottom .ss-footer__colophon {
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  color: inherit;
}

.ss-footer__bottom .ss-footer__nav a {
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  color: inherit;
}

/* The reference collapses the header at 1024px: nav and phone out, toggle in, logo
   down to 132px (components/nav/Nav.module.css). WordPress's navigation block has its
   own breakpoint at 600px and no setting for it, so between 600 and 1024 it kept
   showing six inline links beside the phone, the switcher and the CTA — which
   overflowed 1024 and 768 and scrolled the whole page sideways. Caught by
   tests/e2e/navigation.spec.ts, which is exactly the regression that test is for. */
@media (width <= 1024px) {
  .ss-header__actions .ss-phone {
    display: none;
  }

  .ss-header {
    --ss-header-logo: 132px;
  }

  /* Collapsed, the navigation is one button, and the reference pushes THAT to the right
     with the language switch and the CTA after it (Nav.module.css .menuToggle). Moving
     the auto margin from the cluster to the navigation keeps that order without
     reordering the DOM — two auto margins would split the free space between them and
     strand the button in the middle. */
  .ss-header .ss-header__nav {
    margin-inline-start: auto;
  }

  .ss-header .ss-header__actions {
    margin-inline-start: 0;
  }

  /**
   * The overlay is the site map, so it has to read as a list.
   *
   * Core lays the sub-menus out static and indented once the overlay opens, which is the
   * right treatment — but it spaces every item by the block gap and puts another one
   * above each sub-list. That suits six items. With twenty-seven it is most of a
   * thousand pixels of scrolling, and the indent stops doing the work of grouping
   * because the vertical space between siblings is larger than the step in.
   *
   * Five classes, because core's own overlay rules are five.
   */
  .ss-header .ss-header__nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container,
  .ss-header .ss-header__nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__submenu-container {
    gap: var(--space-2);
    padding-top: 0;
  }

  .ss-header .ss-header__nav .wp-block-navigation__responsive-container.is-menu-open .has-child {
    padding-block-end: var(--space-5);
  }

  /* On the bar a section is told apart from its panel by being in the bar. In the
     overlay there is no panel, so it is told apart by the design's own top-level size:
     16px against the 15px of a sub-item. */
  .ss-header .ss-header__nav .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container > .wp-block-navigation-item > a {
    font-size: 16px;
  }
}

/* The header CTA comes out on phones.
   Below 768px the sticky bottom bar carries Call, Email and Request a quote, so keeping
   the button here duplicates an action and — with the logo, the menu toggle and the
   language switcher — overruns a 360px viewport, which pushes the whole page into
   horizontal scroll. That is the reference's own note, and the WordPress theme had the
   button without the bar, so it did both halves wrong at once. */
@media (width <= 768px) {
  /* Scoped to .ss-header to out-specify WordPress's own
     `.wp-block-buttons-is-layout-flex { display: flex }`, which is a single class like
     .ss-header__cta and wins the tie on source order — so the button stayed visible and
     the page still scrolled sideways at 360px after being told to hide. */
  .ss-header .ss-header__cta {
    display: none;
  }

  /* Already the condensed height, which is why the scroll animation stops at 769: there
     is nothing left for it to take, and the fixed element on a phone is the contact bar
     at the foot rather than this. */
  .ss-header {
    --ss-header-height: 60px;
    --ss-header-logo: 118px;
  }

  .ss-header__inner {
    gap: var(--space-4);
  }

  .ss-header__actions {
    gap: var(--space-3);
  }

  /* Clear of the fixed bar, or the last thing on every page sits under it. */
  body {
    padding-bottom: 56px;
  }
}

@media (width >= 600px) and (width <= 1024px) {
  .ss-header__nav .wp-block-navigation__responsive-container:not(.hidden-by-default, .is-menu-open) {
    display: none;
  }

  .ss-header__nav .wp-block-navigation__responsive-container-open:not(.always-shown) {
    display: flex;
  }
}

/* Below 1200px the six nav items, the phone, the switcher and the CTA no longer fit
   1440px of header, and the nav wrapped onto a second line under the logo — which is
   how it shipped, and it reads as a mistake before a reader has read anything. */
@media (width <= 1200px) {
  .ss-header__nav a {
    font-size: 14px;
  }

  .ss-header__inner,
  .ss-header__actions {
    gap: var(--space-4);
  }
}
