/* =============================================================
   Layout

   The page frame: header and nav (including the mobile panel),
   the main content column, generic section spacing, and the footer.
   Everything that positions a page rather than styles a thing in it.

   Structure only. Every value is a token from tokens.css — no
   hard-coded hex, spacing or type values in here (see CLAUDE.md).

   Load order: 3rd, after base.css. See the stylesheet block in
   _includes/layouts/base.njk for the full order and why it matters.
   ============================================================= */

/* --- Header / nav ------------------------------------------- */

.site-header {
  /* A query container, for the name switch further down. */
  container: site-header / inline-size;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding: var(--space-s) var(--container-gutter);
  max-width: var(--container-max);
  margin-inline: auto;
}

.site-header__logo {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  text-decoration: none;
}

/* The mark's display size lives here, not in its width/height attributes.
   Those carry the file's INTRINSIC dimensions, which is what the browser
   needs to reserve the right aspect ratio before the image arrives. The
   predecessor's header only looked right because it hard-coded 32x31 into
   them; templated from config/site.json they became the real 314x306, and
   on the first client the mark filled a third of a 1024px window. 2rem
   matches the 32px the reference site renders. */
.site-header__logo img {
  block-size: 2rem;
  inline-size: auto;
}

.site-header__wordmark {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  color: var(--color-text);
  white-space: nowrap;
}

/* The short form (config/site.json shortName) is for when the full name and
   the nav do not fit on one row: see the breakpoints at the end of this
   block. Wide enough, only the full name shows. */
.site-header__short-name {
  display: none;
}

.nav-toggle {
  display: none;
  align-items: center;
  gap: var(--space-3xs);
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  padding: var(--space-xs) var(--space-s);
  font-size: var(--text-sm);
  min-height: var(--target-comfortable);
  cursor: pointer;
}

.nav-toggle__icon {
  flex: none;
  width: 1.25em;
  height: 1.25em;
}

/* Three bars that fold into a cross while the menu is open. fill-box puts
   each bar's turning point at its own centre, so the outer two meet in the
   middle. 6 is the gap between bars in the icon's 24-unit viewBox. */
.nav-toggle__bar {
  fill: none;
  stroke: currentcolor;
  stroke-width: 2;
  stroke-linecap: round;
  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform var(--duration-fast) var(--ease),
    opacity var(--duration-fast) var(--ease);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar--top {
  transform: translateY(6px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar--middle {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bar--bottom {
  transform: translateY(-6px) rotate(-45deg);
}

@media (prefers-reduced-motion: reduce) {
  .nav-toggle__bar {
    transition: none;
  }
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__list a {
  color: var(--color-text);
  white-space: nowrap;
  text-decoration: none;
  font-weight: var(--weight-medium);
  padding: var(--space-3xs) 0;
  border-bottom: 2px solid transparent;
}

.site-nav__list a:hover,
.site-nav__list a[aria-current="page"] {
  border-bottom-color: var(--color-brand);
}

/* The header's call to action (site.nav item with "button": true): the same
   .button as elsewhere, so it reads as the one thing to do next. The list is
   aligned on centre (above) so a taller button does not drop the text links. Its
   current-page state is a ring, not the underline: the button already fills
   the space the underline would sit in. */
.site-nav__list .site-nav__cta a {
  padding: var(--space-2xs) var(--space-m);
  white-space: nowrap;
  border-bottom: 0;
  color: var(--color-text);
}

.site-nav__list .site-nav__cta a:hover {
  color: var(--color-text-inverse);
}

.site-nav__list .site-nav__cta a[aria-current="page"] {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

/* WHERE THE HEADER CHANGES SHAPE. Both widths are measured, not guessed,
   and depend on this site's name and nav, so a site with a longer nav or
   name re-measures them. Last measured 16 October 2026, at 16px, with seven
   nav items: full name 365px, short name 101px, the nav 782px, the gap
   between them 24px, the header's gutters 48px.

   - The name is decided by the HEADER's width, not the window's: the header
     stops growing at --container-max, so a window-width rule would show the
     full name on a wide screen where it still does not fit. Below 74rem of
     header content (365 + 24 + 782 = 1171px, rounded up) the short name
     shows and the full name is visually hidden: still the link's accessible
     name, never display: none. With this nav that is every width, because
     the header's content never exceeds 72rem. Browsers without container
     queries show the full name, which wraps but still reads.
   - Up to 61rem of window the nav does not fit beside even the short name
     (101 + 24 + 782 + 48 = 955px), so it folds behind the Menu button
     (below). It was 640px until 16 October 2026, which left a band where
     the links wrapped onto two lines each. */
@container site-header (max-width: 74rem) {
  .site-header__wordmark--has-short .site-header__name {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .site-header__wordmark--has-short .site-header__short-name {
    display: inline;
  }
}

@media (max-width: 61rem) {
  /* Without this, .site-nav stays a flex item in the same nowrap row as
     the logo and toggle — width:100% below just shrinks it to whatever
     space is left, instead of moving to its own line. Found by actually
     opening the menu at a real mobile width: it rendered as a narrow
     column floating top-right, overlapping the hero content underneath
     it rather than pushing it down. */
  .site-header {
    flex-wrap: wrap;
  }

  .nav-toggle {
    display: inline-flex;
  }

  .site-nav {
    display: none;
    width: 100%;
  }

  .site-nav.is-open {
    display: block;

    /* Reads as a distinct panel now that it sits on its own line,
       not page content bleeding into the header row. */
    border-top: 1px solid var(--color-border);
    margin-top: var(--space-s);
  }

  .site-nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3xs);
    padding: var(--space-s) 0;
  }

  /* Full-width, min-44px tap targets for the stacked mobile list —
     the horizontal-inline version above stays compact since desktop
     nav is a mouse target, not a touch one. Measured before this fix:
     30px tall and only as wide as the link text (e.g. 43×30 for
     "Home") — under both Apple's 44pt and Android's 48dp touch-target
     guidance, and with items only 8px apart, adjacent short links were
     genuinely mis-tappable. */
  .site-nav__list a {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: var(--target-comfortable);
    padding: var(--space-2xs) 0;
  }

  /* Stacked, the call to action keeps its button shape at its own width,
     with a little space above to set it apart from the text links. */
  .site-nav__list .site-nav__cta {
    margin-top: var(--space-2xs);
  }

  .site-nav__list .site-nav__cta a {
    width: auto;
    display: inline-flex;
    padding: var(--space-2xs) var(--space-m);
  }
}

/* With scripting off, nothing can open the panel above: main.js is the only
   thing that adds .is-open. Without this block the mobile nav was simply
   gone — a Menu button that did nothing and no links at all. So show the
   stacked list and drop the dead button. This covers scripting disabled,
   not main.js failing to load; a script-added class would cover both, but
   it collapses the nav after first paint and shifts the whole page. */
@media (max-width: 61rem) and (scripting: none) {
  .nav-toggle {
    display: none;
  }

  .site-nav {
    display: block;
  }
}

/* --- Main content --------------------------------------------- */

main {
  max-width: var(--container-max);
  margin-inline: auto;
  padding: var(--space-l) var(--container-gutter) var(--space-3xl);
}

/* --- Section spacing ------------------------------------------------ */

/* `.section` sits at (0,1,0) — the same specificity as .hero, .notice,
   .card-grid and .photo, which all also set max-width. Safe because no
   element in any template carries .section alongside one of those; it is
   always a wrapper around them. `.section > :first-child` at (0,2,0)
   outranks their margin shorthands regardless of file order. */
.section {
  margin: var(--space-2xl) 0;
  max-width: none;
}

.section > :first-child {
  margin-top: 0;
}

/* --- Footer ------------------------------------------------------ */

/* Real extracted footer: dark navy background, near-white text —
   see tokens.css --color-brand-strong / --color-text-inverse. Not the
   generic light footer this started as. Contained, not full-bleed —
   matches how .site-header and main already just centre a column
   rather than introducing a new full-width-background pattern. */
.site-footer {
  background: var(--color-brand-strong);
  padding: var(--space-l) var(--container-gutter);
  max-width: var(--container-max);
  margin-inline: auto;
  display: grid;
  gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  font-size: var(--text-sm);
  color: var(--color-text-inverse);
}

.site-footer h2 {
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-brand);
  margin: 0 0 var(--space-2xs);
}

/* (0,1,1), equal to `a:hover` in base.css and deliberately winning over
   it — footer links keep their inverse colour on hover. Depends on this
   file loading AFTER base.css; see base.njk. */
.site-footer a {
  color: var(--color-text-inverse);
}

.site-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3xs);
}

.site-footer__copyright {
  grid-column: 1 / -1;
  margin: 0;
  padding-top: var(--space-m);
  border-top: 1px solid var(--color-border);
}

.icon {
  vertical-align: -0.15em;
}

/* --- Report layout (articles) --------------------------
   `main` is a centred, padded column (see "Main content" above) — every
   other page on this site is happy inside it. The report masthead and
   mid-article pull-quote band are the one place this site needs a full-
   bleed colour band, because that's the strongest, most legible way to
   mark "this is a different kind of page" against the plain brochure
   pages either side of it in the nav. `calc(50% - 50vw)` breaks a band
   out of any ancestor's centred max-width, however deep it's nested —
   the standard, boring way to do this without a wrapping-element hack. */
.report-band {
  margin-inline: calc(50% - 50vw);
  padding-inline: max(var(--container-gutter), calc(50vw - var(--container-max) / 2 + var(--container-gutter)));
}

.report-masthead {
  padding-block: var(--space-2xl) var(--space-xl);
  background: var(--color-brand-strong);
  color: var(--color-text-inverse);
  border-bottom: 3px solid var(--color-brand);
}

.report-masthead :is(h1, p) {
  color: var(--color-text-inverse);
}

.report-band--pull {
  padding-block: var(--space-xl);
  background: var(--color-brand-strong);
  color: var(--color-text-inverse);
}

.report-band--pull :is(h2, p, blockquote) {
  color: var(--color-text-inverse);
}

/* The closing "through-line" band — the last section of every article.
   Same ground and inverse text as .report-band--pull, so a piece opens
   dark, has one dark beat in the middle, and closes dark: the argument
   lands on the same surface it was set up on, and the two dark bands
   bracket the evidence between them.

   The top border repeats the masthead's bottom border exactly (3px solid
   var(--color-brand)) so the closer reads as that headline's bookend
   rather than a third, unrelated block.

   The selector carries `.report-body > .section` deliberately. That rule
   sets `border-top: 1px solid var(--color-border)` on every section and
   sits at (0,2,0); a bare `.report-band--closer` at (0,1,0) would lose to
   it and leave a grey hairline above the band instead of the brand rule.

   padding-top is left to `.report-body > .section` (--space-2xl) on
   purpose rather than restated here — that is where .report-band--pull's
   own top padding comes from too, so both bands sit on the same 64/48
   rhythm. Only padding-bottom needs setting: a plain .section has none,
   which on a coloured band would crop the text against the fold. */
.report-body > .section.report-band--closer {
  padding-bottom: var(--space-xl);
  background: var(--color-brand-strong);
  color: var(--color-text-inverse);
  border-top: 3px solid var(--color-brand);
}

.report-band--closer :is(h2, p, blockquote) {
  color: var(--color-text-inverse);
}

/* The body of an article: same section rhythm as brochure pages
   (.section, reused as-is), but articles are read start to finish, so a
   rule between sections reads better than the brochure pages' plain gap. */
.report-body > .section {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2xl);
}

.report-body > .section:first-child {
  border-top: 0;
  padding-top: 0;
}

.report-colophon {
  margin-top: var(--space-2xl);
  padding-top: var(--space-l);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.report-colophon p {
  max-width: none;
}
