/* =========================================================================
   Infrastructure in Motion - home page, second direction.
   Full-bleed photographic hero, fixed navy bar, counted figures, accordion,
   horizontal mandate rail. Palette and type are the brand's; the structure
   shares nothing with the first build.
   Radius is 0 everywhere. Sage is the only accent, in both themes.
   ========================================================================= */

/* ---------- Fonts (self-hosted, extracted from the brand reference) ------- */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('assets/fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 500;
  font-stretch: 100%;
  font-display: swap;
  src: url('assets/fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('assets/fonts/outfit-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('assets/fonts/outfit-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---------- Tokens ------------------------------------------------------- */

:root {
  /* Brand palette, unchanged from the reference. */
  --paper:       #f2efe8;
  --paper-band:  #e8e4d9;
  --paper-warm:  #f6f4ee;
  --rule-light:  #d3cec1;
  --navy:        #101c33;
  --navy-deep:   #0d1a33;
  --slate-light: #b9c4d6;
  /* One accent hue, three tones. #437f45 is the accent as given; as text it
     measures 4.19:1 on cream and 3.60:1 on navy, both under the 4.5:1 AA
     floor, so text uses the tuned siblings. All three are hue 122. */
  --green:       #437f45;   /* fills, rules, borders, map, focus  */
  --green-text:  #407942;   /* accent as text on cream  4.54:1    */
  --green-lift:  #4c914f;   /* accent as text on navy   4.51:1    */
  --green-tint:  #3b6f3d;   /* accent as text on tint   4.69:1    */
  /* The hover accent on the track-record ledger. Hue 215, which puts it in the
     brand navy's own family (218) rather than introducing a fourth colour. */
  --blue:        #2a5ea8;   /* hover accent on tint     5.06:1    */
  --blue-lift:   #89b2e6;   /* the same on dark tint    7.49:1    */
  --ink:         #4f4b44;

  /* Semantic tokens: dark mode is a swap of this block and nothing else. */
  --bg:        var(--paper);
  --band:      var(--paper-band);
  --surface:   var(--paper);
  --heading:   var(--navy);
  --text:      var(--ink);
  --link:      var(--green-text);
  --rule:      var(--rule-light);
  --tint:      var(--paper-band);
  --shadow:    rgba(13, 26, 51, 0.28);

  /* Navy furniture: the bar, the offices band, the closing block, the footer.
     Identical in both themes so the brand reads the same either way. */
  --dark:        var(--navy);
  --dark-deep:   var(--navy-deep);
  --on-dark:     var(--paper-warm);
  --on-dark-dim: var(--slate-light);
  --rule-dark:   rgba(242, 239, 232, 0.16);

  --accent:    var(--green);
  --on-accent: #ffffff;

  /* The mandate ledger runs on a tinted card, where --green-text drops to
     4.10:1 and misses AA. It gets its own pair: resting, and on hover. */
  --ledger:       var(--green-tint);
  --ledger-hover: var(--blue);

  /* Placeholder hatch, from the reference's own slot treatment. */
  --hatch-a: #e0dbcf;
  --hatch-b: #d6d0c2;

  --nav-h:  clamp(56px, 8dvh, 68px);
  --gutter: clamp(20px, 5vw, 72px);
  --ease:   cubic-bezier(0.16, 1, 0.3, 1);
  --page:   1500px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:      var(--navy-deep);
    --band:    var(--navy);
    --surface: var(--navy);
    --heading: var(--paper-warm);
    --text:    var(--slate-light);
    /* Forest green fails contrast on navy, so links take the sage accent. */
    --link:    var(--green-lift);
    --ledger:       var(--green-lift);
    --ledger-hover: var(--blue-lift);
    --rule:    rgba(242, 239, 232, 0.16);
    --tint:    rgba(242, 239, 232, 0.05);
    --shadow:  rgba(0, 0, 0, 0.55);
    --hatch-a: rgba(242, 239, 232, 0.10);
    --hatch-b: rgba(242, 239, 232, 0.04);
  }
}

/* ---------- Base --------------------------------------------------------- */

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Archivo', Helvetica, Arial, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.68;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  margin: 0;
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-weight: 550;
  color: var(--heading);
  letter-spacing: -0.02em;
}

p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }

a {
  color: var(--link);
  text-decoration: none;
  transition: color 0.25s var(--ease);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

section[id], main[id] { scroll-margin-top: calc(var(--nav-h) + 12px); }

.skip-link {
  position: fixed;
  left: -9999px;
  top: 0;
  z-index: 60;
  padding: 12px 18px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.skip-link:focus { left: 0; }

/* Mono micro-type. */
.eyebrow, .meta, .stat__label, .nav__links a, .btn, .more,
.person__role, .cta__email, .foot__nav a {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
}

.eyebrow {
  /* Accent as text on navy needs the lifted tone to clear AA. */
  color: var(--green-lift);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
}

/* ---------- Buttons ----------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 15px 26px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  white-space: nowrap;
  transition: background-color 0.25s var(--ease), transform 0.15s var(--ease);
}
.btn:hover { background: var(--paper-warm); color: var(--navy-deep); }
.btn:active { transform: translateY(1px); }
.btn--sm { padding: 11px 18px; font-size: 0.9375rem; }
.btn--lg { padding: 18px 34px; }

/* ---------- Fixed bar --------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  /* Solid navy at all times. It used to fade in over a photographic hero;
     with the hero now typographic there is nothing to fade over, and a
     half-transparent bar just read as unfinished. */
  background: var(--dark-deep);
  box-shadow: 0 1px 0 var(--rule-dark);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 44px);
  max-width: var(--page);
  height: var(--nav-h);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.nav__brand { display: block; flex: none; }
.nav__brand img { width: clamp(116px, 13vw, 150px); }

.nav__links {
  display: flex;
  gap: clamp(14px, 2.4vw, 34px);
  margin-left: auto;
}
.nav__links a {
  color: var(--on-dark);
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  white-space: nowrap;
}
.nav__links a:hover { color: var(--accent); }

.nav__toggle { display: none; }

/* Scroll position, drawn as a sage hairline along the base of the bar. */
.nav__progress {
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* ---------- Hero -------------------------------------------------------- */

.hero {
  position: relative;
  background: var(--dark-deep);
  display: flex;
  align-items: flex-end;
  /* Deliberately short of a full viewport so the figures band below always
     peeks above the fold and the page reads as continuing. The hero still
     grows past this if its own content needs more room. */
  min-height: min(80dvh, 820px);
  padding: calc(var(--nav-h) + clamp(48px, 12dvh, 130px)) var(--gutter) clamp(40px, 9dvh, 96px);
  overflow: hidden;
  background: var(--dark-deep);
}

.hero__body {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--page);
  margin: 0 auto;
}

.hero__headline {
  margin: clamp(16px, 3dvh, 26px) 0 0;
  color: var(--on-dark);
  font-size: min(clamp(2.5rem, 6.4vw, 5.25rem), 11dvh);
  line-height: 1.04;
}
/* Each line is masked so the words can rise into view on load. */
.hero__headline .line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.06em;
}
.hero__headline .line > span { display: block; }

.hero__sub {
  max-width: 44ch;
  margin-top: clamp(14px, 2.4dvh, 24px);
  color: var(--on-dark-dim);
  font-size: clamp(1.125rem, 1.26vw, 1.3125rem);
}

.hero .btn { margin-top: clamp(20px, 3.4dvh, 34px); }

/* ---------- Figures ----------------------------------------------------- */

.stats {
  padding: clamp(40px, 6vw, 68px) var(--gutter);
  background: var(--band);
  border-bottom: 1px solid var(--rule);
}

.stats__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 40px);
  max-width: var(--page);
  margin: 0 auto;
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-left: 18px;
  border-left: 2px solid var(--accent);
}

.stat__figure {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: clamp(2rem, 3.4vw, 3.25rem);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
  color: var(--heading);
}

.stat__label {
  color: var(--text);
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
}

/* ---------- Generic section -------------------------------------------- */

.section {
  max-width: var(--page);
  margin: 0 auto;
  padding: clamp(56px, 9vw, 116px) var(--gutter);
}

.section__title {
  font-size: clamp(1.875rem, 4vw, 3.25rem);
  line-height: 1.1;
}

.section__head { margin-bottom: clamp(28px, 4vw, 52px); }

/* A standfirst under a section title. It had no rule of its own, so it sat
   flush against the 2px accent rule under the heading, and ran the full width
   of the page: a 1300px measure at 17px is roughly twice a readable line. */
.lede {
  /* A standfirst under a section title, so it sits between the title and the
     body text rather than at body size. It had no font-size at all, which is
     why it read as an ordinary paragraph under a 52px heading.

     The measure comes down as the size goes up: 62ch at 26px is an 830px
     line, which is far too long to track. */
  max-width: 46ch;
  margin: clamp(14px, 1.6vw, 20px) 0 0;
  font-size: clamp(1.25rem, 1.8vw, 1.625rem);
  line-height: 1.38;
}

.acc { border-bottom: 1px solid var(--rule); }

/* ---------- Mandate rail ------------------------------------------------ */

.work {
  padding: clamp(56px, 9vw, 116px) 0;
  background: var(--band);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.work__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  max-width: var(--page);
  margin: 0 auto clamp(28px, 4vw, 48px);
  padding: 0 var(--gutter);
}

.work__controls { display: flex; gap: 10px; flex: none; }

/* Set by main.js when the rail has nothing to scroll. */
.work.is-static .work__controls { display: none; }

.rail-btn {
  width: 46px;
  height: 46px;
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--heading);
  font-size: 1.0625rem;
  cursor: pointer;
  transition: background-color 0.25s var(--ease), border-color 0.25s var(--ease),
    color 0.25s var(--ease), opacity 0.25s var(--ease);
}
.rail-btn:hover:not(:disabled) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.rail-btn:disabled { opacity: 0.3; cursor: default; }

/* Bleeds off the right edge so the rail reads as continuing. */
.work__scroller {
  display: flex;
  gap: clamp(16px, 2vw, 28px);
  padding: 4px var(--gutter) 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--gutter);
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
}

.proj {
  flex: 0 0 clamp(258px, 74vw, 430px);
  scroll-snap-align: start;
}

.proj__title {
  font-size: clamp(1.375rem, 1.87vw, 1.8125rem);
  line-height: 1.2;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin-top: 10px;
  color: var(--text);
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
}

.more {
  max-width: var(--page);
  margin: clamp(20px, 3vw, 34px) auto 0;
  padding: 0 var(--gutter);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}
.more a { padding-bottom: 4px; border-bottom: 1px solid var(--accent); }

.social {
  margin-top: clamp(22px, 2.6vw, 30px);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}
.social a { color: var(--link); padding-bottom: 4px; border-bottom: 1px solid var(--accent); }
.social a:hover { color: var(--accent); }

/* ---------- Principals -------------------------------------------------- */

.people {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(30px, 3vw, 44px);
}

.person__name {
  font-size: clamp(1.25rem, 1.65vw, 1.5rem);
  line-height: 1.25;
}

.person__role {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 16px;
  margin: 8px 0 10px;
  color: var(--link);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.13em;
}

.person__bio { font-size: clamp(1.125rem, 1.35vw, 1.25rem); }

/* ---------- Offices band ------------------------------------------------ */

.offices {
  padding: clamp(56px, 9vw, 110px) var(--gutter);
  background: var(--dark);
  border-top: 1px solid var(--rule-dark);
}

.offices__title {
  max-width: var(--page);
  margin: 0 auto clamp(30px, 4vw, 54px);
  color: var(--on-dark);
  font-size: clamp(1.875rem, 4vw, 3.25rem);
  line-height: 1.1;
}

.offices__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0;
  max-width: var(--page);
  margin: 0 auto;
  border-top: 1px solid var(--rule-dark);
}

.office {
  padding: clamp(18px, 2.4vw, 30px) 0;
  border-bottom: 1px solid var(--rule-dark);
}
.office span {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: clamp(1.5rem, 2.6vw, 2.25rem);
  line-height: 1.15;
  color: var(--on-dark);
  transition: color 0.3s var(--ease), transform 0.3s var(--ease);
  display: inline-block;
}
.office:hover span { color: var(--accent); transform: translateX(8px); }

/* ---------- Closing block ----------------------------------------------- */

.cta {
  padding: clamp(64px, 11vw, 140px) var(--gutter);
  background: var(--dark-deep);
  border-top: 1px solid var(--rule-dark);
}

.cta__title {
  max-width: 22ch;
  color: var(--on-dark);
  font-size: clamp(2.125rem, 5.2vw, 4.25rem);
  line-height: 1.06;
}

.cta__body {
  max-width: 52ch;
  margin-top: clamp(18px, 2.4vw, 26px);
  color: var(--on-dark-dim);
  font-size: clamp(1.125rem, 1.26vw, 1.25rem);
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 24px;
  margin-top: clamp(26px, 3.4vw, 40px);
}

.cta__email {
  color: var(--accent);
  font-size: 1.0625rem;
  letter-spacing: 0.04em;
  /* advisory@infrastructureinmotion.com is one unbreakable token 36 characters
     long. At 16px it is wider than a 320px phone, so it has to be allowed to
     break rather than push the page sideways. */
  overflow-wrap: anywhere;
}
.cta__email:hover { color: var(--paper-warm); }

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

.foot {
  padding: clamp(30px, 4vw, 46px) var(--gutter);
  background: var(--dark-deep);
  border-top: 1px solid var(--rule-dark);
}

.foot__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  max-width: var(--page);
  margin: 0 auto;
}

.foot__brand { display: block; }
.foot__logo { width: 132px; }

.foot__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
}
.foot__nav a {
  color: var(--on-dark-dim);
  font-size: 0.9375rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}
.foot__nav a:hover { color: var(--accent); }

/* ---------- Responsive -------------------------------------------------- */

/* The bar collapses to a toggle here. The number tracks the type: at the
   sizes below, brand + four links + the CTA stop fitting on one line at
   around 1030px, which is where the button started hanging off the edge. */
@media (max-width: 1039px) {
  /* Bar collapses to a toggle; the links become a navy drop panel. */
  .nav__toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    margin-left: auto;
    padding: 0 10px;
    background: transparent;
    border: 0;
    cursor: pointer;
  }
  .nav__toggle span {
    display: block;
    height: 1.5px;
    background: var(--on-dark);
    transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
  }
  .nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
  .nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

  .nav__links {
    position: absolute;
    inset: var(--nav-h) 0 auto 0;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 0 var(--gutter);
    max-height: 0;
    overflow: hidden;
    background: var(--dark-deep);
    transition: max-height 0.4s var(--ease);
  }
  .nav__links.is-open { max-height: 60dvh; }
  .nav__links a {
    padding: 17px 0;
    border-bottom: 1px solid var(--rule-dark);
  }

  .nav__cta { display: none; }
}

@media (min-width: 600px) {
  .people { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 768px) {
  .stats__grid { grid-template-columns: repeat(5, 1fr); }
  .offices__list { grid-template-columns: repeat(4, 1fr); }
  .office { padding-right: 20px; }
}

@media (min-width: 1024px) {
  .people { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Motion ------------------------------------------------------ */

@media (prefers-reduced-motion: no-preference) {
  /* Hero: the headline rises out of its mask, then the supporting copy. */
  .hero__headline .line > span {
    animation: riseIn 1s var(--ease) both;
  }
  .hero__headline .line:nth-child(1) > span { animation-delay: 0.12s; }
  .hero__headline .line:nth-child(2) > span { animation-delay: 0.24s; }
  .hero .eyebrow  { animation: fadeIn 0.8s var(--ease) both; }
  .hero__sub      { animation: fadeUp 0.9s var(--ease) 0.42s both; }
  .hero .btn      { animation: fadeUp 0.9s var(--ease) 0.54s both; }

  /* Scroll reveal, staggered per item. */
  .js .reveal {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
    transition-delay: calc(var(--i, 0) * 70ms);
  }
  .js .reveal.is-visible { opacity: 1; transform: none; }
}

/* Scroll position bar. Pure CSS where the browser supports it, so no
   scroll listener is needed; elsewhere the bar simply stays hidden. */
@supports (animation-timeline: scroll(root block)) {
  @media (prefers-reduced-motion: no-preference) {
    .nav__progress {
      animation: progress linear both;
      animation-timeline: scroll(root block);
    }
  }
}

@keyframes riseIn  { from { transform: translateY(105%); } to { transform: none; } }
@keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeUp  { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; }
  .work__scroller { scroll-behavior: auto; }
}

/* =========================================================================
   Mandates page (mandates.html). Reuses the tokens, bar and footer above.
   ========================================================================= */

.page { padding-top: var(--nav-h); }

/* ---------- Page head --------------------------------------------------- */

.phead {
  padding: clamp(40px, 7vw, 84px) var(--gutter) clamp(28px, 4vw, 44px);
  background: var(--dark-deep);
}

.phead__inner {
  max-width: var(--page);
  margin: 0 auto;
}

.phead .eyebrow { margin-bottom: 16px; }

.phead__title {
  max-width: 26ch;
  color: var(--on-dark);
  font-size: clamp(1.875rem, 4.4vw, 3.5rem);
  line-height: 1.08;
}

.phead__sub {
  max-width: 62ch;
  margin-top: 18px;
  color: var(--on-dark-dim);
  font-size: clamp(1.125rem, 1.32vw, 1.3125rem);
}

/* ---------- Filter bar -------------------------------------------------- */

.filters {
  position: sticky;
  top: var(--nav-h);
  z-index: 40;
  padding: clamp(18px, 2.4vw, 26px) var(--gutter);
  background: var(--band);
  border-bottom: 1px solid var(--rule);
}

.filters__inner {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: var(--page);
  margin: 0 auto;
}

.filters__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
}

.field__label {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text);
}

.field__input {
  width: 100%;
  padding: 11px 13px;
  background: var(--bg);
  color: var(--heading);
  font-family: 'Archivo', Helvetica, sans-serif;
  font-size: 1.0625rem;
  border: 1px solid var(--rule);
  border-radius: 0;
  transition: border-color 0.25s var(--ease);
}
.field__input:hover { border-color: var(--accent); }
.field__input:focus-visible { border-color: var(--accent); }
/* Placeholder must clear AA against the input fill, not sit at browser grey. */
.field__input::placeholder { color: var(--text); opacity: 0.75; }

select.field__input { cursor: pointer; }

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  background: transparent;
  color: var(--heading);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  border: 1px solid var(--rule);
  cursor: pointer;
  transition: background-color 0.22s var(--ease), border-color 0.22s var(--ease),
    color 0.22s var(--ease);
}
.chip:hover { border-color: var(--accent); }
.chip.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.chip__n {
  font-size: 0.875rem;
  opacity: 0.7;
}

.filters__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 20px;
}

.count {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--heading);
}

/* Summary on the left, controls on the right. */
.filters__acts {
  display: flex;
  align-items: center;
  gap: 8px 22px;
  margin-left: auto;
}

/* The single control that switches the whole result set between the card grid
   and full-width detail rows. Reads as a control rather than a link: it has a
   box, and it fills in when it is on. */
.viewbtn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 9px 16px;
  background: none;
  color: var(--link);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  border: 1px solid var(--accent);
  cursor: pointer;
  transition: background-color 0.22s var(--ease), color 0.22s var(--ease),
              border-color 0.22s var(--ease);
}
.viewbtn:hover {
  background: var(--accent);
  color: var(--on-accent);
}
.viewbtn[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
}
.viewbtn[aria-pressed="true"]:hover {
  background: var(--blue);
  border-color: var(--blue);
}

/* Plus that becomes a minus, the same figure the cards used to carry. */
.viewbtn__icon {
  position: relative;
  flex: none;
  width: 13px;
  height: 13px;
}
.viewbtn__icon::before,
.viewbtn__icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transition: transform 0.3s var(--ease);
}
.viewbtn__icon::after { transform: rotate(90deg); }
.viewbtn[aria-pressed="true"] .viewbtn__icon::after { transform: rotate(0deg); }

.linkbtn {
  padding: 0 0 3px;
  background: none;
  color: var(--link);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  border: 0;
  border-bottom: 1px solid var(--accent);
  cursor: pointer;
}

.sector-note {
  max-width: 80ch;
  color: var(--text);
  font-size: 1.0625rem;
  line-height: 1.6;
}

/* ---------- Results ----------------------------------------------------- */

.results {
  max-width: var(--page);
  margin: 0 auto;
  padding: clamp(32px, 5vw, 64px) var(--gutter) clamp(48px, 7vw, 88px);
}

.results__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}

/* Detail view: one full-page-width row per mandate at every width. */
.results__grid.is-detail { grid-template-columns: 1fr; }

/* Cells share hairlines, so the grid reads as one ledger rather than 40 boxes.

   Every card rests in what used to be its hover state: tinted, with the rule
   drawn across the top and the accent on the type. Hover no longer changes the
   shape of the card, only its colour, and it does that by reassigning one
   custom property that the rule, the sector, the title, the toggle and the
   icon all read from. Green at rest, blue under the cursor. */
.mandate {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(20px, 2.4vw, 28px);
  background: var(--tint);
  --card-accent: var(--ledger);
  transition: background-color 0.3s var(--ease);
}
.mandate:hover,
.mandate:focus-within { --card-accent: var(--ledger-hover); }

/* Drawn on every card now, not just the hovered one. */
.mandate::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--card-accent);
  transition: background-color 0.25s var(--ease);
}

.mandate__title {
  color: var(--card-accent);
  transition: color 0.25s var(--ease);
}

.mandate__sector {
  margin-bottom: 10px;
  color: var(--card-accent);
  transition: color 0.25s var(--ease);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.mandate__title {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-weight: 550;
  font-size: clamp(1.3125rem, 1.76vw, 1.625rem);
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.mandate__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 14px;
  margin: 16px 0 0;
}
.mandate__facts dt {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--text);
  padding-top: 3px;
}
.mandate__facts dd {
  margin: 0;
  font-size: 1.0625rem;
  color: var(--heading);
}

/* Holds the card's own stack together once .mandate itself becomes a grid. */
.mandate__main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.mandate__panel { padding-top: 14px; }
.mandate__panel p {
  max-width: 62ch;
  font-size: 1.0625rem;
  line-height: 1.65;
}

/* The four mandates with no written description. Inside the panel now, so
   detail view has one shape rather than two. */
.mandate__nodetail {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text);
}

.results__empty {
  padding: clamp(36px, 6vw, 64px) 0;
  color: var(--text);
  font-size: 1.0625rem;
  text-align: center;
}

.results .cta__title { max-width: none; }

/* ---------- Responsive -------------------------------------------------- */

@media (min-width: 700px) {
  .filters__row { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto; }
  .results__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1100px) {
  .results__grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* Wide enough to read two columns: the row splits, so the mandate keeps a
     card-width column on the left and the description takes the other two.
     Narrower than this the row stays full width but simply stacks. */
  .results__grid.is-detail .mandate {
    display: grid;
    grid-template-columns: 1fr 2fr;
    align-items: start;
  }
  .results__grid.is-detail .mandate__panel {
    margin-left: clamp(28px, 3.2vw, 56px);
    padding: 2px 0 0 clamp(28px, 3.2vw, 56px);
    border-left: 1px solid var(--rule);
  }
  /* Two thirds of a 1500px page is a very long line otherwise. */
  .results__grid.is-detail .mandate__panel p { max-width: 78ch; }
}

@media (prefers-reduced-motion: no-preference) {
  .mandate__panel { animation: fadeUp 0.4s var(--ease) both; }
}

/* =========================================================================
   Life pass: scroll-linked depth, rules that draw themselves in, and
   hover states with some weight to them. Every item below is motivated and
   every item collapses under prefers-reduced-motion.
   ========================================================================= */

/* Screen-reader-only, for the filter status text that the summary shows visually. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Section titles: a sage rule draws in under each one ---------- */

.section__title::after,
.offices__title::after {
  content: '';
  display: block;
  width: clamp(44px, 6vw, 84px);
  height: 2px;
  margin-top: 0.55em;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* ---------- Mandate rail: sage bar tracks how far the rail is scrolled --- */

.work__progress {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.work__progress span {
  display: block;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.work.is-static .work__progress { display: none; }
.person__name { transition: color 0.25s var(--ease); }
.person:hover .person__name { color: var(--link); }

/* ---------- Offices: the row reacts as a set -------------------------- */

.office { transition: background-color 0.3s var(--ease); }
.office:hover { background: rgba(242, 239, 232, 0.04); }

/* ---------- Mandates page: live summary figures ------------------------ */

.summary {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 34px;
}

.summary__item {
  display: flex;
  align-items: baseline;
  gap: 9px;
}

.summary__n {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: clamp(1.5rem, 2.4vw, 2.125rem);
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--heading);
}

.summary__l {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--text);
}

/* ---------- Motion ----------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  /* Titles: the rule arrives with the heading it belongs to. */
  .is-visible .section__title::after,
  .section__title.is-visible::after,
  .offices__title.is-visible::after {
    transition: transform 0.75s var(--ease) 0.12s;
    transform: scaleX(1);
  }
}

/* Under reduced motion the rules are simply present, not drawn. */
@media (prefers-reduced-motion: reduce) {
  .section__title::after,
  .offices__title::after { transform: scaleX(1); }
}

/* Scroll-linked depth on the hero, and the rail's own progress bar. Pure CSS,
   so no scroll listener; browsers without support just skip both. */

/* The rail bar is a SIBLING of the scroller, so the timeline name has to be
   published into a shared scope with timeline-scope or it never resolves, and
   an unresolved timeline would leave the bar stuck at its end state (a
   permanently full sage rule). Both features are required, hence the combined
   query: without them the bar simply stays at scaleX(0) and never shows. */
@supports (animation-timeline: --rail) and (timeline-scope: --rail) {
  @media (prefers-reduced-motion: no-preference) {
    .work { timeline-scope: --rail; }
    .work__scroller {
      scroll-timeline-name: --rail;
      scroll-timeline-axis: inline;
    }
    .work__progress span {
      animation: progress linear both;
      animation-timeline: --rail;
    }
  }
}


/* =========================================================================
   Typographic pass. The site ships before photography exists, so the hero,
   the practice list, the mandate rail and the principals all had to work with
   type alone rather than sit around empty image slots.
   ========================================================================= */

/* ---------- Hero without a photograph ----------------------------------- */

.hero--type {
  /* No image to hold the composition, so the type carries it: bigger scale,
     a sage rule under the headline, and the figures band right below. */
  align-items: center;
  min-height: min(74dvh, 760px);
  border-bottom: 1px solid var(--rule-dark);
}

.hero--type .hero__headline {
  max-width: 22ch;
}

.hero--type .hero__headline::after {
  content: '';
  display: block;
  width: clamp(56px, 8vw, 120px);
  height: 2px;
  margin-top: 0.42em;
  background: var(--accent);
  transform-origin: 0 50%;
  /* Visible by default. Nothing else drives this pseudo-element, so scaling it
     to zero here would simply hide it for good. */
}

@media (prefers-reduced-motion: no-preference) {
  .hero--type .hero__headline::after {
    animation: ruleIn 0.7s var(--ease) 0.42s both;
  }
}

@keyframes ruleIn {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.hero--type .hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px 28px;
  margin-top: clamp(22px, 3.6dvh, 38px);
}
.hero--type .hero__actions .more { color: var(--accent); }
.hero--type .hero__actions .more:hover { color: var(--on-dark); }

/* ---------- Practice rows ----------------------------------------------- */

.prows { border-bottom: 1px solid var(--rule); }

.prow {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
  padding: clamp(20px, 2.6vw, 30px) 0;
  border-top: 1px solid var(--rule);
  transition: background-color 0.3s var(--ease);
}
.prow:hover { background: var(--tint); }

.prow__title {
  font-size: clamp(1.375rem, 2.2vw, 1.8125rem);
  line-height: 1.2;
  transition: color 0.25s var(--ease);
}
.prow:hover .prow__title { color: var(--link); }

.prow__body { max-width: 56ch; }

/* ---------- The About page's opening paragraphs --------------------------- */

.intro__body {
  max-width: 62ch;
  margin: 0 0 clamp(16px, 1.8vw, 24px);
  font-size: clamp(1.1875rem, 1.43vw, 1.375rem);
  line-height: 1.6;
}
.intro__body:last-child { margin-bottom: 0; }


/* ---------- A section on the navy ground ---------------------------------

   The About page alternates grounds so no two blocks in a row read the same:
   navy head, cream firm, navy phases, tinted values, cream principals, navy
   close. Colours come from the dark tokens, which are identical in both
   themes, so this band looks the same in light and dark mode.
   ------------------------------------------------------------------------ */

.section--dark {
  max-width: none;
  padding-inline: 0;
  background: var(--dark);
  border-top: 1px solid var(--rule-dark);
  border-bottom: 1px solid var(--rule-dark);
  color: var(--on-dark-dim);
}

/* .section caps its own width; on the full-bleed dark band that job moves to
   an explicit wrapper, so the background can run to the window edge.
   A `.section--dark > *` rule would have the same specificity as .phases and
   lose to its `padding: 0`, which is exactly how the columns ended up flush
   against the window edge the first time. */
.section--dark .section__inner {
  max-width: var(--page);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section--dark .section__title { color: var(--on-dark); }
.section--dark .lede { color: var(--on-dark-dim); }


/* ---------- Phases of the asset life -------------------------------------

   Four columns, each opening with a rule, because these are a sequence: 01 is
   where a mandate starts and 04 is where it ends. The numbering earns its
   place here in a way it would not on an unordered list.
   ------------------------------------------------------------------------ */

.phases {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 clamp(18px, 1.9vw, 32px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.phase { padding-top: clamp(18px, 2vw, 26px); }

/* The rule belongs to the step, not the column, so it stops at the text
   rather than running the full gap. */
.phase {
  border-top: 2px solid var(--green-lift);
}

.phase__step {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 0 0 clamp(12px, 1.4vw, 18px);
  color: var(--green-lift);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}

.phase__n { font-variant-numeric: tabular-nums; }
.phase__title {
  margin: 0 0 10px;
  color: var(--on-dark);
  font-size: clamp(1.3125rem, 1.65vw, 1.5625rem);
  line-height: 1.25;
}

.phase__body {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1.125rem, 1.35vw, 1.25rem);
  color: var(--on-dark-dim);
}

@media (max-width: 1239px) {
  .phases { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: clamp(28px, 4vw, 44px); }
}

@media (max-width: 620px) {
  .phases { grid-template-columns: 1fr; row-gap: clamp(24px, 5vw, 36px); }
}


/* Two columns of uppercase labels plus a large figure stop fitting once the
   labels are at body size. COUNTRIES alone is most of a 320px column. */
@media (max-width: 360px) {
  .stats__grid { grid-template-columns: 1fr; }
}


/* ---------- The firm's statement -----------------------------------------

   One line, set large, standing on its own in the section. The accent rule
   above it does the work a quotation mark would otherwise have to, so the
   marks can stay light rather than becoming decoration.
   ------------------------------------------------------------------------ */

.statement { margin: 0; }

/* The same short accent rule the section titles carry, rather than a border
   across the whole section: the quote is narrower than the page, and a rule
   running past it points at nothing. */
.statement::before {
  content: '';
  display: block;
  width: clamp(44px, 6vw, 84px);
  height: 2px;
  margin-bottom: clamp(22px, 2.6vw, 34px);
  background: var(--accent);
}

.statement p {
  /* em, not ch: the measure has to scale with the clamped size, or the quote
     is two lines on a laptop and six on a desktop. */
  max-width: 21em;
  margin: 0;
  color: var(--heading);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-weight: 550;
  font-size: clamp(1.375rem, 2.6vw, 2.125rem);
  line-height: 1.22;
  letter-spacing: -0.015em;
  text-wrap: balance;
}


/* ---------- Where the partners came from ---------------------------------

   The firm name is the content here, so it carries the weight and the
   supporting line stays quiet. Divided by hairlines rather than boxed, so it
   reads as one strip of provenance rather than a third grid of cards.
   ------------------------------------------------------------------------ */

.origins {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: clamp(18px, 2vw, 26px) 0 0;
  padding: 0;
  list-style: none;
}

.origin {
  padding: 0 clamp(14px, 1.4vw, 22px);
  border-left: 1px solid var(--rule);
}
.origin:first-child {
  padding-left: 0;
  border-left: 0;
}
.origin:last-child { padding-right: 0; }

.origin__name {
  margin: 0 0 6px;
  color: var(--heading);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-weight: 550;
  font-size: clamp(1.375rem, 1.87vw, 1.625rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
}

.origin__kind {
  margin: 0 0 12px;
  color: var(--link);
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.origin__body {
  margin: 0;
  max-width: 32ch;
  font-size: clamp(1.125rem, 1.35vw, 1.25rem);
}

/* The second subhead in the section needs air above it; the first sits right
   under the lede. */
.work__subtitle--spaced { margin-top: clamp(44px, 6vw, 76px); }

@media (max-width: 620px) {
  .origins { grid-template-columns: 1fr; row-gap: clamp(22px, 5vw, 32px); }
  .origin {
    padding-left: 0;
    padding-right: 0;
    border-left: 0;
  }
}


/* ---------- Values, on the About page ------------------------------------

   Four columns divided by hairlines, which is how the firm presents these in
   its own materials. The rules do the separating, so the cards carry no
   background of their own and the band colour runs straight through.
   ------------------------------------------------------------------------ */

.values {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--rule);
}

.value {
  padding: clamp(24px, 2.6vw, 36px) clamp(14px, 1.4vw, 22px);
  border-left: 1px solid var(--rule);
}

/* The outer edges align with the section gutter rather than sitting inside a
   rule that has nothing on the other side of it. */
.value:first-child {
  padding-left: 0;
  border-left: 0;
}
.value:last-child { padding-right: 0; }

.value__title {
  margin: 0 0 10px;
  font-size: clamp(1.3125rem, 1.65vw, 1.5625rem);
  line-height: 1.25;
}

.value__body {
  margin: 0;
  max-width: 34ch;
  font-size: clamp(1.125rem, 1.35vw, 1.25rem);
}

/* Three across, then stacked. Three cards do not split evenly into two
   columns, so the narrower layouts go straight to one. */
@media (max-width: 1099px) {
  .values { grid-template-columns: 1fr; }

  .value {
    padding-left: 0;
    padding-right: 0;
    border-left: 0;
    border-top: 1px solid var(--rule);
  }
  .value:first-child { border-top: 0; }
  .value__body { max-width: 68ch; }
}

/* ---------- Mandate rail without images --------------------------------- */

/* The card was mostly photograph; with that gone the client name carries it. */
.proj {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 30px);
  background: var(--bg);
  border-top: 2px solid var(--accent);
}

.proj__title {
  font-size: clamp(1.375rem, 2.09vw, 1.8125rem);
  line-height: 1.18;
}

/* ---------- Principals without portraits -------------------------------- */

.person {
  display: block;
  padding: clamp(20px, 2.4vw, 28px) 0;
  border-top: 1px solid var(--rule);
}

@media (min-width: 700px) {
  .prow { grid-template-columns: minmax(0, 1fr) minmax(0, 1.45fr); gap: 28px; }
}

/* ---------- Principals, typographic ------------------------------------- */

.person {
  padding-top: 20px;
  border-top: 2px solid var(--accent);
}

.person__name {
  font-size: clamp(1.375rem, 1.98vw, 1.625rem);
  line-height: 1.2;
}

.person__bio { max-width: 68ch; }
.person__bio + .person__bio { margin-top: 0.9em; }

/* ---------- Principals, with portraits ---------------------------------- */

/* The bios run to several paragraphs, so one person per row: portrait
   beside the text from tablet width up, above it on phones. */
.people { grid-template-columns: 1fr; }

.person {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  align-items: start;
}

.person__photo {
  display: block;
  width: 96px;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  background: var(--tint);
}

/* First paragraph always shows; main.js hides the rest behind a toggle.
   Without JavaScript the whole bio shows and the toggle stays hidden. */
.person__more { margin-top: 0.9em; }
.person { scroll-margin-top: calc(var(--nav-h) + 12px); }
.person__more[hidden] { display: none; }

.person__toggle {
  margin-top: 1.1em;
  padding: 0 0 4px;
  border: 0;
  border-bottom: 1px solid var(--accent);
  background: none;
  color: var(--link);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  cursor: pointer;
}
.person__toggle:hover { color: var(--accent); }
.person__toggle::after { content: " +"; }
.person__toggle[aria-expanded="true"]::after { content: " \2212"; }

@media (min-width: 700px) {
  .person { grid-template-columns: 128px minmax(0, 1fr); gap: 32px; }
  .person__photo { width: 100%; }
}

/* ---------- Mandate rail cards, typographic ----------------------------- */

/* Without a photograph the card is short, so it sits tight to its content
   rather than stretching to a photograph-sized box. */
.work__scroller { align-items: stretch; }

.proj { justify-content: flex-start; gap: 12px; }
.proj .meta { margin-top: auto; }

/* =========================================================================
   Print. Credentials get printed and PDF'd for panels, so the mandates page
   in particular has to come out as a clean list rather than a screenshot of a
   website: no bar, no filter controls, no navy fills eating toner, every
   mandate detail expanded, and the URL of each link spelled out.
   ========================================================================= */

@media print {
  /* Ink economy: everything on white, dark text. */
  :root {
    --bg: #ffffff;
    --band: #ffffff;
    --surface: #ffffff;
    --tint: #ffffff;
    --heading: #101c33;
    --text: #33302b;
    --rule: #c9c4b8;
    --dark: #ffffff;
    --dark-deep: #ffffff;
    --on-dark: #101c33;
    --on-dark-dim: #33302b;
    --rule-dark: #c9c4b8;
  }

  body { background: #fff; color: #33302b; font-size: 10.5pt; line-height: 1.45; }

  /* Screen furniture that means nothing on paper. */
  .nav, .nav__progress, .skip-link, .work__controls, .work__progress,
  .filters, .foot__nav, .cta__actions, .hero__actions, .linkbtn,
  .feat__toggle, .viewbtn { display: none !important; }

    /* Reveal animations must never leave printed content invisible.
       Transition declarations sit ABOVE !important in the cascade, so an
       in-flight reveal transition holds opacity at 0 and prints a blank
       block no matter how forcefully the rule below is written. Both the
       animations and the transitions have to be switched off. */
    *, *::before, *::after {
      animation: none !important;
      transition: none !important;
    }
  .reveal, .js .reveal { opacity: 1 !important; transform: none !important; }

  /* Every disclosure prints open. */
  .mandate__panel, .feat__panel { display: block !important; }
  .mandate__panel[hidden], .feat__panel[hidden] { display: block !important; }

  main, .page { padding-top: 0 !important; }

  .hero, .phead, .section, .cta, .offices, .results, .work {
    padding: 0 0 14pt !important;
    min-height: 0 !important;
    background: #fff !important;
    border: 0 !important;
  }

  .hero__headline, .phead__title { font-size: 20pt; color: #101c33; }
  .section__title, .offices__title, .cta__title { font-size: 14pt; color: #101c33; }
  .section__title::after { display: none; }

  /* The mandate list: one clean row per mandate, never split across pages. */
  .results__grid {
    display: block !important;
    background: none !important;
    border: 0 !important;
  }
  .mandate {
    break-inside: avoid;
    page-break-inside: avoid;
    padding: 8pt 0 !important;
    border-top: 0.5pt solid #c9c4b8 !important;
    background: #fff !important;
  }
  .mandate::before { display: none !important; }
  .mandate__title { font-size: 12pt; }
  .mandate__facts { gap: 1pt 8pt; }
  .mandate__panel p { font-size: 9.5pt; }

  .proj, .person, .prow, .value, .office { break-inside: avoid; page-break-inside: avoid; }

  /* The rail cannot scroll on paper, so it stacks. */
  .work__scroller {
    display: block !important;
    overflow: visible !important;
    background: none !important;
    border: 0 !important;
  }
  .proj { border-top: 0.5pt solid #c9c4b8 !important; padding: 8pt 0 !important; }

  .people, .offices__grid, .values, .stats__grid {
    display: block !important;
    background: none !important;
    border: 0 !important;
  }
  .stat { display: inline-block; margin: 0 18pt 8pt 0; }

  /* Spell out where a link actually goes. */
  a[href^="http"]::after,
  a[href^="mailto"]::after {
    content: ' (' attr(href) ')';
    font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
    font-size: 8pt;
    color: #55514a;
  }
  a[href^="#"]::after, a[href$=".html"]::after { content: ''; }

  /* Contact details belong on every printed page. */
  @page { margin: 16mm 14mm; }
}

/* Very short desktop windows: the typographic hero has more stacked text than
   the photographic one did, so it needs its own tighter step or it pushes the
   figures band off the fold entirely. */
@media (min-width: 900px) and (max-height: 640px) {
  .hero--type {
    min-height: 0;
    padding-top: calc(var(--nav-h) + 22px);
    padding-bottom: 22px;
  }
  .hero--type .hero__headline { font-size: min(2rem, 8dvh); }
  .hero--type .hero__headline::after { margin-top: 0.3em; }
  .hero--type .hero__sub { max-width: 62ch; font-size: 1.0625rem; }
  .hero--type .hero__actions { margin-top: 18px; }
}

/* =========================================================================
   Footprint map. Grey world, brand navy on the countries carrying mandates.
   Geometry is generated by build-map.py; everything visual lives here so the
   map themes with the rest of the page instead of baking colours into the SVG.
   ========================================================================= */

/* Referenced by about.html, which had no rule for it until now. */
.section--tint { background: var(--band); }

:root {
  --map-base: var(--rule-light);       /* the grey world */
  --map-on:   var(--navy);             /* countries with mandates */
  --map-line: var(--band);             /* hairline between countries */
}

@media (prefers-color-scheme: dark) {
  :root {
    --map-base: rgba(242, 239, 232, 0.10);
    /* Navy on navy would vanish, so the highlight moves up the same blue
       family rather than switching to a different accent. */
    --map-on:   var(--slate-light);
    --map-line: var(--navy-deep);
  }
}

.map {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 3vw, 32px);
}

.map__plate {
  /* The plate is 2.6:1, so it gets its own scroll container rather than
     squeezing the whole page at phone widths. */
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.map__svg {
  display: block;
  width: 100%;
  min-width: 560px;      /* below this the landmasses stop being readable */
  height: auto;
}

.map__base path {
  fill: var(--map-base);
  stroke: var(--map-line);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
}

.map__live path {
  fill: var(--map-on);
  stroke: var(--map-line);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
  transition: fill 0.25s var(--ease);
}
.map__live path:hover { fill: var(--accent); }

/* ---------- Key: the counts the fills cannot carry ---------------------- */

.map__key {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.map__key li {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 8px 13px;
  background: var(--tint);
  border: 1px solid var(--rule);
}

.map__country {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 0.9375rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--heading);
}

.map__n {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1.125rem;
  font-weight: 550;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--link);
}

@media print {
  /* Fills survive, but the hairlines and the hover state are noise on paper. */
  .map__plate { overflow: visible !important; }
  .map__svg { min-width: 0 !important; }
}

/* =========================================================================
   Track record: two parts under one heading. They need to read as separate
   moments without becoming separate sections, so each gets its own subhead
   and the second is divided off by a rule.
   ========================================================================= */

.work__part + .work__part {
  margin-top: clamp(40px, 6vw, 76px);
  padding-top: clamp(32px, 4vw, 56px);
  border-top: 1px solid var(--rule);
}

.work__subtitle {
  font-family: 'Outfit', 'Century Gothic', system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--link);
}

/* The rail controls used to sit beside the h2; they now sit beside the
   subhead, so the row needs to align on the smaller type. */
.work__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 24px;
  margin-bottom: clamp(18px, 2.5vw, 28px);
}

.work__part .lede { margin-top: 10px; margin-bottom: clamp(20px, 3vw, 32px); }

/* =========================================================================
   Photography.

   Gated behind class="with-photos" on <body>, which index.html now carries.
   The gate stays because the other three pages have no photographs, and
   because removing the class returns the page to its typographic state
   exactly rather than to a page full of broken image boxes.

   Files live in assets/photos/ as webp, resized and re-encoded by
   build-photos.py from raw-images/. Do not commit the originals: the five in
   use came to 6.9 MB and ship at 283 KB. Project images are in
   assets/photos/projects/, named after the mandate they show; the source
   jpg/png files beside them are kept out of git by .gitignore.
   ========================================================================= */

.with-photos .hero--type {
  position: relative;
  isolation: isolate;
  /* Named once: the desktop slab and the narrow-window variant below both
     reference it, and the preview bundler inlines it as base64, so a second
     literal would ship the cover twice. */
  --hero-photo: url('assets/photos/hero.webp');
}

/* The cover photograph is a slab down the RIGHT of the hero that dissolves
   leftward into the navy, rather than a full-bleed image under a scrim. Two
   reasons: the headline then sits on flat navy and needs no scrim of its own,
   and the dissolve reads as the section opening onto the photograph instead of
   the photograph being pasted behind it.

   The dissolve is mask-image, one layer only. Two mask layers would need
   mask-composite: intersect, which falls back to a union where it is not
   supported and would undo the fade entirely. So the vertical softening is
   done in the veil gradient stacked over the photograph instead, which also
   pulls the image into the navy palette. */
.with-photos .hero--type::before {
  content: '';
  position: absolute;
  inset: 0 0 0 auto;
  width: min(56%, 940px);
  z-index: -1;
  background-image:
    linear-gradient(
      180deg,
      rgba(13, 26, 51, 0.88) 0%,
      rgba(13, 26, 51, 0.34) 30%,
      rgba(13, 26, 51, 0.40) 68%,
      rgba(13, 26, 51, 0.94) 100%),
    var(--hero-photo);
  background-size: cover, cover;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 0.42) 30%, #000 68%);
          mask-image:
    linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 0.42) 30%, #000 68%);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

@media (prefers-reduced-motion: no-preference) {
  .with-photos .hero--type::before { animation: fadeIn 1.1s var(--ease) 0.15s both; }
}

/* Narrow windows have no room for a slab beside the headline, so the cover
   goes full bleed and takes the heavier scrim the type then needs. */
@media (max-width: 900px) {
  .with-photos .hero--type::before {
    width: 100%;
    background-image:
      linear-gradient(
        180deg,
        rgba(13, 26, 51, 0.93) 0%,
        rgba(13, 26, 51, 0.74) 45%,
        rgba(13, 26, 51, 0.96) 100%),
      var(--hero-photo);
    -webkit-mask-image: none;
            mask-image: none;
  }
}

/* .more is a standalone "see all" link everywhere else on the site, so it
   carries auto side margins and its own gutter padding. Inside the hero action
   row that auto margin resolved to 314px and threw the link across onto the
   photograph, which is why the row's own gap had never had any effect. */
.with-photos .hero--type .hero__actions .more {
  max-width: none;
  margin: 0;
  padding: 0;
}

/* Each card carries the asset class it is about. */
.proj__media { display: none; }

.with-photos .proj__media {
  display: block;
  height: clamp(112px, 14vw, 152px);
  margin: calc(clamp(22px, 2.4vw, 30px) * -1) calc(clamp(22px, 2.4vw, 30px) * -1)
          clamp(16px, 2vw, 22px);
  background-color: var(--tint);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.with-photos .proj[data-photo="sr400"]   .proj__media { background-image: url('assets/photos/projects/sr400.webp'); }
.with-photos .proj[data-photo="bts"]     .proj__media { background-image: url('assets/photos/projects/proj-toll-road.webp'); }
.with-photos .proj[data-photo="minera"]  .proj__media { background-image: url('assets/photos/projects/ruta-minera.webp'); }
.with-photos .proj[data-photo="zambia"]  .proj__media { background-image: url('assets/photos/projects/proj-solar.webp'); }

@media (prefers-reduced-motion: no-preference) {
  .with-photos .proj__media { transition: opacity 0.4s var(--ease); }
  .with-photos .proj:hover .proj__media { opacity: 0.88; }
}

@media print {
  /* Background images do not print by default in most browsers, and a grey
     band where a photograph should be looks worse than no band at all. */
  .with-photos .hero--type::before { display: none !important; }
  .with-photos .proj__media { display: none !important; }
}

/* The gutter for everything in the Track record band except the rail, which
   bleeds to the edge on purpose. .work itself has no horizontal padding, so
   without this its heading, subheads, lede and map sat flush against the
   window edge while every other section was inset. */
.work__inner {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* Divider between the two parts, inset to the content width rather than
   running the full width of the band. */
.work__part + .work__part {
  position: relative;
  border-top: 0;
}
.work__part + .work__part::before {
  content: '';
  position: absolute;
  top: 0;
  left: var(--gutter);
  right: var(--gutter);
  max-width: var(--page);
  height: 1px;
  margin: 0 auto;
  background: var(--rule);
}

/* The facts list is a two-column grid whose first column sizes to the widest
   label. With the larger type, COUNTERPARTY at 11px plus letter-spacing made
   that column wide enough to push the whole card past a 320px viewport, so on
   the narrowest screens the labels sit above their values instead. */
@media (max-width: 430px) {
  .mandate__facts {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .mandate__facts dt { padding-top: 8px; }
  .mandate__facts dd { padding-bottom: 2px; }
}

/* Grid items default to min-content width, which lets a stubborn child widen
   the whole cell. */
.mandate { min-width: 0; }

/* Display figures follow the titles now that everything is sans: at weight 400
   they read as body copy at a large size rather than as headline numbers. */
.stat__figure,
.summary__n,
.map__n,
.sector__count,
.sblock__count span { font-weight: 550; }
