/* ==========================================================================
   V15 Vantage — 180 Degrees Consulting Delft–Rotterdam
   Vanilla CSS. No framework, no build step. Opens as a plain file.

   Layering inside the hero (all self-contained, nothing position:fixed —
   so the bottom-blur can never bleed onto the content below, with or
   without JavaScript):
     0  .hero__media    photo layers (parallax wrapper + Ken Burns img)
     2  .hero__blur     bottom-only backdrop blur, masked, no darkening
     3  .hero__inner    bottom-aligned hero content
     40 .site-header    sticky navbar
     90 .mobile-menu    slide-down overlay
   ========================================================================== */

/* Inter (300–700) is requested from Google Fonts by a <link> in each page's
   <head>, not by an @import here. An @import chains HTML -> styles.css ->
   font CSS -> font files, delaying first paint by a whole round trip. */

/* --------------------------------------------------------------- tokens -- */

:root {
  --green: #78b038;          /* brand mark — decorative / large surfaces only */
  --green-deep: #4a7322;     /* AA-safe on white for text */
  --green-darkest: #3d6b1c;

  --night: #0a0f16;          /* sampled from the hero set's darkest water */
  --night-2: #111823;
  --ink: #0e1411;
  --paper: #fafbf9;
  --paper-2: #f1f4ef;
  --line: #dfe4da;
  --muted: #5c6660;

  --wrap: 1240px;
  --nav-h: 74px;

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* the sticky navbar must not cover the target of a scroll-anchor link */
  scroll-padding-top: calc(var(--nav-h) + 16px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: Inter, "Segoe UI", system-ui, -apple-system, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: 1.12; letter-spacing: -0.022em; font-weight: 600; margin: 0; }
p { margin: 0 0 1rem; }
img { max-width: 100%; display: block; }
a { color: var(--green-deep); }

.wrap { width: min(100% - 3rem, var(--wrap)); margin-inline: auto; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--ink); color: #fff; padding: 0.75rem 1.15rem;
  border-radius: 0 0 10px 0; font-weight: 600; text-decoration: none;
}
.skip:focus { left: 0; }

:where(a, button, input, textarea, summary):focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
  border-radius: 4px;
}
/* on the photo, the brand green alone is too low-contrast for a focus ring */
.hero :where(a, button, input):focus-visible,
.site-header :where(a, button, input):focus-visible,
.mobile-menu :where(a, button, input):focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

/* ------------------------------------------------------- liquid glass -- */
/* Reusable. Used by: nav search pill, profile button, hamburger, secondary
   hero CTA, prev/next city buttons, the search + member panels.            */

.liquid-glass {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.01);
  background-blend-mode: luminosity;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  border: none;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1);
}

/* thin gradient-stroke border: a padded gradient box, dual-masked so only
   the padding ring paints and the fill is punched out */
.liquid-glass::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1.4px;
  border-radius: inherit;
  background: linear-gradient(
    160deg,
    rgba(255, 255, 255, 0.45) 0%,
    rgba(255, 255, 255, 0) 40%,
    rgba(255, 255, 255, 0) 60%,
    rgba(255, 255, 255, 0.45) 100%
  );
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* ---------------------------------------------------------- animation -- */

@keyframes blurFadeUp {
  from { opacity: 0; filter: blur(20px); transform: translateY(40px); }
  to   { opacity: 1; filter: blur(0);    transform: translateY(0); }
}

/* staggered entrance — base state is invisible so the delay reads as a hold */
[data-fade] {
  opacity: 0;
  animation: blurFadeUp 1s var(--ease-out) forwards;
}
[data-fade="0"]   { animation-delay: 0ms; }
[data-fade="100"] { animation-delay: 100ms; }
[data-fade="200"] { animation-delay: 200ms; }
[data-fade="300"] { animation-delay: 300ms; }
[data-fade="350"] { animation-delay: 350ms; }
[data-fade="400"] { animation-delay: 400ms; }
[data-fade="500"] { animation-delay: 500ms; }
[data-fade="600"] { animation-delay: 600ms; }
[data-fade="700"] { animation-delay: 700ms; }
[data-fade="800"] { animation-delay: 800ms; }
[data-fade="900"] { animation-delay: 900ms; }

@keyframes kenBurns {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(1.13) translate3d(-1.2%, -1.6%, 0); }
}

/* ------------------------------------------------ scroll choreography -- */
/*
   Native CSS scroll-driven animation. No JavaScript, no library, no build
   step, and nothing to keep in sync on the main thread — the browser runs
   these off the compositor.

   Everything here is additive: the base state of every element is visible and
   in place. A browser without `animation-timeline` support simply shows the
   finished state, which is the page exactly as it was. Reduced motion opts
   out at the @media level, so nothing is animated and nothing starts hidden.

   `view()` measures each element against the viewport, so a grid of cards
   staggers itself for free — later cards enter later. No per-item delays.
*/

@keyframes revealUp {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes heroExit {
  /* hero copy lifts away and dims as the section scrolls out from under it */
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-70px); }
}

@keyframes railGrow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* --- content reveals. Selectors target the existing structure, so no
           markup had to change and nothing can fall out of sync with it. --- */
    .section-head,
    .prose,
    .fact-panel,
    .card,
    .timeline-step,
    .service-list article,
    .proof-strip div,
    .person,
    .cta-panel > *,
    .intake-form,
    .guide-body > * {
      animation: revealUp 1ms linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 22%;
    }

    /* --- the hero hands off to the page instead of just scrolling away.
           Staged heroes are excluded: their contents are pinned, so a view()
           timeline would sit frozen while the section is stuck. They use the
           eased --hero-p that drives the dolly instead, so copy and camera
           move on exactly the same clock. --- */
    .hero:not(.hero--stage) .hero__inner {
      animation: heroExit 1ms linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 85%;
    }

    /* --- a hairline that draws itself across each section head --- */
    .section-head::after {
      content: "";
      display: block;
      height: 1px;
      margin-top: 1.6rem;
      background: linear-gradient(90deg, var(--green) 0%, rgba(120, 176, 56, 0) 70%);
      transform-origin: left center;
      animation: railGrow 1ms linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 30%;
    }
    .section--dark .section-head::after {
      background: linear-gradient(90deg, #a7d16a 0%, rgba(167, 209, 106, 0) 70%);
    }
  }
}

/* ------------------------------------------------------------- navbar -- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  /* Dark glass at all times, and opaque enough that white nav text clears AA
     even when what sits behind it is the near-white page body rather than a
     night photo — the guide has no hero, and every page's header crosses
     light sections on scroll. */
  background: rgba(10, 15, 22, 0.8);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  transition: background 0.4s var(--ease-out), border-color 0.4s var(--ease-out);
}
.site-header.is-scrolled {
  background: rgba(10, 15, 22, 0.92);
  border-bottom-color: rgba(255, 255, 255, 0.12);
}

.nav-shell {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  width: min(100% - 2.5rem, var(--wrap));
  margin-inline: auto;
}

.brand {
  display: flex; align-items: center; gap: 0.65rem;
  text-decoration: none; color: #fff; flex-shrink: 0;
}
.brand img { width: 34px; height: 24px; object-fit: contain; }
.brand span { display: flex; flex-direction: column; line-height: 1.18; }
.brand strong { font-size: 0.845rem; font-weight: 600; letter-spacing: -0.01em; }
.brand small { font-size: 0.68rem; color: rgba(255, 255, 255, 0.62); letter-spacing: 0.06em; text-transform: uppercase; }

.main-nav {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-inline: auto;
  min-width: 0;   /* flex items default to min-width:auto and refuse to shrink */
}
.main-nav a {
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.74);
  padding: 0.5rem 0.8rem;
  border-radius: 999px;
  white-space: nowrap;
  transition: color 0.25s var(--ease-out), background 0.25s var(--ease-out);
}
.main-nav a:hover { color: #fff; }

/* NAV DIFFERENTIATOR — this variant's device:
   scroll-anchor links are plain text; own-page links are glass pills with a
   green status dot. Consistent across all six pages.                        */
.main-nav .nav-page {
  color: rgba(255, 255, 255, 0.92);
  font-weight: 500;
  background: rgba(255, 255, 255, 0.055);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.13);
  padding-left: 1.6rem;
}
.main-nav .nav-page::before {
  content: "";
  position: absolute;
  left: 0.75rem; top: 50%;
  width: 5px; height: 5px; margin-top: -2.5px;
  border-radius: 50%;
  background: var(--green);
  opacity: 0.55;
  transition: opacity 0.25s var(--ease-out), box-shadow 0.25s var(--ease-out);
}
.main-nav .nav-page { position: relative; }
.main-nav .nav-page:hover { background: rgba(255, 255, 255, 0.1); }
.main-nav .nav-page:hover::before { opacity: 1; }
.main-nav .nav-page[aria-current="page"] {
  background: rgba(120, 176, 56, 0.17);
  box-shadow: inset 0 0 0 1px rgba(120, 176, 56, 0.42);
  color: #fff;
}
.main-nav .nav-page[aria-current="page"]::before {
  opacity: 1;
  box-shadow: 0 0 0 3px rgba(120, 176, 56, 0.25);
}

.nav-tools { display: flex; align-items: center; gap: 0.55rem; flex-shrink: 0; }

.nav-search {
  display: flex; align-items: center; gap: 0.5rem;
  height: 38px; padding: 0 0.95rem;
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.72);
  font: inherit; font-size: 0.82rem;
  cursor: pointer;
  min-width: 148px;
  transition: color 0.25s var(--ease-out);
}
.nav-search:hover { color: #fff; }
.nav-profile,
.nav-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  color: rgba(255, 255, 255, 0.82);
  cursor: pointer;
  padding: 0;
  transition: color 0.25s var(--ease-out);
}
.nav-profile:hover, .nav-toggle:hover { color: #fff; }
.nav-toggle { display: none; border-radius: 12px; }

.ico { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }

/* --------------------------------------------------------------- hero -- */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;              /* contains the parallax overscan */
  min-height: 100vh;
  min-height: 100svh;
  margin-top: calc(-1 * var(--nav-h));   /* photo runs up behind the navbar */
  padding-top: var(--nav-h);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;     /* hero content is bottom-aligned */
  background: var(--night);      /* shows through before the photo decodes */
}
.hero--inner { min-height: 86vh; min-height: 86svh; }

/* ------------------------------------------------------- scroll stage -- */
/*
   The hero gets a scroll runway so the camera has somewhere to travel: the
   section is two viewports tall and its contents pin to the top for the first
   one. Scrolling the second viewport dollies into the photograph instead of
   just sliding it away.

   Gated on reduced-motion at the CSS level rather than on a JS class, so the
   page height is settled at first paint and nothing shifts once scripts run.
   With reduced motion the runway disappears entirely and the hero is a plain
   one-viewport section again — no dead scroll to wade through.
*/
@media (prefers-reduced-motion: no-preference) {
  /* The section becomes a plain block: as a flex column with
     justify-content:flex-end it shoved the sticky child to the far end of the
     runway, leaving the top of the page empty. The stage is the flex context
     now, and it carries the nav padding too. */
  .hero--stage {
    min-height: 0;
    height: 200vh;
    height: 200svh;
    display: block;
    padding-top: 0;
    /* `overflow: hidden` on an ancestor silently disables position:sticky on
       its descendants — the stage scrolled away instead of pinning. The clip
       is no longer needed here anyway: .hero__stage does it now. */
    overflow: visible;
  }
  .hero--stage.hero--inner { height: 186vh; height: 186svh; }
  .hero--stage .hero__stage {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    overflow: hidden;
    padding-top: var(--nav-h);
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
  }
  /* the photo must fill the pinned stage, not the two-viewport section */
  .hero--stage .hero__media { top: 0; height: 100%; }

  /* Copy recedes as the camera pushes in, on the same eased clock as the
     dolly. --hero-p is written by hero-controller.js; the fallback of 0
     means an un-upgraded page simply shows the copy in place. */
  .hero--stage .hero__inner {
    opacity: clamp(0, calc(1 - var(--hero-p, 0) * 1.45), 1);
    transform: translate3d(0, calc(var(--hero-p, 0) * -70px), 0);
    will-change: opacity, transform;
  }
  .hero--stage .hero__scroll {
    opacity: clamp(0, calc(1 - var(--hero-p, 0) * 1.8), 1);
  }

  /* Without WebGL the dolly still happens, just without depth separation:
     a plain scale on the photo driven by the same progress value. */
  .hero--stage:not(.is-webgl) .hero__img {
    transform: scale(calc(1.02 + var(--hero-p, 0) * 0.38));
    transform-origin: center 45%;
  }
  .hero--stage:not(.is-webgl) .hero__img { animation: none; }
}

/* scroll indicator — SCROLL, a progress rail, and a percentage */
.hero__scroll {
  position: absolute;
  left: 0; right: 0; bottom: 1.5rem;
  z-index: 4;
  width: min(100% - 3rem, var(--wrap));
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}
.hero__scroll-track {
  flex: 1;
  max-width: 260px;
  height: 1px;
  background: rgba(255, 255, 255, 0.28);
  position: relative;
  overflow: hidden;
}
.hero__scroll-fill {
  position: absolute;
  inset: 0;
  background: var(--green);
  transform: scaleX(0);
  transform-origin: left center;
}
.hero__scroll-count { font-variant-numeric: tabular-nums; min-width: 3.2em; }
.hero[data-tone="light"] .hero__scroll {
  color: #2c352f;
  text-shadow: 0 1px 10px rgba(255, 255, 255, 0.8);
}
.hero[data-tone="light"] .hero__scroll-track { background: rgba(0, 0, 0, 0.22); }
.no-js .hero__scroll { display: none; }
@media (prefers-reduced-motion: reduce) { .hero__scroll { display: none; } }

/* parallax wrapper — JS writes transform here only. 15% overscan top and
   bottom gives the parallax somewhere to travel without exposing an edge. */
.hero__media {
  position: absolute;
  left: 0; right: 0;
  top: -15%;
  height: 130%;
  z-index: 0;
  will-change: transform;
}

/* Ken Burns lives on the img itself, so the two transforms never collide */
.hero__img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 42%;
  transform: scale(1.02);
  animation: kenBurns 38s ease-in-out infinite alternate;
}
.hero__img--incoming { opacity: 0; }
/* crossfade is opacity-only — never triggers layout */
.hero__img { transition: opacity 0.62s var(--ease-out); }

.hero.is-paused .hero__img { animation-play-state: paused; }

/* ---------------------------------------------------------- depth hero -- */
/* hero-depth.js mounts a WebGL canvas that renders the photo through its
   depth map. It only ever appears on top of a working <img>, so removing it
   returns the page to the CSS pan with no other change. */

.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 1;
}

/* With the shader doing the parallax, the wrapper no longer needs its 15%
   overscan and the img no longer needs a 1.13 zoom. Dropping both is a real
   sharpness gain: the photo is sampled near 1:1 instead of magnified ~1.3x
   before the display even scales it. */
.hero.is-webgl .hero__media {
  top: 0;
  height: 100%;
  transform: none !important;
  will-change: auto;
}
/* the <img> layers stay in the DOM — they carry the alt text and are the
   fallback the moment WebGL goes away — but they are not painted */
.hero.is-webgl .hero__img {
  opacity: 0 !important;
  animation: none !important;
}

/* bottom-only blur. Blur, no darkening — the mask fades it out by mid-frame.
   Sits above the photo and below the content.                              */
.hero__blur {
  position: absolute;
  inset: 0;
  z-index: 2;
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  /* Held deliberately low. An earlier pass pushed full blur to 42% and cleared
     at 66% to calm the lede, which worked on the numbers and lost the
     photograph: two thirds of the frame went soft and the city stopped being
     legible as a place. The frame is what the hero is for. Full blur now stops
     at 14% and clears by 38%, so it sits under the controls and the bottom of
     the copy and nothing else. The lede is carried by its own text-shadow and
     by choosing frames that are dark where the copy lands, not by softening the
     picture until the problem goes away. */
  -webkit-mask-image: linear-gradient(to top, black 0%, black 14%, transparent 38%);
  mask-image: linear-gradient(to top, black 0%, black 14%, transparent 38%);
  pointer-events: none;
}

/* a very light scrim under the text only — the spec forbids darkening the
   photo, so this is confined to the text column and stays subtle. It is the
   difference between "legible" and "nearly legible" on the fog frame. */
.hero__inner {
  position: relative;
  z-index: 3;
  width: min(100% - 3rem, var(--wrap));
  margin-inline: auto;
  padding-block: 3rem 3.4rem;
  color: #fff;
}

/* The pool spans near-black night frames and a near-white fog frame. White
   copy survives the dark end unaided but disappears on the light end, so
   everything in the hero carries a shadow. This darkens glyph edges only —
   the photograph itself is never tinted, per the blur-only rule above. */
.hero__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.55rem 0.9rem;
  font-size: 0.775rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.92);
  margin-bottom: 1.35rem;
  font-weight: 500;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.65);
}
.hero__meta .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--green); flex-shrink: 0; }
.hero__meta .tag {
  padding: 0.28rem 0.7rem; border-radius: 999px;
  background: rgba(120, 176, 56, 0.2);
  box-shadow: inset 0 0 0 1px rgba(120, 176, 56, 0.45);
  color: #eaf5dc; letter-spacing: 0.08em;
}

.hero h1 {
  font-size: clamp(2.6rem, 6.4vw, 5.1rem);
  font-weight: 600;
  max-width: 17ch;
  margin-bottom: 1.15rem;
  text-shadow: 0 2px 26px rgba(0, 0, 0, 0.62), 0 1px 3px rgba(0, 0, 0, 0.3);
}
.hero h1 em { font-style: normal; color: #c6e39a; }

.hero__lede {
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  font-weight: 300;
  line-height: 1.6;
  max-width: 60ch;
  color: #fff;
  text-shadow: 0 1px 16px rgba(0, 0, 0, 0.7), 0 1px 3px rgba(0, 0, 0, 0.35);
  margin-bottom: 1.9rem;
}

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: center; }

.btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.82rem 1.5rem;
  border-radius: 999px;
  font-size: 0.9rem; font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  border: none;
  white-space: nowrap;
  transition: transform 0.25s var(--ease-out), background 0.25s var(--ease-out), box-shadow 0.25s var(--ease-out);
}
.btn--solid { background: #fff; color: #0d1210; }
.btn--solid:hover { background: #eef2ea; transform: translateY(-1px); }
/* The secondary hero action used to be a dark glass pill, which went muddy
   against a night frame and read as disabled rather than secondary. It is now
   the same white pill outlined instead of filled: unmistakably the same family
   as the primary, still clearly second. */
.btn--glass {
  color: #fff;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.85);
}
.btn--glass:hover { background: rgba(255, 255, 255, 0.16); transform: translateY(-1px); }

/* Hero-scoped legibility floor. The reusable .liquid-glass fill is
   rgba(255,255,255,0.01) by design, which reads as nothing when the frame
   behind it is the near-white fog photo. Controls sitting directly on the
   photography get a neutral tint so they stay operable across all eight
   frames; the .liquid-glass class itself is unchanged. */
.hero .btn--glass,
.hero .city-switch__btn {
  background: rgba(10, 15, 22, 0.34);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
}
.hero .btn--glass:hover,
.hero .city-switch__btn:hover:not(:disabled) {
  background: rgba(10, 15, 22, 0.5);
  transform: translateY(-1px);
}

/* ---------------------------------------------------------- light frame --
   Two frames in the pool are too bright for white copy: the fog render, which
   is near-white end to end, and the Delft Markt golden hour, whose sky sits
   behind the text column. No amount of text shadow gets white copy to AA over
   either, and darkening the photo is ruled out, so the hero inverts to dark
   type for those frames. Set from the POOL entry's `tone`.

   The small-text colours are near-black rather than the mid-greys this block
   originally carried. Those greys were tuned against the near-white fog frame
   and cleared AA there, but the golden-hour frame is mid-tone, not white — it
   measured 4.37:1 for the meta row, 4.15:1 for the lede and 3.81:1 for the
   counter, all short of 4.5. Going darker costs nothing on the fog frame,
   where more contrast is only ever an improvement. */

.hero[data-tone="light"] .hero__meta { color: #10160f; text-shadow: 0 1px 10px rgba(255, 255, 255, 0.75); }
.hero[data-tone="light"] .hero__meta .tag {
  background: rgba(74, 115, 34, 0.16);
  box-shadow: inset 0 0 0 1px rgba(61, 107, 28, 0.5);
  color: #33540f;
}
.hero[data-tone="light"] h1 {
  color: #10160f;
  text-shadow: 0 1px 14px rgba(255, 255, 255, 0.8);
}
.hero[data-tone="light"] h1 em { color: var(--green-darkest); }
.hero[data-tone="light"] .hero__lede {
  color: #141a13;
  text-shadow: 0 1px 12px rgba(255, 255, 255, 0.85);
}
.hero[data-tone="light"] .btn--solid { background: #10160f; color: #fff; }
.hero[data-tone="light"] .btn--solid:hover { background: #232b26; }
.hero[data-tone="light"] .city-switch__now { color: #10160f; text-shadow: 0 1px 10px rgba(255, 255, 255, 0.8); }
.hero[data-tone="light"] .city-switch__now strong { color: #10160f; }
/* the glass tint that reads over a night frame lands at mid-grey over a
   white one, which drops white labels under AA — deepen it here */
.hero[data-tone="light"] .btn--glass,
.hero[data-tone="light"] .city-switch__btn { background: rgba(10, 15, 22, 0.62); }
.hero[data-tone="light"] .btn--glass:hover,
.hero[data-tone="light"] .city-switch__btn:hover:not(:disabled) { background: rgba(10, 15, 22, 0.74); }

/* ------------------------------------------------- city cycling control -- */

.city-switch {
  display: flex; align-items: center; gap: 0.85rem;
  margin-top: 2.3rem;
  flex-wrap: wrap;
}
.city-switch__btn {
  display: flex; align-items: center; gap: 0.4rem;
  height: 40px; padding: 0 1.05rem;
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.86);
  font: inherit; font-size: 0.8rem; font-weight: 500;
  cursor: pointer;
  transition: color 0.25s var(--ease-out), background 0.25s var(--ease-out);
}
.city-switch__btn:hover:not(:disabled) { color: #fff; background: rgba(255, 255, 255, 0.09); }
.city-switch__btn:disabled { opacity: 0.45; cursor: default; }

.city-switch__now {
  display: flex; flex-direction: column; gap: 0.1rem;
  font-size: 0.74rem; line-height: 1.3;
  color: rgba(255, 255, 255, 0.8);
  letter-spacing: 0.055em;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.65);
}
.city-switch__now strong {
  font-size: 0.8rem; font-weight: 500; color: #fff;
  letter-spacing: 0.02em;
}
.city-switch__count { font-variant-numeric: tabular-nums; }

/* Photographer + licence for the frame on screen. Tertiary by design: it must
   be legible and findable, not compete with the place name above it. Sits
   outside the aria-live region so cycling does not announce it. */
.city-switch__credit {
  margin: 0.15rem 0 0;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.78);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.6);
}
.hero[data-tone="light"] .city-switch__credit {
  color: #1c2420;
  text-shadow: 0 1px 10px rgba(255, 255, 255, 0.8);
}

/* JS-only affordances stay hidden until JS confirms it is running */
.no-js .city-switch,
.no-js .nav-search,
.no-js .nav-profile,
.no-js .nav-toggle { display: none; }

/* ------------------------------------------------------- mobile menu -- */

.mobile-menu {
  position: fixed;
  top: var(--nav-h); left: 0;
  /* matches the scroll-lock's scrollbar compensation so the fixed menu
     stays flush with the body edge instead of jumping when it opens */
  right: var(--sbw, 0px);
  z-index: 90;
  background: rgba(10, 15, 22, 0.95);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  padding: 1.1rem 0 1.5rem;
  /* closed state: collapsed and non-interactive, never in flow, so opening
     it cannot shift a single pixel of page layout */
  transform: translateY(-12px);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.34s var(--ease-out), opacity 0.28s var(--ease-out), visibility 0s linear 0.34s;
  max-height: calc(100dvh - var(--nav-h));
  overflow-y: auto;
}
.mobile-menu.is-open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition: transform 0.38s var(--ease-out), opacity 0.3s var(--ease-out), visibility 0s;
}

.mobile-menu__inner { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }

.mobile-menu a {
  display: block;
  padding: 0.72rem 0.15rem;
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
  font-size: 1.02rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  opacity: 0;
  transform: translateY(-8px);
}
.mobile-menu.is-open a { animation: blurFadeUp 0.5s var(--ease-out) forwards; }
.mobile-menu.is-open a:nth-child(1) { animation-delay: 40ms; }
.mobile-menu.is-open a:nth-child(2) { animation-delay: 80ms; }
.mobile-menu.is-open a:nth-child(3) { animation-delay: 120ms; }
.mobile-menu.is-open a:nth-child(4) { animation-delay: 160ms; }
.mobile-menu.is-open a:nth-child(5) { animation-delay: 200ms; }
.mobile-menu.is-open a:nth-child(6) { animation-delay: 240ms; }
.mobile-menu.is-open a:nth-child(7) { animation-delay: 280ms; }

.mobile-menu .nav-page {
  color: #fff; font-weight: 500;
  padding-left: 1.15rem;
  position: relative;
}
.mobile-menu .nav-page::before {
  content: "";
  position: absolute; left: 0; top: 50%;
  width: 5px; height: 5px; margin-top: -2.5px;
  border-radius: 50%; background: var(--green);
}
.mobile-menu .nav-page[aria-current="page"] { color: #c6e39a; }

.mobile-menu__tools {
  display: flex; gap: 0.6rem; align-items: center;
  margin-top: 1.2rem;
  opacity: 0;
}
.mobile-menu.is-open .mobile-menu__tools {
  animation: blurFadeUp 0.5s var(--ease-out) 320ms forwards;
}
.mobile-menu__tools .nav-search { flex: 1; }

/* ------------------------------------------- search / member panels -- */

.panel {
  position: fixed;
  z-index: 95;
  top: calc(var(--nav-h) + 10px);
  right: max(1.25rem, calc((100vw - var(--wrap)) / 2));
  width: min(calc(100vw - 2.5rem), 400px);
  border-radius: 16px;
  background: rgba(12, 18, 26, 0.93);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45), inset 0 1px 1px rgba(255, 255, 255, 0.1);
  color: #fff;
  padding: 0.9rem;
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 0.24s var(--ease-out), transform 0.24s var(--ease-out), visibility 0s linear 0.24s;
}
/* the backdrop filter is attached only while open — a hidden element that
   still declares one keeps compositing the area behind it */
.panel.is-open {
  opacity: 1; visibility: visible; transform: translateY(0);
  pointer-events: auto;
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  backdrop-filter: blur(20px) saturate(1.4);
  transition: opacity 0.26s var(--ease-out), transform 0.26s var(--ease-out), visibility 0s;
}

.panel input[type="search"] {
  width: 100%;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  padding: 0.65rem 0.85rem;
  color: #fff;
  font: inherit; font-size: 0.92rem;
}
.panel input[type="search"]::placeholder { color: rgba(255, 255, 255, 0.45); }
.panel__results { list-style: none; margin: 0.6rem 0 0; padding: 0; max-height: 46vh; overflow-y: auto; }
.panel__results li { margin: 0; }
.panel__results a {
  display: block; padding: 0.55rem 0.7rem; border-radius: 9px;
  color: rgba(255, 255, 255, 0.88); text-decoration: none; font-size: 0.9rem;
}
.panel__results a small { display: block; font-size: 0.74rem; color: rgba(255, 255, 255, 0.5); }
.panel__results a:hover, .panel__results a:focus-visible { background: rgba(255, 255, 255, 0.09); }
.panel__empty { padding: 0.7rem; font-size: 0.86rem; color: rgba(255, 255, 255, 0.55); }
.panel__note { font-size: 0.83rem; line-height: 1.55; color: rgba(255, 255, 255, 0.72); margin: 0.35rem 0 0.7rem; }
.panel h2 { font-size: 0.95rem; margin-bottom: 0.15rem; }
.panel a.panel__mail { color: #c6e39a; font-size: 0.87rem; }

/* ------------------------------------------------------------ sections -- */

.section { padding: 5.5rem 0; }
.section--wash { background: var(--paper-2); }
.section--mist { background: linear-gradient(180deg, var(--paper) 0%, var(--paper-2) 100%); }
.section--dark { background: var(--night); color: #fff; }
.section--dark a { color: #c6e39a; }

.section-head { max-width: 62ch; margin-bottom: 2.6rem; }
.section-head h2, .prose h2 { font-size: clamp(1.75rem, 3.1vw, 2.5rem); margin-bottom: 0.9rem; }
.eyebrow {
  font-size: 0.755rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--green-deep); font-weight: 600; margin-bottom: 0.7rem;
}
.section--dark .eyebrow { color: #a7d16a; }
.lede { font-size: 1.1rem; color: #333c36; }
.section--dark .lede { color: rgba(255, 255, 255, 0.86); }

.split { display: grid; grid-template-columns: 1.5fr 1fr; gap: 3.5rem; align-items: start; }
.grid { display: grid; gap: 1.15rem; }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.6rem;
  transition: transform 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out);
}
.card h3 { font-size: 1.08rem; margin-bottom: 0.5rem; }
.card p { font-size: 0.94rem; color: var(--muted); margin-bottom: 0; }
.card--lift:hover { transform: translateY(-3px); box-shadow: 0 14px 34px rgba(14, 20, 17, 0.09); }
.card-index {
  display: block; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.1em;
  color: var(--green-deep); margin-bottom: 0.85rem;
}

.fact-panel { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.6rem; }
.fact-list, .hero-facts { margin: 0; display: grid; gap: 1rem; }
.fact-list div, .hero-facts div { display: grid; gap: 0.15rem; }
.fact-list dt, .hero-facts dt {
  font-size: 0.73rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); font-weight: 600;
}
.fact-list dd, .hero-facts dd { margin: 0; font-size: 0.97rem; font-weight: 500; }

.proof-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.15rem; }
.proof-strip div { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.4rem; }
.proof-strip strong { display: block; font-size: 1.85rem; font-weight: 600; letter-spacing: -0.03em; }
.proof-strip span { font-size: 0.85rem; color: var(--muted); }

.placeholder {
  display: inline-block;
  font-size: 0.8rem; font-weight: 500;
  color: var(--muted);
  background: repeating-linear-gradient(-45deg, #f4f6f2 0 8px, #ecefe8 8px 16px);
  border: 1px dashed #c3cbbd;
  border-radius: 8px;
  padding: 0.42rem 0.8rem;
}
.section--dark .placeholder,
.panel .placeholder {
  color: rgba(255, 255, 255, 0.82);
  background: repeating-linear-gradient(-45deg, rgba(255,255,255,0.05) 0 8px, rgba(255,255,255,0.02) 8px 16px);
  border-color: rgba(255, 255, 255, 0.25);
}

.button {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.8rem 1.5rem; border-radius: 999px;
  font-size: 0.92rem; font-weight: 500; text-decoration: none;
  transition: transform 0.25s var(--ease-out), background 0.25s var(--ease-out);
  border: none; cursor: pointer;
}
/* Matches the hero's white pill so a visitor meets one button shape across the
   site. On the near-white page background a plain white fill would vanish, so
   the definition comes from a 1.5px ink ring rather than from a colour fill:
   still a white button, still obviously a button. */
.button--deep {
  background: #fff; color: var(--ink);
  box-shadow: inset 0 0 0 1.5px rgba(14, 20, 17, 0.85);
}
.button--deep:hover { background: var(--paper-2); transform: translateY(-1px); }
.button--outline { color: var(--ink); background: #fff; box-shadow: inset 0 0 0 1px var(--line); }
.button--outline:hover { background: var(--paper-2); }
.button:disabled { opacity: 0.55; cursor: not-allowed; transform: none; }
.button-row { display: flex; flex-wrap: wrap; gap: 0.7rem; }

.quality-rule { display: grid; grid-template-columns: 1fr 1.2fr; gap: 3rem; align-items: start; }
.quality-quote { font-size: clamp(1.5rem, 2.7vw, 2.15rem); font-weight: 500; }
.quality-copy p { color: rgba(255, 255, 255, 0.82); }

.work-steps { display: grid; gap: 0.55rem; margin-top: 1.1rem; }
.work-steps div { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.87rem; border-top: 1px solid var(--line); padding-top: 0.55rem; }
.work-steps span { color: var(--muted); }

.role-card .role-label {
  display: inline-block; font-size: 0.73rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 700; color: var(--green-deep);
  background: rgba(120, 176, 56, 0.13); border-radius: 999px; padding: 0.3rem 0.75rem;
  margin-bottom: 0.9rem;
}

.team-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.15rem; }
.person { text-align: center; }
.person__known { font-weight: 400; color: var(--muted); }
.person h3 { font-size: 0.97rem; margin-bottom: 0.25rem; }
.person p { font-size: 0.82rem; color: var(--muted); }
.person a { font-size: 0.8rem; word-break: break-word; }
.portrait-slot {
  aspect-ratio: 1; border-radius: 14px;
  background: repeating-linear-gradient(-45deg, #f4f6f2 0 8px, #ecefe8 8px 16px);
  border: 1px dashed #c3cbbd;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 0.85rem;
}
.portrait-slot span { font-size: 1.25rem; font-weight: 600; color: #9aa695; letter-spacing: 0.05em; }

.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.15rem; }
.timeline-step { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.5rem; }
.timeline-step span { font-size: 0.73rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--green-deep); font-weight: 600; }
.timeline-step h3 { font-size: 1.02rem; margin: 0.6rem 0 0.45rem; }
.timeline-step p { font-size: 0.9rem; color: var(--muted); margin: 0; }

.service-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 2.5rem; }
.service-list article { border-top: 1px solid var(--line); padding: 1.4rem 0; }
.service-list h3 { font-size: 1.03rem; margin-bottom: 0.4rem; }
.service-list p { font-size: 0.92rem; color: var(--muted); margin: 0; }

.form-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 3.5rem; align-items: start; }
.intake-form { background: #fff; border: 1px solid var(--line); border-radius: 14px; padding: 1.8rem; display: grid; gap: 1rem; }
.field { display: grid; gap: 0.35rem; }
.field label { font-size: 0.83rem; font-weight: 500; }
.field input, .field textarea {
  font: inherit; font-size: 0.94rem;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--paper); color: var(--ink);
}
.field textarea { min-height: 110px; resize: vertical; }
.form-note { font-size: 0.82rem; color: var(--muted); margin: 0; }

/* The client page has no form. The route to the branch is an email, so the
   right-hand column names the people behind that address instead of asking the
   visitor to fill anything in. It borrows .intake-form's surface deliberately,
   so it reads as the same piece of furniture rather than a bolted-on box. */
.contact-panel {
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  padding: 1.8rem; display: grid; gap: 1rem; align-content: start;
}
.contact-panel h3 { font-size: 1.05rem; margin: 0; }
.contact-panel p { font-size: 0.92rem; color: var(--muted); margin: 0; }

/* A list, not a card grid: four people with the same title do not need four
   identical boxes, and the visitor is explicitly told not to choose between
   them. The rule between rows carries the separation instead. */
.people-list { list-style: none; margin: 0; padding: 0; display: grid; }
.people-list li {
  display: grid; gap: 0.1rem;
  padding: 0.62rem 0;
  border-top: 1px solid var(--line);
}
.people-list li:last-child { border-bottom: 1px solid var(--line); }
.people-list__name { font-weight: 600; font-size: 0.95rem; color: var(--ink); }
/* The address is shown as text, not hidden behind the name. Someone who wants
   to copy it into their own mail client should not have to hover to find it. */
.people-list__mail { font-size: 0.86rem; color: var(--green-deep); text-decoration: none; word-break: break-word; }
.people-list__mail:hover { text-decoration: underline; }
.people-list__role { font-size: 0.82rem; color: var(--muted); }
.people-list__all { font-size: 0.86rem; color: var(--muted); margin: 0; }
.contact-panel .button { justify-self: start; }

/* ============================================================ case exhibits */
/* Cards deliberately do not repeat one icon-heading-text shape. Each carries a
   tag, a decision-led title, the situation and what was delivered, so the row
   reads as five different pieces of work rather than five instances of a
   template. */
.case-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(288px, 100%), 1fr));
  gap: 1.1rem;
}
.case-card {
  height: 100%;
  display: grid; grid-template-rows: auto auto 1fr auto auto;
  gap: 0.6rem;
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  padding: 1.4rem 1.4rem 1.15rem;
  transition: border-color 0.2s, transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.case-card:hover { border-color: var(--green-deep); transform: translateY(-2px); }
.case-card__tag {
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 600; color: var(--green-deep); margin: 0;
}
.case-card h3 { font-size: 1.12rem; line-height: 1.3; margin: 0; }
.case-card__situation { font-size: 0.9rem; color: var(--muted); margin: 0; }
.case-card__delivered { font-size: 0.86rem; margin: 0; padding-top: 0.55rem; border-top: 1px solid var(--line); }
.case-card__delivered strong { font-weight: 600; }
.case-card__open {
  font-size: 0.88rem; font-weight: 600; text-decoration: none;
  color: var(--green-deep); justify-self: start;
}
.case-card__open:hover { text-decoration: underline; }

/* With JS the details live in the dialog. Without it they stay in the flow,
   which is why this rule is scoped to the class the head script removes. */
html:not(.no-js) .case-details { display: none; }
.case-details { display: grid; gap: 2.5rem; margin-top: 3rem; }

.case-detail__tag {
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 600; color: var(--green-deep); margin: 0 0 0.4rem;
}
.case-detail h3 { font-size: clamp(1.3rem, 2.4vw, 1.75rem); line-height: 1.25; margin: 0 0 0.7rem; max-width: 26ch; }
.case-detail__lede { font-size: 1rem; color: var(--muted); max-width: 62ch; margin: 0 0 1.8rem; }

/* A rebuilt slide. The action title is the point: the source decks headed every
   page with a label ("Executive Summary", "Conclusion") and buried the so-what
   in the body, which is the main thing separating them from senior work. Here
   the claim is the largest thing on the panel and the supporting structure sits
   under it, which is also what makes the exhibit readable at a glance on a
   phone, where a photographed A4 page is not readable at all. */
.slide-panel {
  background: var(--paper); border: 1px solid var(--line); border-radius: 13px;
  padding: clamp(1.1rem, 2.4vw, 1.7rem);
}
.slide-panel__eyebrow {
  font-size: 0.68rem; letter-spacing: 0.11em; text-transform: uppercase;
  font-weight: 700; color: var(--green-deep); margin: 0 0 0.5rem;
}
.slide-panel__title {
  font-size: clamp(1.05rem, 1.9vw, 1.4rem); line-height: 1.32; font-weight: 600;
  margin: 0 0 1.15rem; max-width: 42ch; color: var(--ink);
}
.slide-panel__source {
  font-size: 0.79rem; color: var(--muted); margin: 1.05rem 0 0;
  padding-top: 0.7rem; border-top: 1px solid var(--line); max-width: 64ch;
}
.slide-panel .lens-row li,
.slide-panel .pillar-set li,
.slide-panel .phase-track li { background: #fff; }

.because { list-style: none; margin: 1.05rem 0 0; padding: 0; display: grid; gap: 0.4rem; }
.because li { font-size: 0.88rem; color: var(--muted); }
.because strong { color: var(--green-deep); font-weight: 700; }

/* An original deliverable page, shown as-is. Kept visually distinct from the
   rebuilt panels so a reader can tell which treatment they are looking at, and
   given an escape hatch to full size because a page designed for A4 will not be
   readable inline on a phone no matter what we do to the container. */
.slide-figure { margin: 0 0 1.9rem; }
.slide-figure figcaption {
  font-size: 0.76rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin-bottom: 0.7rem;
}
.slide-figure img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--line); border-radius: 10px; background: #fff;
}
.slide-figure__foot {
  display: flex; flex-wrap: wrap; gap: 0.55rem 0.9rem; align-items: center;
  margin-top: 0.6rem; font-size: 0.8rem; color: var(--muted);
}
.slide-figure__full {
  font-weight: 600; color: var(--green-deep); text-decoration: none;
  border: 1px solid var(--line); border-radius: 999px; padding: 0.28rem 0.75rem;
}
.slide-figure__full:hover { border-color: var(--green-deep); }

.exhibit { margin: 0 0 1.9rem; }
.exhibit figcaption {
  font-size: 0.76rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin-bottom: 0.7rem;
}
.exhibit__note { font-size: 0.83rem; color: var(--muted); margin: 0.75rem 0 0; max-width: 62ch; }

.lens-row { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: 0.6rem; }
.lens-row li { display: grid; gap: 0.25rem; align-content: start; padding: 0.85rem; background: var(--paper); border-radius: 11px; }
.lens-row__n { font-size: 0.7rem; font-weight: 700; color: var(--green-deep); letter-spacing: 0.08em; }
.lens-row strong { font-size: 0.94rem; }
.lens-row span:last-child { font-size: 0.82rem; color: var(--muted); line-height: 1.45; }

.rank-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.rank-list li { display: grid; grid-template-columns: 8.5rem 1fr auto; gap: 0.9rem; align-items: center; }
.rank-list__label { font-size: 0.88rem; font-weight: 600; }
.rank-list__bar { height: 9px; border-radius: 999px; background: var(--line); position: relative; }
.rank-list__bar::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: var(--v);
  border-radius: 999px; background: var(--green);
}
.rank-list__note { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }

.pillar-set { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.pillar-set li {
  display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.85rem;
  padding: 0.95rem 1.1rem; background: var(--paper); border-radius: 11px;
}
.pillar-set__k {
  grid-row: 1 / span 2; align-self: start;
  font-size: 0.72rem; font-weight: 700; color: var(--green-deep); letter-spacing: 0.06em;
  padding-top: 0.16rem;
}
.pillar-set strong { font-size: 0.98rem; }
.pillar-set li > span:last-child { font-size: 0.87rem; color: var(--muted); line-height: 1.5; }

.phase-track { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr)); gap: 0.6rem; }
.phase-track li { display: grid; gap: 0.22rem; align-content: start; padding: 0.9rem; border-top: 2px solid var(--green); background: var(--paper); border-radius: 0 0 11px 11px; }
.phase-track__k { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--green-deep); }
.phase-track strong { font-size: 0.95rem; }
.phase-track span:last-child { font-size: 0.82rem; color: var(--muted); line-height: 1.45; }

.compare { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.compare th, .compare td { text-align: left; padding: 0.62rem 0.7rem; border-bottom: 1px solid var(--line); }
.compare thead th { font-size: 0.74rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.compare tbody th { font-weight: 600; }
.tag-yes, .tag-no { font-size: 0.76rem; font-weight: 600; padding: 0.18rem 0.55rem; border-radius: 999px; white-space: nowrap; }
.tag-yes { background: rgba(120, 176, 56, 0.18); color: var(--green-darkest); }
.tag-no { background: rgba(16, 22, 15, 0.07); color: var(--muted); }

.split-bar { display: flex; gap: 3px; }
.split-bar__seg {
  width: var(--w); display: grid; gap: 0.15rem; align-content: center;
  padding: 1rem 0.85rem; border-radius: 10px; background: var(--green); color: #10160f;
  min-width: 0;
}
.split-bar__seg--mid { background: rgba(120, 176, 56, 0.42); }
.split-bar__seg--min { background: rgba(120, 176, 56, 0.18); }
.split-bar__seg strong { font-size: 1.15rem; }
.split-bar__seg span { font-size: 0.78rem; line-height: 1.3; }

.reco { margin: 0; padding: 1.3rem 1.5rem; background: var(--paper); border-radius: 12px; }
.reco p { margin: 0; font-size: 1.08rem; line-height: 1.5; font-weight: 500; max-width: 48ch; }

/* Native <dialog>: showModal() brings focus trapping, Escape, an inert
   background and focus restoration without reimplementing any of it. */
.case-dialog {
  width: min(100% - 2rem, 62rem); max-height: 88vh; padding: 0;
  border: none; border-radius: 16px; background: #fff; color: var(--ink);
  overflow: hidden;
}
.case-dialog::backdrop { background: rgba(10, 15, 9, 0.55); backdrop-filter: blur(3px); }
.case-dialog__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.7rem 0.9rem 0.7rem 1.1rem;
  border-bottom: 1px solid var(--line); background: var(--paper);
}
.case-dialog__nav { display: flex; align-items: center; gap: 0.35rem; }
.case-dialog__step, .case-dialog__close {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: 40px; padding: 0 0.7rem;
  font: inherit; font-size: 0.85rem; font-weight: 600;
  background: transparent; border: 1px solid var(--line); border-radius: 999px;
  color: var(--ink); cursor: pointer;
}
.case-dialog__step { padding: 0 0.55rem; }
.case-dialog__step:hover, .case-dialog__close:hover { background: #fff; border-color: var(--green-deep); }
.case-dialog__count { font-size: 0.8rem; color: var(--muted); margin: 0 0.4rem; min-width: 4.5rem; text-align: center; }
.case-dialog__body { padding: 1.8rem clamp(1.1rem, 3vw, 2.2rem) 2.2rem; overflow-y: auto; max-height: calc(88vh - 57px); }
.case-dialog__body:focus { outline: none; }
.case-dialog .case-detail { display: block; }

@media (max-width: 640px) {
  .rank-list li { grid-template-columns: 6.5rem 1fr; }
  .rank-list__note { grid-column: 2; font-size: 0.74rem; }
  .split-bar { flex-direction: column; }
  .split-bar__seg { width: auto; }
  .compare { font-size: 0.82rem; }
  .compare th, .compare td { padding: 0.5rem 0.4rem; }
}

.cta-panel { display: grid; grid-template-columns: 1.6fr auto; gap: 2.5rem; align-items: center; }
.testimonial blockquote { margin: 0 0 0.9rem; }
.testimonial footer { font-size: 0.82rem; color: var(--muted); }

/* --------------------------------------------------------------- guide -- */

.guide-body { max-width: 74ch; }
.guide-body h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); margin: 2.8rem 0 0.9rem; }
.guide-body h3 { font-size: 1.1rem; margin: 1.8rem 0 0.6rem; }
.guide-body ul, .guide-body ol { padding-left: 1.15rem; }
.guide-body li { margin-bottom: 0.5rem; }
.guide-body code {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size: 0.87em; background: var(--paper-2); border: 1px solid var(--line);
  border-radius: 5px; padding: 0.1rem 0.35rem;
}
.ledger { width: 100%; border-collapse: collapse; margin: 1.2rem 0; font-size: 0.88rem; }
.ledger th, .ledger td { text-align: left; padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.ledger th { font-size: 0.74rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.table-scroll { overflow-x: auto; }
.notice {
  border-left: 3px solid var(--green);
  background: var(--paper-2);
  padding: 1.1rem 1.3rem;
  border-radius: 0 10px 10px 0;
  margin: 1.4rem 0;
}
.notice p:last-child { margin-bottom: 0; }

/* -------------------------------------------------------------- footer -- */

.site-footer { background: var(--night); color: rgba(255, 255, 255, 0.72); padding: 4rem 0 2rem; }
.footer-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr; gap: 2.5rem; }
.footer-lockup { width: 200px; height: auto; margin-bottom: 1rem; }
.site-footer h2 { font-size: 0.78rem; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 255, 255, 0.55); margin-bottom: 0.9rem; }
.site-footer p { font-size: 0.9rem; }
.footer-links { display: grid; gap: 0.5rem; }
.footer-links a { color: rgba(255, 255, 255, 0.82); text-decoration: none; font-size: 0.9rem; }
.footer-links a:hover { color: #fff; text-decoration: underline; }
.footer-legal {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.6rem;
  margin-top: 3rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 0.82rem; color: rgba(255, 255, 255, 0.66);
}
/* the photography-credit link lives here; the default link green is far too dark
   against the footer's near-black */
.footer-legal a { color: #a7d16a; }
.footer-legal a:hover { color: #c6e39a; }

/* ---------------------------------------------------------- responsive -- */

@media (max-width: 1023px) {
  .main-nav { display: none; }
  .nav-toggle { display: flex; }
  /* scoped to the header — the same two controls are repeated inside the
     mobile menu and must stay visible there */
  .site-header .nav-search,
  .site-header .nav-profile { display: none; }
  .nav-shell { justify-content: space-between; }
  .nav-tools { margin-left: auto; }
}
@media (min-width: 1024px) {
  .mobile-menu { display: none; }
}

/* Between the lg breakpoint and ~1180px the full nav plus a labelled search
   pill is wider than the viewport. Collapse the pill to its icon rather than
   letting the bar overflow. */
@media (min-width: 1024px) and (max-width: 1179px) {
  .nav-search {
    min-width: 0;
    width: 38px;
    padding: 0;
    justify-content: center;
  }
  .nav-search span { display: none; }
  .main-nav { gap: 0.15rem; }
  .main-nav a { padding: 0.5rem 0.62rem; font-size: 0.83rem; }
  .main-nav .nav-page { padding-left: 1.35rem; }
  .main-nav .nav-page::before { left: 0.6rem; }
  .nav-shell { gap: 1rem; }
}

@media (max-width: 900px) {
  .split, .form-layout, .quality-rule, .cta-panel { grid-template-columns: 1fr; gap: 2.2rem; }
  .grid--3, .timeline { grid-template-columns: repeat(2, 1fr); }
  .team-grid { grid-template-columns: repeat(3, 1fr); }
  .proof-strip { grid-template-columns: repeat(2, 1fr); }
  .service-list { grid-template-columns: 1fr; gap: 0; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .cta-panel { justify-items: start; }
}

@media (max-width: 620px) {
  body { font-size: 16px; }
  .section { padding: 3.75rem 0; }
  .grid--2, .grid--3, .timeline, .proof-strip { grid-template-columns: 1fr; }
  .team-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr; gap: 2rem; }
  .hero__inner { padding-block: 2.2rem 2.6rem; }
  .hero h1 { max-width: 100%; }
  .city-switch { gap: 0.5rem; }
  .city-switch__now { order: 3; width: 100%; }
  .btn { flex: 1 1 auto; justify-content: center; }
  .footer-legal { flex-direction: column; }
}

/* Touch pointers get the 44px targets the rest of the repo standardised on;
   the 38px sizing is a mouse-only affordance. */
@media (pointer: coarse) {
  .nav-toggle, .nav-profile { width: 44px; height: 44px; }
  .nav-search { height: 44px; }
  .city-switch__btn { height: 44px; padding: 0 1.2rem; }
  .mobile-menu a { padding-block: 0.85rem; }
}

/* ------------------------------------------------------ reduced motion -- */
/* Every motion this variant adds is switched off here: the Ken Burns pan,
   the parallax (JS also stops writing transforms), the entrance stagger,
   and the crossfade. What remains is a static, correctly-cropped frame.   */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .hero__img {
    animation: none !important;
    transform: none !important;
    object-position: center 45%;
  }
  .hero__media { transform: none !important; }
  .hero__img { transition: none !important; }

  [data-fade],
  .mobile-menu.is-open a,
  .mobile-menu a,
  .mobile-menu__tools,
  .mobile-menu.is-open .mobile-menu__tools {
    animation: none !important;
    opacity: 1;
    filter: none;
    transform: none;
  }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* Windows high-contrast / forced colours: drop the glass, keep the structure */
@media (forced-colors: active) {
  .liquid-glass::before { display: none; }
  .liquid-glass { border: 1px solid ButtonBorder; }
  .hero__blur { display: none; }
}

/* ------------------------------------------------------ applying (students) */
/* Two columns that answer the two questions an applicant actually has: what do
   I have to hand over, and what happens to me afterwards. The selection stages
   are published rather than left to whoever knows a board member, which is the
   fairness argument as much as the reassurance one. */
.apply-layout {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 2.4rem 3rem; margin-top: 2.4rem;
}
.apply-h { font-size: 1.1rem; margin: 0 0 0.5rem; }
.apply-note { font-size: 0.9rem; color: var(--muted); margin: 0 0 1.1rem; max-width: 52ch; }
.apply-note:last-child { margin-bottom: 0; margin-top: 1.1rem; }

.need-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.need-list li {
  display: grid; gap: 0.12rem;
  padding: 0.8rem 1rem; background: #fff;
  border: 1px solid var(--line); border-radius: 11px;
}
.need-list strong { font-size: 0.94rem; }
.need-list span { font-size: 0.85rem; color: var(--muted); line-height: 1.5; }

/* Numbered by position rather than by a hand-typed digit, so inserting or
   removing a stage cannot leave the sequence lying about itself. */
.stage-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; counter-reset: stage; }
.stage-list li {
  display: grid; gap: 0.12rem;
  padding: 0.85rem 1rem 0.85rem 1.1rem;
  background: #fff; border: 1px solid var(--line); border-radius: 11px;
  border-left: 1px solid var(--line);
}
.stage-list__k {
  font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 700; color: var(--green-deep);
}
.stage-list strong { font-size: 0.96rem; }
.stage-list li > span:last-child { font-size: 0.85rem; color: var(--muted); line-height: 1.5; }

.apply-cta { margin: 2.2rem 0 0; display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: start; }

/* A control that is honest about not being ready. <summary> is a button here,
   so the marker has to go: ::-webkit-details-marker for Safari, list-style for
   everyone else. */
.gate { max-width: 46rem; }
.gate summary { list-style: none; cursor: pointer; user-select: none; }
.gate summary::-webkit-details-marker { display: none; }
.gate summary::after {
  content: ""; width: 8px; height: 8px; margin-left: 0.15rem;
  border-right: 1.8px solid currentColor; border-bottom: 1.8px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.25s var(--ease-out);
}
.gate[open] summary::after { transform: rotate(-135deg) translate(-1px, -1px); }
.gate__body {
  margin-top: 0.9rem; padding: 1.1rem 1.3rem;
  background: var(--paper-2); border-radius: 12px;
}
.gate__body p { font-size: 0.9rem; margin: 0 0 0.7rem; max-width: 60ch; }
.gate__body p:last-child { margin-bottom: 0; }

/* Divider between the rebuilt exhibit and the original deliverable pages, so a
   reader is never unsure which one they are looking at. */
.orig-head {
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 700; color: var(--ink); margin: 2.4rem 0 0.3rem;
  padding-top: 1.4rem; border-top: 1px solid var(--line);
}
.orig-note { font-size: 0.86rem; color: var(--muted); margin: 0 0 1.3rem; max-width: 58ch; }
.nowrap { white-space: nowrap; }

/* ------------------------------------------------------------- mail popover */
/* A mailto is silent for anyone without a mail client registered. This gives
   the click a visible result either way: the address to copy, and web routes
   for people who read mail in a browser. */
.mailpop {
  position: absolute; z-index: 120;
  width: min(21rem, calc(100vw - 24px));
  background: #fff; color: var(--ink);
  border: 1px solid var(--line); border-radius: 13px;
  box-shadow: 0 18px 44px rgba(10, 15, 9, 0.18);
  padding: 1rem 1.1rem 0.9rem;
}
.mailpop__x {
  position: absolute; top: 0.35rem; right: 0.5rem;
  background: none; border: none; cursor: pointer;
  font-size: 1.25rem; line-height: 1; color: var(--muted); padding: 0.25rem;
}
.mailpop__x:hover { color: var(--ink); }
.mailpop__addr {
  font-weight: 600; font-size: 0.94rem; margin: 0 1.2rem 0.75rem 0;
  word-break: break-all;
}
.mailpop__row { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.mailpop__copy, .mailpop__alt {
  font: inherit; font-size: 0.82rem; font-weight: 600;
  padding: 0.4rem 0.7rem; border-radius: 999px; cursor: pointer;
  text-decoration: none; border: 1px solid var(--line);
  background: #fff; color: var(--ink);
}
.mailpop__copy { box-shadow: inset 0 0 0 1px rgba(14, 20, 17, 0.55); }
.mailpop__copy:hover, .mailpop__alt:hover { background: var(--paper-2); }
.mailpop__note { font-size: 0.78rem; color: var(--muted); margin: 0.7rem 0 0; }

/* Homepage teaser for the work section. The real exhibits live on the client
   page; this lists what is there and sends you to it, rather than repeating the
   section or leaving reserved tiles standing next to work that now exists. */
.work-teaser { list-style: none; margin: 2rem 0 0; padding: 0; display: grid; gap: 0; }
.work-teaser li {
  display: grid; grid-template-columns: 13rem 1fr; gap: 0.2rem 1.4rem;
  align-items: baseline; padding: 0.85rem 0; border-top: 1px solid var(--line);
  font-size: 1rem;
}
.work-teaser li:last-child { border-bottom: 1px solid var(--line); }
.work-teaser__tag {
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 700; color: var(--green-deep);
}
.work-teaser__cta { margin: 1.6rem 0 0; }
@media (max-width: 620px) { .work-teaser li { grid-template-columns: 1fr; } }

/* Source-slide case studies use the site's existing card and modal language. */
.slide-figure > a { display: block; cursor: zoom-in; }
.slide-figure__foot { justify-content: space-between; }
.case-dialog { width: min(100% - 2rem, 72rem); }

@media (max-width: 620px) {
  .case-dialog { width: calc(100% - 0.75rem); max-height: 96vh; border-radius: 12px; }
  .case-dialog__body { max-height: calc(96vh - 57px); padding-inline: 0.75rem; }
  .slide-figure__foot { display: grid; }
}
