/* ==========================================================================
   GSI Labs / gsilabs.de
   Design system and page styles.

   Colors are locked to "brand assets/brand.md" and exposed as the four
   custom properties below. Nothing in this file introduces another hue.
   ========================================================================== */

/* --- fonts ---------------------------------------------------------------- */

@font-face {
  font-family: 'Space Grotesk';
  src: url('../fonts/SpaceGrotesk-Variable.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter var';
  src: url('../fonts/Inter-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --- tokens --------------------------------------------------------------- */

:root {
  /* Brand palette. Locked. */
  /* Was the warm off white #F8F6F2 until the client asked for pure white as the
     light page ground. It also serves as the type color inside black bands. */
  --paper: #ffffff;
  --ink: #1f271b;
  /* Secondary surface. Was the brand sand #EAE3D7 until the client asked for
     light grey on the tiles. */
  --mist: #ebebeb;
  --void: #000000;

  /* Derived surfaces and lines. Opacity only, never a new hue. */
  --ink-70: color-mix(in srgb, var(--ink) 70%, transparent);
  --ink-55: color-mix(in srgb, var(--ink) 55%, transparent);
  --rule: color-mix(in srgb, var(--ink) 16%, transparent);
  --rule-soft: color-mix(in srgb, var(--ink) 9%, transparent);

  /* Per band, set by .band variants so components stay ground agnostic. */
  --ground: var(--paper);
  --on-ground: var(--ink);
  --on-ground-dim: var(--ink-70);
  --on-ground-faint: var(--ink-55);
  --line: var(--rule);
  --line-soft: var(--rule-soft);
  --surface: var(--mist);

  /* Type */
  --font-display: 'Space Grotesk', 'Segoe UI', system-ui, sans-serif;
  --font-body: 'Inter var', 'Inter', system-ui, -apple-system, sans-serif;

  --step-hero: clamp(2.85rem, 6.25vw, 6.1rem);
  --step-display: clamp(1.9rem, 3.9vw, 3.5rem);
  --step-title: clamp(1.35rem, 1.9vw, 1.75rem);
  --step-metric: clamp(3rem, 6.4vw, 5.75rem);
  --step-lede: clamp(1.0625rem, 1.05vw, 1.1875rem);
  --step-body: 1rem;
  --step-small: 0.875rem;
  --step-label: 0.6875rem;

  /* Spacing scale. Sections only ever use --band-y. */
  --s1: 0.375rem;
  --s2: 0.625rem;
  --s3: 1rem;
  --s4: 1.5rem;
  --s5: 2.25rem;
  --s6: 3.5rem;
  --s7: 5rem;
  --s8: 7.5rem;

  --band-y: clamp(4.5rem, 8.5vw, 9rem);
  --gutter: clamp(1.25rem, 4vw, 4.5rem);
  --measure: 1560px;
  --col-gap: clamp(1.25rem, 2.2vw, 2.25rem);

  --head-h: 74px;
  /* No corner radius token. Tiles, controls, image frames and the nav panel are
     all square by the client's decision. The single exception is the leadership
     portraits on the about page, which are circles by the same decision. */

  /* Layered, ink tinted shadows. No flat grey drops. */
  --lift-1: 0 1px 1px color-mix(in srgb, var(--ink) 4%, transparent),
            0 4px 10px -4px color-mix(in srgb, var(--ink) 10%, transparent);
  --lift-2: 0 1px 1px color-mix(in srgb, var(--ink) 5%, transparent),
            0 10px 20px -8px color-mix(in srgb, var(--ink) 14%, transparent),
            0 28px 48px -28px color-mix(in srgb, var(--ink) 18%, transparent);

  --ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.33, 1, 0.68, 1);
}

/* --- reset ---------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--head-h) + 1rem);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-body);
  line-height: 1.7;
  letter-spacing: -0.004em;
  -webkit-font-smoothing: antialiased;
  font-synthesis-weight: none;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

/* The HTML width/height attributes are presentation hints that would otherwise
   beat a CSS aspect-ratio, so any sized media resolves its height from the
   ratio instead. Zero specificity keeps component rules in control. */
:where(img[width][height]) {
  height: auto;
}

/* Photography ships in its original colour. Where type sits over an image the
   legibility scrim is a plain black gradient, so it darkens without tinting. */
.photo {
  filter: saturate(1.02) contrast(1.02);
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
ul,
ol,
dl,
dd {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 0;
}

::selection {
  background: var(--ink);
  color: var(--paper);
}

.skip {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-display);
  font-size: var(--step-small);
}

.skip:focus-visible {
  top: 0.75rem;
}

.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- type primitives ------------------------------------------------------ */

.display,
.hero__line,
.metric__value,
.title {
  font-family: var(--font-display);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.display {
  font-size: var(--step-display);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.title {
  font-size: var(--step-title);
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.lede {
  font-size: var(--step-lede);
  line-height: 1.7;
  color: var(--on-ground-dim);
  text-wrap: pretty;
}

.body {
  color: var(--on-ground-dim);
  text-wrap: pretty;
}

.small {
  font-size: var(--step-small);
  line-height: 1.6;
  color: var(--on-ground-faint);
}

/* Utility face: Space Grotesk, uppercase, wide. Eyebrows, meta, title blocks. */
.label {
  font-family: var(--font-display);
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--on-ground-faint);
}

/* The "//" delimiter is lifted from GSI Labs' own copy and is the only
   connective mark used between label fragments. */
.label__sep {
  opacity: 0.45;
}

.eyebrow {
  margin-bottom: var(--s5);
}

/* --- layout --------------------------------------------------------------- */

.band {
  position: relative;
  padding-block: var(--band-y);
  background: var(--ground);
  color: var(--on-ground);
}

.band--void {
  --ground: var(--void);
  --on-ground: var(--paper);
  --on-ground-dim: color-mix(in srgb, var(--paper) 72%, transparent);
  --on-ground-faint: color-mix(in srgb, var(--paper) 52%, transparent);
  --line: color-mix(in srgb, var(--paper) 17%, transparent);
  --line-soft: color-mix(in srgb, var(--paper) 9%, transparent);
  --surface: color-mix(in srgb, var(--paper) 6%, transparent);
}

.band--flush {
  padding-block: 0;
}

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--col-gap);
}

/* --- buttons and links ---------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.8rem 1.35rem;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  letter-spacing: -0.01em;
  /* Every control on the site is square cornered, same as the tiles. */
  border-radius: 0;
  border: 1px solid transparent;
  transition: transform 0.35s var(--ease-spring), opacity 0.2s linear,
    background-color 0.2s linear, border-color 0.2s linear, color 0.2s linear;
}

.btn__arrow {
  width: 0.85em;
  height: 0.85em;
  flex: none;
  transition: transform 0.35s var(--ease-spring);
}

.btn:hover .btn__arrow {
  transform: translateX(3px);
}

.btn:active {
  transform: translateY(1px) scale(0.985);
}

.btn--solid {
  background: var(--on-ground);
  color: var(--ground);
}

.btn--solid:hover {
  background: color-mix(in srgb, var(--on-ground) 82%, var(--ground));
}

.btn--outline {
  border-color: var(--line);
  color: var(--on-ground);
}

.btn--outline:hover {
  border-color: var(--on-ground);
  background: color-mix(in srgb, var(--on-ground) 6%, transparent);
}

/* Inline link with a rule that draws in on hover. */
.link {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  position: relative;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
}

.link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease-spring);
}

.link:hover::after {
  transform: scaleX(1);
}

/* ==========================================================================
   Header
   ========================================================================== */

.site-head {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  height: var(--head-h);
  color: var(--paper);
  /* The header floats over two different grounds, so it carries its own pair
     of ground tokens and every button inside it stays ground agnostic. */
  --ground: var(--void);
  --on-ground: var(--paper);
  --line: color-mix(in srgb, var(--paper) 20%, transparent);
  transition: background-color 0.35s var(--ease-out), color 0.35s var(--ease-out),
    box-shadow 0.35s var(--ease-out);
}

.site-head::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 1px;
  background: currentColor;
  opacity: 0;
  transition: opacity 0.35s var(--ease-out);
}

/* Paper appearance: once the dark hero has scrolled away, and whenever the
   mobile sheet is open so the bar and the sheet read as one surface. */
.site-head[data-mode='paper'],
.site-head[data-sheet='open'] {
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.1);
  color: var(--ink);
  --ground: var(--paper);
  --on-ground: var(--ink);
  --line: var(--rule);
}

/* backdrop-filter would make the header a containing block for its own
   position: fixed descendants, which collapses the mobile sheet. With the
   sheet open the bar is fully opaque anyway, so drop the filter. */
.site-head[data-sheet='open'] {
  background: var(--paper);
  backdrop-filter: none;
}

.site-head[data-mode='paper']::after,
.site-head[data-sheet='open']::after {
  opacity: 0.16;
}

.site-head[data-sheet='open'] .brand__day {
  opacity: 1;
}

.site-head[data-sheet='open'] .brand__night {
  opacity: 0;
}

.site-head__inner {
  height: 100%;
  display: flex;
  align-items: center;
  gap: var(--s4);
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.brand {
  position: relative;
  display: block;
  flex: none;
  width: 92px;
  height: 42px;
}

.brand img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left center;
  transition: opacity 0.35s var(--ease-out);
}

.brand__day {
  opacity: 0;
}

.site-head[data-mode='paper'] .brand__day {
  opacity: 1;
}

.site-head[data-mode='paper'] .brand__night {
  opacity: 0;
}

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

.nav {
  margin-inline: auto;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.nav__trigger,
.nav__top {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  border-radius: 0;
  transition: background-color 0.2s linear, opacity 0.2s linear;
}

.nav__trigger:hover,
.nav__top:hover,
.nav__item[data-open='true'] .nav__trigger {
  background: color-mix(in srgb, currentColor 9%, transparent);
}

.nav__chev {
  width: 9px;
  height: 9px;
  opacity: 0.55;
  transition: transform 0.35s var(--ease-spring);
}

.nav__item[data-open='true'] .nav__chev {
  transform: rotate(180deg);
}

.nav__item {
  position: relative;
}

/* The menu is a transparent bridge that starts flush against the trigger, and the
   visible panel is the list inside it. Without the bridge, the gap between the two
   belongs to neither, and the pointer leaves the item while crossing it, which
   closes the menu before it can be reached. */
.nav__menu {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 268px;
  padding-top: 0.6rem;
  color: var(--ink);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.22s var(--ease-out), transform 0.32s var(--ease-spring);
}

.nav__menu > ul {
  padding: 0.5rem;
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  box-shadow: var(--lift-2);
}

.nav__item[data-open='true'] .nav__menu {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

.nav__menu a {
  display: block;
  padding: 0.55rem 0.75rem;
  font-size: var(--step-small);
  border-radius: 0;
  color: var(--ink-70);
  transition: background-color 0.18s linear, color 0.18s linear;
}

.nav__menu a:hover {
  background: var(--mist);
  color: var(--ink);
}

/* --- language switcher ---------------------------------------------------- */

/* The nav beside it is centred by its own auto margins, so this only needs to
   keep its width. */
.nav__item--lang {
  flex: none;
}

/* The code is two capitals, so it needs the tracking of a label rather than the
   setting of a nav word. */
.nav__trigger--lang {
  letter-spacing: 0.08em;
}

/* Right aligned and narrow: it sits at the end of the header, where a wide panel
   dropped from the left edge would run past the viewport. */
.nav__menu--lang {
  left: auto;
  right: 0;
  min-width: 168px;
}

/* The two entries match the nav links above them, whichever element they are:
   a link once the language exists, a disabled button until then, and a span for
   the one that is already on. */
.nav__lang {
  display: block;
  width: 100%;
  padding: 0.55rem 0.75rem;
  font-family: inherit;
  font-size: var(--step-small);
  text-align: left;
  color: var(--ink-70);
}

.nav__lang.is-current {
  color: var(--ink);
  background: var(--mist);
}

.nav__lang:disabled {
  color: color-mix(in srgb, var(--ink) 35%, transparent);
  cursor: default;
}

.head__cta {
  flex: none;
}

.nav__foot {
  display: none;
}

.nav-toggle {
  display: none;
  flex: none;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.7rem 0.9rem;
  border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  border-radius: 0;
}

/* ==========================================================================
   Hero
   ========================================================================== */

/* The hero is one full bleed frame of footage. It starts at the very top of the
   page so the video also runs behind the transparent header. */
.hero {
  position: relative;
  overflow: clip;
  padding-top: calc(var(--head-h) + clamp(2rem, 5vw, 4.5rem));
  padding-bottom: clamp(2.5rem, 6vw, 5rem);
  min-height: max(620px, 100svh);
  display: flex;
  flex-direction: column;
}

.hero__video,
.hero__cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Held down a little so paper coloured type reads over bright concrete and
     sky. The colour itself is untouched. */
  filter: contrast(1.04) brightness(0.86);
}

/* Legibility scrim. Weighted to the left where the headline sits, to the top
   under the header, and to the bottom under the closing copy. */
.hero__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to right, color-mix(in srgb, var(--void) 82%, transparent) 0%,
      color-mix(in srgb, var(--void) 60%, transparent) 30%,
      color-mix(in srgb, var(--void) 32%, transparent) 62%,
      color-mix(in srgb, var(--void) 22%, transparent) 100%),
    linear-gradient(to bottom, color-mix(in srgb, var(--void) 62%, transparent) 0%,
      transparent 22%),
    linear-gradient(to top, color-mix(in srgb, var(--void) 80%, transparent) 0%,
      color-mix(in srgb, var(--void) 30%, transparent) 26%,
      transparent 48%);
}

/* A still holds far more detail than a frame of the video does, and the gradients
   alone leave the middle of it competing with the type. An even wash under them
   settles the whole picture down without flattening the corners further. */
.hero--cover .hero__scrim {
  background:
    linear-gradient(color-mix(in srgb, var(--void) 42%, transparent),
      color-mix(in srgb, var(--void) 42%, transparent)),
    linear-gradient(to right, color-mix(in srgb, var(--void) 72%, transparent) 0%,
      color-mix(in srgb, var(--void) 44%, transparent) 38%,
      color-mix(in srgb, var(--void) 22%, transparent) 100%),
    linear-gradient(to bottom, color-mix(in srgb, var(--void) 58%, transparent) 0%,
      transparent 24%),
    linear-gradient(to top, color-mix(in srgb, var(--void) 62%, transparent) 0%,
      transparent 40%);
}

.hero__wrap {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.hero__stage {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  /* A flexible middle row pins the opening lines to the top and the closing
     line plus the support copy to the bottom of the frame. */
  grid-template-rows: auto minmax(clamp(2rem, 8vw, 7rem), 1fr) auto;
  column-gap: var(--col-gap);
  row-gap: clamp(1rem, 3vw, 2rem);
}

.hero__title {
  position: relative;
  pointer-events: none;
  text-shadow: 0 1px 30px color-mix(in srgb, var(--void) 40%, transparent);
}

.hero__title--a {
  grid-column: 1 / 9;
  grid-row: 1;
}

.hero__title--b {
  grid-column: 1 / 8;
  grid-row: 3;
  align-self: end;
}

.hero__line {
  display: block;
  font-size: var(--step-hero);
  font-weight: 400;
  line-height: 0.98;
  letter-spacing: -0.038em;
  text-wrap: nowrap;
}

.hero__support {
  grid-column: 9 / 13;
  grid-row: 3;
  align-self: end;
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  position: relative;
}

.hero__support p {
  font-size: var(--step-lede);
  line-height: 1.55;
  letter-spacing: -0.012em;
  color: var(--paper);
  text-shadow: 0 1px 24px color-mix(in srgb, var(--void) 55%, transparent);
}

.hero__support .btn {
  align-self: start;
}

/* ==========================================================================
   Thesis band
   ========================================================================== */

.statement {
  grid-column: 1 / 10;
}

.statement__title {
  font-size: clamp(1.75rem, 3.5vw, 3.15rem);
  line-height: 1.1;
  letter-spacing: -0.032em;
  font-family: var(--font-display);
  font-weight: 400;
  text-wrap: balance;
}

/* ==========================================================================
   Capabilities
   ========================================================================== */

.deflist {
  grid-column: 1 / 7;
  display: flex;
  /* The media beside it takes six columns, so the copy keeps a measure of its
     own instead of running up against the image. */
  padding-right: var(--col-gap);
  flex-direction: column;
  gap: var(--s5);
}

.deflist__lede {
  max-width: 46ch;
}

.cap__list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}

.cap__row {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: var(--s3);
  padding-block: var(--s4);
  border-bottom: 1px solid var(--line);
}

.cap__row dt {
  padding-top: 0.35rem;
}

.cap__row dd {
  color: var(--on-ground-dim);
}

/* The figure spans both rows so the quote card below the copy has something to
   sit against instead of leaving the right half of the band empty. */
/* Six columns rather than five, at the client's request. The spacer column that
   used to sit between the copy and the image is gone, so the copy carries its
   own right hand padding. */
.deflist__media {
  grid-column: 7 / 13;
  grid-row: 1 / 3;
  position: relative;
  align-self: stretch;
  min-height: clamp(320px, 40vw, 620px);
}

.deflist__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* No caption sits on this image any more, so it carries no scrim. The hairline
   seats it against the paper ground, same as the news card images. */
.deflist__media::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 10%, transparent);
  pointer-events: none;
}

/* --- quote card ----------------------------------------------------------- */

.quote-card {
  grid-column: 1 / 7;
  margin-top: var(--s6);
  /* Flush with the rules above it, which the copy column insets by the same
     amount to hold itself off the image. */
  margin-right: var(--col-gap);
  padding: var(--s5);
  background: var(--surface);
  /* Tiles are square cornered on purpose. */
  border-radius: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s4);
  align-items: start;
  box-shadow: var(--lift-1);
}

.quote-card blockquote {
  font-family: var(--font-display);
  font-size: clamp(1.0625rem, 1.35vw, 1.3125rem);
  font-weight: 400;
  line-height: 1.42;
  letter-spacing: -0.018em;
  text-wrap: pretty;
}

.quote-card figcaption {
  margin-top: var(--s4);
}

.quote-card__name {
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
}

.quote-card__logo {
  width: 82px;
  height: auto;
  flex: none;
  mix-blend-mode: multiply;
}

/* ==========================================================================
   Metrics
   ========================================================================== */

.metrics__head {
  grid-column: 1 / 8;
}

.metrics__statement {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.85vw, 1.6875rem);
  font-weight: 400;
  line-height: 1.34;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}

.metrics__list {
  grid-column: 1 / 13;
  margin-top: var(--s7);
  display: grid;
  gap: 0;
  border-top: 1px solid var(--line);
}

.metric {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--s3) var(--col-gap);
  align-items: baseline;
  padding-block: clamp(1.75rem, 3.2vw, 2.75rem);
  border-bottom: 1px solid var(--line);
}

.metric__value {
  font-size: var(--step-metric);
  font-weight: 400;
  line-height: 0.94;
  letter-spacing: -0.045em;
}

.metric__label {
  font-size: var(--step-lede);
  color: var(--on-ground-dim);
  text-wrap: pretty;
}

.metrics__note {
  grid-column: 1 / 9;
  margin-top: var(--s4);
}

/* ==========================================================================
   Process
   ========================================================================== */

.process__head {
  grid-column: 1 / 8;
}

.process__list {
  grid-column: 1 / 13;
  margin-top: var(--s7);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
  counter-reset: stage;
}

.stage {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  padding: var(--s5);
  background: var(--surface);
  /* Tiles are square cornered on purpose. */
  border-radius: 0;
  transition: transform 0.45s var(--ease-spring), box-shadow 0.45s var(--ease-out);
}

.stage:hover {
  transform: translateY(-4px);
  box-shadow: var(--lift-2);
}

/* `layout: rows` reads the same items down the page instead of across it: the
   number, then the heading, then the prose, one line per item. For a list that is
   read rather than compared, which is what the mission statements are, the tiles
   put three short blocks of text into three boxes for no reason. */
.process__list--rows {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  border-top: 1px solid var(--line);
}

.process__list--rows .stage {
  display: grid;
  grid-template-columns: 4rem minmax(0, 14rem) minmax(0, 1fr);
  gap: var(--col-gap);
  align-items: baseline;
  padding: var(--s4) 0;
  background: none;
  border-bottom: 1px solid var(--line);
}

.process__list--rows .stage:hover {
  transform: none;
  box-shadow: none;
}

/* The rule after the number belongs to the tile, where it fills the width. In a
   row the number sits in its own column and the rule has nothing to span. */
.process__list--rows .stage__index::after {
  display: none;
}

.stage__index {
  counter-increment: stage;
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.stage__index::before {
  content: counter(stage, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: var(--step-label);
  font-weight: 500;
  letter-spacing: 0.14em;
}

.stage__index::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.22;
}

.stage p {
  color: var(--on-ground-dim);
  font-size: var(--step-small);
  line-height: 1.65;
}

/* ==========================================================================
   Pull quote band
   ========================================================================== */

.pull {
  grid-column: 3 / 12;
}

.pull blockquote {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.1vw, 2.75rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.pull__by {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s6);
}

/* A client wordmark, not a portrait, so it is set on the type baseline grid and
   separated by a rule rather than cropped into a circle. */
.pull__logo {
  width: 124px;
  flex: none;
  padding-right: var(--s4);
  border-right: 1px solid var(--line);
}

.pull__name {
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
}

/* ==========================================================================
   News
   ========================================================================== */

.news__head {
  grid-column: 1 / 8;
}

.news__all {
  grid-column: 11 / 13;
  justify-self: end;
  align-self: end;
}

.news__list {
  grid-column: 1 / 13;
  margin-top: var(--s6);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.card__media {
  position: relative;
  overflow: hidden;
}

.card__media img {
  width: 100%;
  aspect-ratio: 16 / 11;
  object-fit: cover;
  transition: transform 0.7s var(--ease-spring), filter 0.4s var(--ease-out);
}

/* No type sits on a card image, so it gets a hairline to seat it against paper
   instead of an overlay that would mute the colour. */
.card__media::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 10%, transparent);
  pointer-events: none;
}

.card:hover .card__media img {
  transform: scale(1.035);
  filter: saturate(1.08) contrast(1.03);
}

.card:hover .card__title {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.card__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding-top: var(--s2);
  border-top: 1px solid var(--line);
}

.card__title {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.32;
  letter-spacing: -0.018em;
  text-wrap: pretty;
}

.card__link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.card {
  position: relative;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-foot {
  background: var(--void);
  color: var(--paper);
  --ground: var(--void);
  --on-ground: var(--paper);
  --on-ground-dim: color-mix(in srgb, var(--paper) 68%, transparent);
  --on-ground-faint: color-mix(in srgb, var(--paper) 46%, transparent);
  --line: color-mix(in srgb, var(--paper) 15%, transparent);
}

.site-foot__main {
  display: grid;
  grid-template-columns: minmax(0, 3fr) repeat(4, minmax(0, 2fr));
  gap: var(--s6) var(--col-gap);
  padding-block: clamp(3.5rem, 6vw, 6rem);
}

.foot-brand {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}

.foot-brand__logo {
  width: 104px;
}

.foot-brand address {
  font-style: normal;
  font-size: var(--step-small);
  line-height: 1.65;
}

.foot-brand dt {
  margin-bottom: 0.25rem;
}

.foot-brand dd {
  font-size: var(--step-small);
  color: var(--on-ground-dim);
}

.foot-brand dd + dt {
  margin-top: var(--s3);
}

.foot-brand a:hover {
  color: var(--paper);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.foot-col h2 {
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  margin-bottom: var(--s4);
}

.foot-col li + li {
  margin-top: 0.6rem;
}

.foot-col a {
  font-size: var(--step-small);
  color: var(--on-ground-dim);
  transition: color 0.2s linear;
}

.foot-col a:hover {
  color: var(--paper);
}

.site-foot__base {
  border-top: 1px solid var(--line);
  padding-block: var(--s5);
  display: grid;
  grid-template-columns: minmax(0, 3fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--s4);
}

.social {
  display: flex;
  gap: 0.6rem;
}

.social a {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 0;
  transition: background-color 0.2s linear, border-color 0.2s linear,
    transform 0.35s var(--ease-spring);
}

.social a:hover {
  background: color-mix(in srgb, var(--paper) 12%, transparent);
  border-color: color-mix(in srgb, var(--paper) 40%, transparent);
  transform: translateY(-2px);
}

.social svg {
  width: 15px;
  height: 15px;
}

.site-foot__out {
  justify-self: end;
  text-align: right;
}

.site-foot__legal {
  padding-bottom: var(--s6);
  color: var(--on-ground-faint);
  font-size: 0.75rem;
  line-height: 1.6;
}

/* The copyright and the way back into the consent choice share the first cell of
   the base row, so the three column grid above stays a three column grid. */
.site-foot__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2) var(--s4);
}

.site-foot__consent {
  color: var(--on-ground-faint);
}

.site-foot__consent:hover {
  color: var(--on-ground);
}

/* ==========================================================================
   Consent
   ========================================================================== */

/* A black band across the foot of the window, on the same ground as the dark
   sections of the site. It sits above the header, which is the only other fixed
   thing on the page. Two panes live inside it and only one is ever shown. */
.consent {
  position: fixed;
  inset: auto 0 0;
  z-index: 200;
  --ground: var(--void);
  --on-ground: var(--paper);
  --on-ground-dim: color-mix(in srgb, var(--paper) 72%, transparent);
  --on-ground-faint: color-mix(in srgb, var(--paper) 58%, transparent);
  --line: color-mix(in srgb, var(--paper) 22%, transparent);
  background: var(--void);
  color: var(--paper);
  border-top: 1px solid var(--line);
  animation: consent-in 0.5s var(--ease-spring) both;
}

@keyframes consent-in {
  from {
    transform: translateY(100%);
  }
}

/* The mobile menu fills the window below the header, so while it is open it has
   to pass over the consent band rather than under it. */
.site-head[data-sheet='open'] {
  z-index: 300;
}

.consent__inner {
  max-width: var(--measure);
  margin-inline: auto;
  padding: var(--s4) var(--gutter);
  /* The panel can outgrow a short window, so it scrolls inside the band rather
     than pushing its own buttons off the bottom of the screen. */
  max-height: min(80vh, 44rem);
  overflow-y: auto;
}

.consent[data-pane='notice'] .consent__panel,
.consent[data-pane='panel'] .consent__notice {
  display: none;
}

/* --- first layer ---------------------------------------------------------- */

.consent__notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3) var(--s5);
}

.consent__text {
  flex: 1 1 30rem;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.consent__text .label {
  color: var(--on-ground-faint);
}

.consent__text p {
  margin: 0;
  max-width: 68ch;
  color: var(--on-ground-dim);
}

.consent__more {
  color: var(--on-ground);
  white-space: nowrap;
}

/* --- second layer --------------------------------------------------------- */

.consent__panel {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.consent__panel > .label {
  color: var(--on-ground-faint);
}

/* Two columns where they fit, one where they do not. Each switch stays with the
   text that explains it either way. */
.consent__groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: var(--s3) var(--s5);
}

.consent-group {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}

.consent-group__row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  cursor: pointer;
}

.consent-group__row:has(:disabled) {
  cursor: default;
}

/* The site has no rounded corners and no second hue, so the switch is a square
   box that fills in, drawn from the same two colours as everything else. */
.consent-group__box {
  appearance: none;
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0;
  border: 1px solid var(--on-ground-faint);
  background: transparent;
  cursor: inherit;
  transition: background-color 0.2s linear, border-color 0.2s linear;
}

.consent-group__box:hover:not(:disabled) {
  border-color: var(--on-ground);
}

.consent-group__box:checked {
  background-color: var(--on-ground);
  border-color: var(--on-ground);
  /* The tick is a drawn path rather than a character, so it needs no glyph in the
     subset and cannot fall back to a system font. Its stroke is the band's own
     black, which is the ground behind the filled box. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.2 6.3 4.7 8.8 9.8 3.4' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='square'/%3E%3C/svg%3E");
  background-size: 84%;
  background-position: center;
  background-repeat: no-repeat;
}

.consent-group__box:disabled {
  opacity: 0.55;
}

.consent-group__name {
  font-family: var(--font-display);
  font-weight: 500;
}

.consent-group__always {
  color: var(--on-ground-faint);
}

.consent-group__body {
  margin: 0;
  color: var(--on-ground-dim);
}

/* --- both layers ---------------------------------------------------------- */

/* Accept and reject are the same control at the same size. A refusal that costs
   more than an agreement is not a decision the visitor made freely. */
.consent__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

.consent__actions .btn {
  flex: 1 1 auto;
  justify-content: center;
  min-width: 9rem;
}

.consent__panel .consent__actions {
  justify-content: flex-end;
}

@media (max-width: 640px) {
  .consent__actions .btn {
    flex: 1 1 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .consent {
    animation: none;
  }
}

/* ==========================================================================
   Blocks used by the generated pages
   ========================================================================== */

/* A section that reads as a continuation of the one above it. */
.band--flush-top {
  padding-top: 0;
}

/* --- statement ------------------------------------------------------------ */

.statement {
  grid-column: 1 / 10;
}

.statement--wide {
  grid-column: 1 / 13;
}

.statement--split {
  grid-column: 1 / 7;
}

.statement__title {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3.5vw, 3.15rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.032em;
  text-wrap: balance;
}

/* The gap belongs under a heading. With no heading above it, the copy starts at
   the top of the column like everything else in the row. */
.statement__body:first-child {
  margin-top: 0;
}

.statement__body {
  margin-top: var(--s5);
  max-width: 60ch;
  display: grid;
  gap: var(--s3);
}

.statement .btn {
  margin-top: var(--s5);
}

/* The picture beside a statement, square like the ones on the solution pages.
   Pinned to the top so copy shorter than the frame does not drag it up the band. */
.statement__media {
  grid-column: 7 / 13;
  align-self: start;
  aspect-ratio: 1 / 1;
}

.statement__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- page hero ------------------------------------------------------------ */

.page-hero {
  padding-top: calc(var(--head-h) + clamp(2.5rem, 6vw, 5.5rem));
}

/* A hero carrying nothing but the title has no second column to fill and no
   lede to hold the band open, so the full band padding below it reads as a gap
   rather than as breathing room. It keeps enough to sit clear of whatever rule
   or section follows. */
.page-hero--title-only {
  padding-bottom: var(--s6);
}

.page-hero__head {
  grid-column: 1 / 7;
  grid-row: 1;
}

.page-hero__title {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 4.9vw, 4.5rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.038em;
  text-wrap: balance;
}

.page-hero__aside {
  grid-column: 1 / 6;
  grid-row: 2;
  align-self: end;
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--s5);
  padding-top: var(--s6);
}

.page-hero__media {
  grid-column: 7 / 13;
  grid-row: 1 / 3;
  position: relative;
  align-self: stretch;
  min-height: clamp(260px, 32vw, 460px);
}

/* With `ratio` on the image, the frame holds that shape instead of stretching to
   whatever the text column happens to be. The six solution pages set 1/1, which
   is the square the pages they replace use: without it a page with a long lede
   gets a hero half again as tall as the next page's, for no reason the reader
   can see. */
.page-hero__media--ratio {
  align-self: start;
  min-height: 0;
  aspect-ratio: var(--media-ratio);
}

/* Banner variant: the picture runs the content width above the title, which is
   how the Company pages open. Row placement rather than markup order, so the
   heading still comes first to anything reading the document. */
.page-hero--banner {
  padding-top: calc(var(--head-h) + var(--s5));
}

.page-hero--banner .page-hero__media {
  grid-column: 1 / 13;
  grid-row: 1;
}

.page-hero--banner .page-hero__head {
  grid-row: 2;
  margin-top: var(--s6);
}

.page-hero--banner .page-hero__aside {
  grid-row: 3;
}

.page-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.page-hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 10%, transparent);
  pointer-events: none;
}

/* --- showcase ------------------------------------------------------------- */

.showcase__head {
  grid-column: 1 / 9;
}

.showcase__title {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.1vw, 2.75rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.showcase__body {
  margin-top: var(--s5);
  max-width: 62ch;
  display: grid;
  gap: var(--s3);
  color: var(--on-ground-dim);
}

.showcase__media {
  grid-column: 1 / 13;
  margin-top: var(--s7);
  position: relative;
}

.showcase__media img {
  width: 100%;
  aspect-ratio: 21 / 9;
  object-fit: cover;
}

.deflist__title {
  margin-bottom: var(--s3);
}

/* --- tile metrics --------------------------------------------------------- */

.tile-metrics {
  grid-column: 8 / 13;
  display: grid;
  gap: var(--col-gap);
  align-content: start;
  /* Beside an image that spans the rows, the row is as tall as the picture. The
     tiles size to their content and sit at the top of it rather than stretching
     to fill the leftover. */
  align-self: start;
}

/* `plain: true` drops the filled tile and rules the figures off instead, which is
   how the careers page lists them. The tile reads as a statistic worth framing;
   these are facts about the company, and framing them opens a gap beside a
   picture that is taller than they are. */
.tile-metrics--plain {
  gap: 0;
  border-top: 1px solid var(--line);
}

.tile-metrics--plain .tile-metric {
  padding: var(--s4) 0;
  background: none;
  border-bottom: 1px solid var(--line);
}

.tile-metric {
  padding: var(--s5);
  background: var(--surface);
}

.tile-metric__value {
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 3.6vw, 3.25rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

.tile-metric__label {
  margin-top: var(--s3);
}

.tile-metrics__note {
  grid-column: 1 / 8;
  margin-top: var(--s5);
}

/* With an image beside them, the tiles move under the claim they back up and the
   picture takes the right hand half, running the height of both. The row spans
   are counted rather than open ended: `-1` resolves against the explicit grid,
   which has no rows declared here, so it would collapse to a single row. */
.band--metrics-media .tile-metrics,
.band--metrics-media .tile-metrics__note {
  grid-column: 1 / 7;
}

.tile-metrics__media {
  grid-column: 7 / 13;
  grid-row: 1 / span 3;
  align-self: start;
  aspect-ratio: 1 / 1;
}

/* No note under the tiles, so there is one row less to run beside. */
.band--metrics-media--short .tile-metrics__media {
  grid-row: 1 / span 2;
}

/* Mirrored: the picture takes the left half and the copy with its numbers the
   right, which is how the careers page reads. */
.band--metrics-media-left .tile-metrics__media {
  grid-column: 1 / 7;
}

.band--metrics-media-left .statement--split,
.band--metrics-media-left .tile-metrics,
.band--metrics-media-left .tile-metrics__note {
  grid-column: 7 / 13;
}

.tile-metrics__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- tabs ----------------------------------------------------------------- */

.tabs__head {
  margin-bottom: var(--s7);
}

.tabs {
  grid-column: 1 / 13;
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 9fr);
  gap: var(--col-gap);
  align-items: start;
}

.tabs__list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}

.tabs__tab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) 0;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  text-align: left;
  color: var(--on-ground-dim);
  border-bottom: 1px solid var(--line);
  transition: color 0.2s linear, padding-left 0.35s var(--ease-spring);
}

.tabs__tab .btn__arrow {
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity 0.2s linear, transform 0.35s var(--ease-spring);
}

.tabs__tab:hover {
  color: var(--on-ground);
  padding-left: 0.5rem;
}

.tabs__tab[aria-selected='true'] {
  color: var(--on-ground);
  padding-left: 0.5rem;
}

.tabs__tab[aria-selected='true'] .btn__arrow {
  opacity: 1;
  transform: none;
}

.tabs__panel {
  grid-column: 2;
  grid-row: 1;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
  gap: var(--col-gap);
  align-items: start;
}

.tabs__panel[hidden] {
  display: none;
}

/* One frame for every panel image on every solution page, and each image fills
   it. 3 by 2 is the median ratio of the 23 the client supplied, which are spread
   from 2:3 portrait to 19:10, so it is the ratio that crops the least across the
   whole set. The frame is what keeps the band from changing height as you click
   through, and filling it is what keeps the images the same size as each other. */
.tabs__media {
  position: relative;
  aspect-ratio: 3 / 2;
}

/* Taken out of flow and pinned to the frame, so a tall image can neither stretch
   the box past its ratio nor overflow it. Percentage sizing in flow is not enough
   here: the height the box gets from aspect-ratio is not definite enough for a
   percentage height to resolve against, and the image falls back to its intrinsic
   size. Centred by default, which suits the photography. The panels whose meaning
   sits at the top, interfaces with a heading and the diagram that reads top down,
   carry `focus: top` in the content and override this per image. */
.tabs__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.tabs__copy {
  display: grid;
  gap: var(--s5);
}

.tabs__points {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--line);
}

.tabs__points li {
  padding-block: var(--s3);
  font-size: var(--step-small);
  line-height: 1.6;
  color: var(--on-ground-dim);
  border-bottom: 1px solid var(--line);
}

/* --- faq ------------------------------------------------------------------ */

.faq__head {
  grid-column: 1 / 6;
}

/* The FAQ heading shares a row with the questions, so it sits a step below the
   full statement size to keep the column from turning into a stack of one word
   lines. */
.faq__head .statement__title {
  font-size: clamp(1.5rem, 2.4vw, 2.1rem);
}

.faq__note {
  margin-top: var(--s4);
  font-size: var(--step-small);
}

.faq__head .btn {
  margin-top: var(--s5);
}

.faq {
  grid-column: 6 / 13;
  border-top: 1px solid var(--line);
}

.faq__item {
  border-bottom: 1px solid var(--line);
}

.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding-block: var(--s4);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.015em;
  transition: color 0.2s linear;
}

.faq__q::-webkit-details-marker {
  display: none;
}

.faq__q:hover {
  color: var(--on-ground);
}

/* A plus that becomes a minus, drawn from two rules so it needs no icon file. */
.faq__sign {
  position: relative;
  flex: none;
  width: 13px;
  height: 13px;
}

.faq__sign::before,
.faq__sign::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto;
  height: 1px;
  background: currentColor;
  transition: transform 0.35s var(--ease-spring), opacity 0.2s linear;
}

.faq__sign::after {
  transform: rotate(90deg);
}

.faq__item[open] .faq__sign::after {
  transform: rotate(0deg);
  opacity: 0;
}

.faq__a {
  padding-bottom: var(--s5);
  max-width: 68ch;
  display: grid;
  gap: var(--s3);
  color: var(--on-ground-dim);
}

/* --- pillars -------------------------------------------------------------- */

.pillars__head {
  grid-column: 1 / 9;
}

.pillars__lede {
  margin-top: var(--s5);
  max-width: 66ch;
  display: grid;
  gap: var(--s3);
}

.pillars__note {
  grid-column: 1 / 9;
  margin-top: var(--s5);
}

/* Bullet points beside a statement. */
.statement__points {
  grid-column: 8 / 13;
  align-self: end;
}

.points {
  display: grid;
  border-top: 1px solid var(--line);
}

.points li {
  padding-block: var(--s3);
  font-size: var(--step-small);
  line-height: 1.6;
  color: var(--on-ground-dim);
  border-bottom: 1px solid var(--line);
}

.pillars {
  grid-column: 1 / 13;
  margin-top: var(--s7);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--col-gap);
}

.pillar {
  display: grid;
  gap: var(--s4);
  align-content: start;
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}

.pillar p {
  font-size: var(--step-small);
  line-height: 1.65;
  color: var(--on-ground-dim);
}

/* --- team ----------------------------------------------------------------- */

/* Two to a row, each portrait beside its name rather than above it, which is the
   reading the page this replaces uses. Three across put the photographs at 420px,
   larger than anything else on the page and larger than the team is. */
.team {
  grid-column: 1 / 13;
  margin-top: var(--s7);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s6) var(--col-gap);
}

.member {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr);
  gap: var(--s4);
  align-items: start;
}

/* The one exception to the square rule, asked for by name: leadership portraits
   are circles. Nothing else on the site rounds, so the radius lives here rather
   than in a token that would invite reuse. */
.member__portrait {
  position: relative;
  overflow: hidden;
  border-radius: 50%;
}

.member__portrait img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}

.member__role {
  margin-top: 0.6rem;
  margin-bottom: var(--s3);
}

/* --- insight listings ----------------------------------------------------- */

.insight-filters {
  grid-column: 1 / 13;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3) var(--s4);
  padding-bottom: var(--s5);
  margin-bottom: var(--s6);
  border-bottom: 1px solid var(--line);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.chip {
  padding: 0.45rem 0.9rem;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  border: 1px solid var(--line);
  border-radius: 0;
  color: var(--on-ground-dim);
  transition: background-color 0.2s linear, color 0.2s linear, border-color 0.2s linear;
}

.chip:hover {
  border-color: var(--on-ground);
  color: var(--on-ground);
}

.chip.is-current {
  background: var(--on-ground);
  border-color: var(--on-ground);
  color: var(--ground);
}

.insight-grid {
  grid-column: 1 / 13;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: 0;
}

.news__list--pair {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.process__lede {
  margin-top: var(--s4);
  max-width: 60ch;
}

/* --- job list ------------------------------------------------------------- */

.joblist {
  grid-column: 8 / 13;
  border-top: 1px solid var(--line);
}

/* Half the column at most: this one sits above a short paragraph rather than
   carrying a band, and at full column width it outweighs the roles beside it. */
.joblist__media {
  margin-block: var(--s5);
  max-width: 20rem;
  aspect-ratio: 1 / 1;
}

.joblist__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.joblist__item {
  border-bottom: 1px solid var(--line);
}

.joblist__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding-block: var(--s4);
  transition: padding-left 0.35s var(--ease-spring);
}

.joblist__link:hover {
  padding-left: 0.5rem;
}

.joblist__role {
  display: grid;
  gap: 0.6rem;
}

.joblist__go {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 0;
  transition: background-color 0.2s linear, border-color 0.2s linear;
}

.joblist__go .btn__arrow {
  width: 12px;
  height: 12px;
}

.joblist__link:hover .joblist__go {
  border-color: var(--on-ground);
  background: color-mix(in srgb, var(--on-ground) 8%, transparent);
}

/* --- prose ---------------------------------------------------------------- */

.prose {
  grid-column: 1 / 9;
  max-width: 72ch;
}

.prose > * + * {
  margin-top: var(--s4);
}

.prose h2,
.prose h3,
.prose h4,
.prose h5 {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.22;
  margin-top: var(--s6);
}

.prose h2 {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
}

.prose h3 {
  font-size: clamp(1.3rem, 1.9vw, 1.6rem);
}

/* Body copy uses h4 and h5 as its section headings, matching the source
   articles, so they are sized as headings rather than as small print. */
.prose h4 {
  font-size: clamp(1.2rem, 1.7vw, 1.4rem);
}

.prose h5 {
  font-size: 1.0625rem;
  color: var(--on-ground);
}

.prose p,
.prose li {
  color: var(--on-ground-dim);
  text-wrap: pretty;
}

.prose strong {
  color: var(--on-ground);
  font-weight: 600;
}

.prose a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

.prose ul,
.prose ol {
  display: grid;
  gap: var(--s2);
  padding-left: 1.35rem;
}

.prose ul li {
  list-style: none;
  position: relative;
}

.prose ul li::before {
  content: '';
  position: absolute;
  left: -1.35rem;
  top: 0.72em;
  width: 5px;
  height: 5px;
  background: currentColor;
  opacity: 0.5;
}

.prose ol {
  list-style: decimal;
}

.prose ol li {
  list-style: decimal;
}

.prose blockquote {
  padding-left: var(--s4);
  border-left: 2px solid var(--line);
  font-family: var(--font-display);
  font-size: 1.125rem;
}

.prose__figure {
  margin-block: var(--s6);
}

.prose__figure img {
  width: 100%;
  height: auto;
  background: var(--surface);
}

.prose__figure figcaption {
  margin-top: var(--s3);
  padding-top: var(--s2);
  border-top: 1px solid var(--line);
}

.prose__title {
  margin-bottom: var(--s5);
}

/* --- closer --------------------------------------------------------------- */

.closer {
  grid-column: 1 / 10;
}

.closer__title {
  font-family: var(--font-display);
  font-size: clamp(1.85rem, 3.6vw, 3.15rem);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.032em;
  text-wrap: balance;
}

.closer__body {
  margin-top: var(--s5);
  max-width: 58ch;
  display: grid;
  gap: var(--s3);
}

.closer__actions {
  margin-top: var(--s6);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s5);
}

/* --- embedded form -------------------------------------------------------- */

/* The enquiry band reads as two halves: what we need from you, and the form to
   put it in. A rule down the middle carries that split the full height of the
   band, not just as far as the shorter column reaches, so it stays a division of
   the page rather than a border on one box. */
.band--split {
  border-top: 1px solid var(--line);
}

.band--split::before {
  content: '';
  position: absolute;
  inset-block: 0;
  left: 50%;
  width: 1px;
  background: var(--line);
}

.form-intro {
  grid-column: 1 / 7;
}

/* No measure cap: the opening line runs the width of its half, the way it does
   on the page this replaces. */

/* The points sit well below the opening line rather than following it, which is
   the pause the original page puts between the ask and what you get for it. */
.form-intro .points {
  margin-top: var(--s8);
}

/* 5 by 3 unless the content asks for another shape: the enquiry page crops its
   photograph that way, the contact page runs a square. */
.form-intro__media {
  margin-top: var(--s6);
  aspect-ratio: var(--media-ratio, 5 / 3);
}

.form-intro__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Six columns, mirroring the intro, so the rule down the middle divides the band
   evenly. The padding is the gap between that rule and the first field, which the
   grid gutter alone would not give it. */
.form-embed {
  grid-column: 7 / 13;
  align-self: start;
  padding-left: var(--col-gap);
}

/* HubSpot sets the frame to height zero and only grows it once the iframe posts
   its measured height back. That message does not always arrive: on an origin
   HubSpot does not recognise the frame stays at zero and the form is invisible.
   This min-height is the floor that keeps it on the page regardless, measured
   against the form as it actually renders in this column. Remeasure it if the
   form gains or loses fields. */
.hs-form-frame {
  min-height: 34rem;
}

.hs-form-frame iframe {
  display: block;
  width: 100%;
  border: 0;
}

.form-embed__note {
  margin-top: var(--s4);
  font-size: var(--step-small);
  line-height: 1.6;
  color: var(--on-ground-faint);
}

.form-embed__note a {
  color: var(--on-ground-dim);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-color: var(--line);
  transition: text-decoration-color 0.2s linear, color 0.2s linear;
}

.form-embed__note a:hover {
  color: var(--on-ground);
  text-decoration-color: currentColor;
}

/* Two to a row here, unlike the stacked column on the enquiry page: this list is
   three short entries under a picture rather than the whole right hand side. */
.contact-grid--inline {
  grid-column: auto;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s4) var(--col-gap);
  margin-top: var(--s4);
}

/* The application form closing a job page. One column rather than the enquiry
   page's split: there is nothing to put beside it, and a form this narrow is
   easier to fill than one stretched across the body measure. */
.job-apply__inner {
  grid-column: 2 / 9;
}

.job-apply .hs-form-frame {
  /* Five fields with the name pair sharing a row, one row more than the enquiry
     form. Measured in this column, same as the other floor. */
  min-height: 36rem;
  margin-top: var(--s5);
}

.job-apply__back {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  border-top: 1px solid var(--line);
  padding-top: var(--s3);
}

/* --- contact grid --------------------------------------------------------- */

/* The column the address sits in, with or without a picture over it. */
.contact-side {
  grid-column: 7 / 13;
  align-self: start;
}

.contact-side__media {
  margin-bottom: var(--s5);
  aspect-ratio: var(--media-ratio, 5 / 3);
}

.contact-side__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.contact-side__title {
  margin-bottom: var(--s4);
}

.contact-side .contact-grid {
  grid-column: auto;
}

.contact-grid {
  grid-column: 8 / 13;
  display: grid;
  gap: var(--s5);
  align-content: start;
}

.contact-grid__item {
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}

.contact-grid dd {
  margin-top: 0.6rem;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.015em;
}

/* ==========================================================================
   Article and job detail
   ========================================================================== */

.article,
.job {
  padding-top: calc(var(--head-h) + clamp(2.5rem, 6vw, 5rem));
  padding-bottom: var(--band-y);
}

.article__top,
.job__top {
  padding-bottom: var(--s7);
}

.article__head,
.job__head {
  grid-column: 1 / 9;
}

.job__head .btn {
  margin-top: var(--s6);
}

.article__kicker {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: var(--s5);
}

.article__kicker a:hover {
  color: var(--on-ground);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.article__title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.2vw, 3.75rem);
  font-weight: 400;
  line-height: 1.04;
  letter-spacing: -0.036em;
  text-wrap: balance;
}

.article__lede {
  margin-top: var(--s5);
  max-width: 58ch;
}

.article__facts {
  grid-column: 10 / 13;
  align-self: end;
  display: grid;
  gap: var(--s4);
}

.article__fact {
  padding-top: var(--s2);
  border-top: 1px solid var(--line);
}

.article__fact dd {
  margin-top: 0.4rem;
  font-family: var(--font-display);
  font-size: var(--step-small);
  font-weight: 500;
  letter-spacing: -0.01em;
}

.article__cover {
  position: relative;
  margin-bottom: var(--s8);
}

.article__cover img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.article__body {
  grid-column: 2 / 10;
}

/* Current section in the header nav. */
.nav__trigger.is-current {
  color: var(--on-ground);
}

.nav__menu a[aria-current='page'] {
  color: var(--ink);
  background: var(--mist);
}

/* ==========================================================================
   Motion
   ========================================================================== */

.reveal {
  opacity: 0;
  transform: translateY(18px);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.7s var(--ease-out), transform 0.9s var(--ease-spring);
  transition-delay: var(--reveal-delay, 0ms);
}

.hero__line,
.hero__media,
.hero__support,
.hero__block {
  --enter-delay: 0ms;
  opacity: 0;
  animation: enter 1s var(--ease-spring) forwards;
  animation-delay: var(--enter-delay);
}

.hero__media {
  animation-name: enter-media;
}

@keyframes enter {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes enter-media {
  from {
    opacity: 0;
    transform: scale(0.975);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.001ms !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  .hero__line,
  .hero__media,
  .hero__support,
  .hero__block {
    opacity: 1;
  }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1180px) {
  .hero__line {
    text-wrap: balance;
  }

  .hero__title--a,
  .hero__title--b {
    grid-column: 1 / 11;
  }

  .hero__support {
    grid-column: 1 / 9;
    grid-row: 4;
  }

  .statement,
  .metrics__head,
  .process__head,
  .news__head {
    grid-column: 1 / 13;
  }

  .deflist {
    grid-column: 1 / 8;
  }

  .deflist__media {
    grid-column: 8 / 13;
  }

  /* Full width from here down, so it has nothing to keep clear of. */
  .quote-card {
    grid-column: 1 / 13;
    margin-right: 0;
  }

  .pull {
    grid-column: 1 / 13;
  }

  .news__all {
    grid-column: 1 / 13;
    justify-self: start;
    margin-top: var(--s3);
  }

  .site-foot__main {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .foot-brand {
    grid-column: 1 / 3;
  }

  /* Generated blocks */
  .statement,
  .statement--split,
  .showcase__head,
  .pillars__head,
  .closer,
  .prose,
  .article__body,
  .article__head,
  .job-apply__inner,
  .job__head {
    grid-column: 1 / 13;
  }

  .page-hero__head,
  .page-hero__aside {
    grid-column: 1 / 8;
  }

  .page-hero__media {
    grid-column: 8 / 13;
  }

  .tile-metrics,
  .contact-grid,
  .contact-side,
  .statement__media,
  .joblist,
  .statement__points,
  .pillars__note {
    grid-column: 1 / 13;
    margin-top: var(--s6);
  }

  .tile-metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .band--metrics-media .tile-metrics,
  .band--metrics-media .tile-metrics__note,
  .tile-metrics__media {
    grid-column: 1 / 13;
  }

  /* Stacked, so the image no longer runs beside anything. */
  .tile-metrics__media,
  .band--metrics-media--short .tile-metrics__media {
    grid-row: auto;
    margin-top: var(--s6);
  }

  /* Stacked, so the rule down the middle has nothing left to divide. */
  .band--split::before {
    display: none;
  }

  .form-intro,
  .form-embed {
    grid-column: 1 / 13;
  }

  .form-embed {
    margin-top: var(--s6);
    padding-left: 0;
  }

  .tile-metrics__note {
    grid-column: 1 / 13;
  }

  .faq__head {
    grid-column: 1 / 13;
    margin-bottom: var(--s6);
  }

  .faq {
    grid-column: 1 / 13;
  }

  .article__facts {
    grid-column: 1 / 13;
    margin-top: var(--s6);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .insight-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 900px) {
  .tabs {
    grid-template-columns: minmax(0, 1fr);
  }

  .tabs__list {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.5rem;
    border-top: 0;
  }

  .tabs__tab {
    width: auto;
    padding: 0.55rem 0.95rem;
    border: 1px solid var(--line);
    border-radius: 0;
  }

  .tabs__tab:hover,
  .tabs__tab[aria-selected='true'] {
    padding-left: 0.95rem;
  }

  .tabs__tab[aria-selected='true'] {
    background: var(--on-ground);
    border-color: var(--on-ground);
    color: var(--ground);
  }

  .tabs__tab .btn__arrow {
    display: none;
  }

  .tabs__panel {
    grid-column: 1;
    grid-row: 2;
    grid-template-columns: minmax(0, 1fr);
  }

  .page-hero__head,
  .page-hero__aside,
  .page-hero__media {
    grid-column: 1 / 13;
    grid-row: auto;
  }

  .page-hero__media {
    min-height: 0;
    aspect-ratio: 3 / 2;
    margin-top: var(--s5);
  }

  .page-hero__aside {
    padding-top: var(--s5);
  }

  .pillars {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .showcase__media img {
    aspect-ratio: 16 / 9;
  }
}

@media (max-width: 640px) {
  .pillars,
  .insight-grid,
  .news__list--pair,
  .tile-metrics,
  .article__facts {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 900px) {
  :root {
    --head-h: 64px;
  }

  .nav,
  .head__cta {
    display: none;
  }

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

  /* The nav is a sheet from here down, so it no longer holds the middle of the
     bar open. The language switcher takes over pushing the right hand group out,
     and sits beside the toggle. */
  .nav__item--lang {
    margin-left: auto;
  }

  /* Mobile sheet */
  .nav.is-open {
    display: block;
    position: fixed;
    inset: var(--head-h) 0 0;
    margin: 0;
    padding: var(--s4) var(--gutter) var(--s7);
    overflow-y: auto;
    background: var(--paper);
    color: var(--ink);
  }

  .nav.is-open .nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .nav.is-open .nav__item {
    border-bottom: 1px solid var(--rule-soft);
  }

  .nav.is-open .nav__trigger,
  .nav.is-open .nav__top {
    width: 100%;
    justify-content: space-between;
    padding: 1.1rem 0;
    font-size: 1.125rem;
    border-radius: 0;
  }

  /* In the sheet the chevron carries the open state, a filled row is too loud. */
  .nav.is-open .nav__trigger:hover,
  .nav.is-open .nav__top:hover,
  .nav.is-open .nav__item[data-open='true'] .nav__trigger {
    background: none;
  }

  .nav.is-open .nav__menu {
    position: static;
    min-width: 0;
    padding: 0 0 var(--s3);
    opacity: 1;
    transform: none;
    pointer-events: auto;
    display: none;
  }

  .nav.is-open .nav__menu > ul {
    padding: 0;
    border: 0;
    box-shadow: none;
    background: none;
  }

  .nav.is-open .nav__item[data-open='true'] .nav__menu {
    display: block;
  }

  .nav.is-open .nav__menu a {
    padding: 0.6rem 0;
    font-size: 1rem;
  }

  .nav.is-open .nav__menu a:hover {
    background: none;
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  .nav.is-open .nav__foot {
    display: grid;
    gap: var(--s5);
    justify-items: start;
    padding-top: var(--s5);
  }

  .nav__contact dd {
    margin-bottom: var(--s3);
    font-size: var(--step-small);
  }

  .nav__contact dd:last-child {
    margin-bottom: 0;
  }

  .nav__contact dd a:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  .hero__title--a,
  .hero__title--b,
  .hero__support {
    grid-column: 1 / 13;
  }

  .deflist,
  .deflist__media {
    grid-column: 1 / 13;
  }

  /* Stacked, so the image no longer sits beside the copy and the padding that
     kept them apart would only skew the gutter. */
  .deflist {
    padding-right: 0;
  }

  .deflist__media {
    grid-row: auto;
    min-height: 0;
  }

  .deflist__media img {
    height: auto;
    aspect-ratio: 3 / 2;
  }

  .quote-card {
    grid-template-columns: minmax(0, 1fr);
  }

  .metric {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  .process__list,
  .news__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .site-foot__base {
    grid-template-columns: minmax(0, 1fr);
    text-align: left;
  }

  .site-foot__out {
    justify-self: start;
    text-align: left;
  }
}

@media (max-width: 480px) {
  /* First and last name drop onto their own rows below this width, adding a row
     to both forms. The job selector is repeated rather than left to the rule
     above, which is less specific than .job-apply .hs-form-frame and so would
     lose to it here and crop the application form. */
  .hs-form-frame {
    min-height: 40rem;
  }

  .job-apply .hs-form-frame {
    min-height: 40rem;
  }
}

@media (max-width: 640px) {
  .cap__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s2);
  }

  .process__list,
  .news__list {
    grid-template-columns: minmax(0, 1fr);
  }

  .team {
    grid-template-columns: minmax(0, 1fr);
  }

  .process__list--rows .stage {
    grid-template-columns: 3rem minmax(0, 1fr);
  }

  .process__list--rows .stage > :last-child {
    grid-column: 2;
  }

  .member {
    grid-template-columns: 8rem minmax(0, 1fr);
  }

  .site-foot__main {
    grid-template-columns: minmax(0, 1fr);
  }

  .foot-brand {
    grid-column: 1;
  }
}
