/* RISE - interim site
   Palette and type follow the RISE CI Manual v1.2:
   navy #001730, magenta #f92d84, purple #751094 (gradient only), pink #ffe1ee.
   Headings are set in Circular, the CI typeface; body text is still Figtree.
   assets/fonts/README.md says why, and what has to arrive to finish it. */

/* ---------- Circular, self-hosted ----------

   Legal Disclaimer for Web licence

   Licence to use self-hosted webfonts for displaying dynamic text on
   specified website domains. Our package includes WOFF and WOFF2 font
   formats.

   Should the license page views limit be exceeded the license holder will be
   required to contact Lineto.com for an upgrade.

   It is strictly prohibited to rename the font and to download or use these
   fonts in any other media.

   These Web fonts are licensed exclusively for the use on the following
   domain(s) and their subdomains:

   www.riseregenerate.com (monthly pageviews: <50K)

   (Lineto supplies that notice with the package and asks for it to be copied
   into the stylesheet. The domain it names is not the one this site is served
   from yet: see assets/fonts/README.md.)

   The family name is Lineto's own file name, because the licence forbids
   renaming the font. One face only, Medium 500 upright, so Circular is used
   for headings and labels and `font-synthesis: none` keeps the browser from
   inventing a bold or an italic from it. */

@font-face {
  font-family: "CircularXXWeb-Medium";
  src: url("../fonts/CircularXXWeb-Medium.woff2") format("woff2"),
       url("../fonts/CircularXXWeb-Medium.woff") format("woff");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ---------- Figtree, self-hosted ----------
   Served from this site rather than from Google Fonts. A Google Fonts
   stylesheet makes every visitor's browser contact Google and discloses
   their IP address, which is the most common GDPR complaint against small
   European sites; self-hosting removes the transfer and one DNS round trip.

   Figtree is licensed under the SIL Open Font License 1.1 - see
   assets/fonts/LICENSE.txt. Only the five styles the site actually uses are
   included. If you start using another weight, add its files from the
   @fontsource/figtree package and a block here, or it will fall back to the
   system sans-serif. */

@font-face {
  font-family: "Figtree";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/figtree-latin-ext-400-normal.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: "Figtree";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/figtree-latin-400-normal.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: "Figtree";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/figtree-latin-ext-500-normal.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: "Figtree";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/figtree-latin-500-normal.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: "Figtree";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/figtree-latin-ext-600-normal.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: "Figtree";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/figtree-latin-600-normal.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: "Figtree";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/figtree-latin-ext-700-normal.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: "Figtree";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/figtree-latin-700-normal.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: "Figtree";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/figtree-latin-ext-500-italic.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: "Figtree";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/figtree-latin-500-italic.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;
}

/* The hidden attribute is only `display: none` in the browser's own
   stylesheet, so any author rule that sets `display` beats it. Several lists
   here set `display: flex` or `grid` on their items, and the scripts hide
   items with `el.hidden = true` - the text filters and the "show all"
   collapse both do. Without this they stay on screen while believing
   themselves hidden. `!important` because the whole point is to outrank
   whatever a component set. */
[hidden] { display: none !important; }

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

   Two layers, deliberately:

   1. BRAND CONSTANTS - the CI colours themselves. These never change,
      in any theme. Anything that must be the RISE magenta or the RISE
      navy whatever the page is doing uses these.

   2. SEMANTIC TOKENS - what a colour is *for*: page background, body
      text, the accent, a rule. These are the ones the theme swaps.

   A light theme therefore redefines eight values and nothing else. The
   next stage of CI work can repoint the semantic layer at a new palette
   without touching a single rule in this file.

   Contrast was checked, not eyeballed. Every text pairing below meets
   WCAG AA (4.5:1 for body text):

     dark    white on navy 18.0   magenta on navy 4.9   muted on navy 8.8
     light   navy on white 18.0   accent on white 5.6   muted on white 6.3
                                  accent on blush 4.6   muted on blush 5.2
*/

:root {
  /* 1. brand constants, per the RISE CI Manual v1.2 */
  --navy:    #001730;
  --magenta: #f92d84;
  /* The lighter magenta the dark theme uses for links and for button hover.
     Named here, as a constant, because Q1.5 needs it in a third place: the
     hero, which is on a dark photograph in both themes. */
  --magenta-light: #ff6ba6;
  --purple:  #751094;   /* gradient only, per the manual */
  --blush:   #ffe1ee;

  /* The CI gradient, navy through purple to magenta. One band uses it as a
     surface; it is the same in both themes, because it is artwork rather
     than a theme colour. */
  --gradient: linear-gradient(120deg, #001730, #751094, #f92d84);

  /* The same gradient with a navy scrim over it, for the one band that
     carries text on the gradient. White on the bare magenta end is 3.66:1,
     which fails AA; a quarter-opacity navy over the whole ramp brings the
     worst point to 5.76:1 and leaves the navy end untouched at 18:1. The
     gradient still reads as the gradient: it is darkened, not greyed. */
  --gradient-text-bed:
    linear-gradient(rgba(0, 23, 48, .25), rgba(0, 23, 48, .25)),
    linear-gradient(120deg, #001730, #751094, #f92d84);

  /* 2. semantic tokens - dark theme, which is the default again (Martin,
     31 Oct 2026, replacing R1's light default). :root carries the dark values
     and [data-theme="light"] below overrides them, so the default needs no
     selector of its own.

     `color-scheme` is what makes the browser's own furniture follow: the sort
     select on /publications/, text inputs, scrollbars, the focus ring. Set on
     the tokens rather than on `html` so it moves with the theme. */
  color-scheme: dark;

  --bg:          #001730;   /* = navy */
  --surface:     #16325a;   /* alternate band, cards. R2 raised this from
                               #0a2347, which was 1.15:1 off the navy page,
                               so a band on it did not read as a band. */
  --tint:        #2a1745;   /* small highlights only, never a whole band */
  --ink:         #ffffff;
  --ink-soft:    #a9b6c6;
  /* A lighter magenta than the CI's #f92d84, and deliberately so. R2 raises
     the dark alternate band to #16325a, and #f92d84 on #16325a is 3.50:1,
     which fails AA for text; every link in an alternate band would be below
     the line. #ff6ba6 measures 4.83:1 there and 6.78:1 on the navy page, so
     one value passes on both beds, which is worth more than carrying the
     exact brand hex into link text. The CI magenta is untouched where it is a
     fill rather than text: the gradient, the hero, and the buttons below. */
  --accent:      var(--magenta-light);
  --accent-hover:#ffa3c7;
  --rule:        rgba(255, 255, 255, 0.13);
  --surface-2:   rgba(255, 255, 255, 0.03);   /* row hover, code */
  --field-bg:    #16325a;   /* text inputs; see the light theme for why */
  --tile-edge:   transparent;   /* white logo tiles need no edge on navy */

  /* buttons: white on magenta is only 3.7:1, which fails AA, so the
     dark theme sets navy on magenta (4.9:1) instead */
  --btn-bg:      var(--magenta);
  --btn-ink:     var(--navy);
  --btn-bg-hover:var(--magenta-light);

  /* Corner radius, two values and no more. Before this the stylesheet used
     1px, 2px, 3px and 4px in different places, which is not a choice anyone
     made - it reads as sharp corners that are slightly inconsistent rather
     than as a deliberate edge.

     --radius-img is the larger one, for anything showing a picture:
     portraits, project cards, publication thumbnails, figures, logo tiles.
     Pictures carry their own detail, so a visible curve reads as intentional
     on them where it would look fussy on a 20px button.
     --radius-ui is for controls, cards and panels. */
  --radius-img: 8px;
  --radius-ui:  4px;

  --measure: 40rem;
  --wide:    64rem;

  /* Two stacks. `--font` is the body text; `--font-display` is headings and
     labels, where Circular is licensed and complete enough to use. Circular
     falls back to Figtree, so a heading never lands on a system font. */
  --font: "Figtree", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
          Roboto, sans-serif;
  --font-display: "CircularXXWeb-Medium", "Figtree", ui-sans-serif, system-ui,
          -apple-system, "Segoe UI", Roboto, sans-serif;
}

/* Light theme. Only the semantic layer moves.

   The accent is the CI magenta shifted along the magenta-to-purple axis
   until it is legible as text on white (#f92d84 manages only 3.7:1).
   It is a blend of two CI colours, so it stays in the family rather than
   introducing a sixth colour from nowhere. The pure magenta is still used
   for fills and rules, where contrast rules for text do not apply.

   Blush finally earns its place here as the raised-panel colour - the CI
   manual reserves it for light use. */

:root[data-theme="light"] {
  color-scheme: light;

  --bg:          #ffffff;
  /* R2: blush is no longer a band colour. It was both the alternate band and
     the card surface, so two blush bands in a row merged into one long pink
     stretch and the bands stopped reading as bands. The alternate band is now
     a light grey, and blush survives as --tint for small highlights. */
  --surface:     #f1f3f6;
  --tint:        #ffe1ee;   /* = blush; a tag, a banner, nothing larger */
  --ink:         #001730;   /* = navy */
  --ink-soft:    #52616f;
  --accent:      #be208b;   /* magenta 55% / purple 45% */
  --accent-hover:#9d198f;
  --rule:        rgba(0, 23, 48, 0.14);
  --surface-2:   rgba(0, 23, 48, 0.04);

  /* R2: navy on white, not magenta on white. Magenta is an accent in the CI,
     and a page of magenta buttons spends it. */
  --btn-bg:      #001730;
  --btn-ink:     #ffffff;   /* 17.2:1 */
  --btn-bg-hover:#16325a;

  /* A blush-filled input reads as highlighted or errored rather than empty,
     so fields stay white and are defined by their border instead. */
  --field-bg:    #ffffff;

  /* Logo tiles and figures are white by design - they are the paper the
     artwork sits on. On a white page they need an edge or they dissolve. */
  --tile-edge:   rgba(0, 23, 48, 0.12);
}

/* The toggle only ever sets data-theme explicitly, so there is no stored
   preference to apply before first paint and no flash of the wrong theme. */

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: var(--wide); margin: 0 auto; padding: 0 1.5rem; }
.narrow { max-width: var(--measure); }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 1rem; top: 1rem; background: var(--bg); padding: .5rem .75rem; z-index: 20; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-hover); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------- header ---------- */

.site-header { padding: 1.6rem 0; }

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.brand img { display: block; height: 30px; width: auto; }

/* Navigation.
   The same markup serves two layouts. Wide: groups sit in the bar, their
   panels drop beneath. Narrow: the whole list lives behind the menu button,
   which is what stopped eight links wrapping onto a second line on a phone. */

.site-nav > ul {
  list-style: none; display: flex; align-items: center;
  gap: 1.5rem; margin: 0; padding: 0;
}
.site-nav li { margin: 0; }

.site-nav a,
.sub-toggle {
  font-size: .95rem; font-weight: 500; font-family: inherit;
  color: var(--ink-soft); text-decoration: none;
  padding-bottom: 2px; border: 0; border-bottom: 2px solid transparent;
  background: none; cursor: pointer; line-height: 1.4;
}
.site-nav a:hover,
.sub-toggle:hover { color: var(--ink); }
.site-nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

.sub-toggle::after {
  content: "";
  display: inline-block;
  width: .36em; height: .36em;
  margin-left: .42em;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-.15em) rotate(45deg);
}
.sub-toggle[aria-expanded="true"] { color: var(--ink); }
.sub-toggle[aria-expanded="true"]::after { transform: translateY(.1em) rotate(-135deg); }

.has-sub { position: relative; }

.site-nav .sub {
  list-style: none; margin: 0; padding: .5rem 0;
  position: absolute; top: calc(100% + .7rem); left: -.9rem;
  min-width: 11rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .3);
  z-index: 40;
  display: none;
}
:root[data-theme="light"] .site-nav .sub { box-shadow: 0 8px 28px rgba(0, 23, 48, .13); }
.sub-toggle[aria-expanded="true"] + .sub { display: block; }
.site-nav .sub li { display: block; }
.site-nav .sub a {
  display: block; padding: .4rem .9rem;
  border-bottom: 0;
}
.site-nav .sub a[aria-current="page"] { border-bottom: 0; color: var(--accent); }

/* the menu button - the burger is three rules that become a cross when open */
.nav-toggle {
  display: none; align-items: center; gap: .5rem;
  font: inherit; font-size: .9rem; font-weight: 500;
  color: var(--ink); background: none; border: 0;
  padding: .35rem 0; cursor: pointer;
}
.burger { position: relative; display: block; width: 18px; height: 12px; }
.burger::before, .burger::after {
  content: ""; position: absolute; left: 0; right: 0; height: 2px;
  background: currentColor; border-radius: var(--radius-ui);
  transition: transform .18s ease, top .18s ease;
}
.burger::before { top: 0; box-shadow: 0 5px 0 currentColor; }
.burger::after  { top: 10px; }
.nav-toggle[aria-expanded="true"] .burger::before { top: 5px; box-shadow: none; transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .burger::after  { top: 5px; transform: rotate(-45deg); }

@media (max-width: 52rem) {
  .nav-toggle { display: inline-flex; }

  .site-nav {
    display: none;
    position: absolute; top: 100%; right: 0; left: 0;
    background: var(--surface);
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    padding: .9rem 1.2rem 1.3rem;
    z-index: 40;
  }
  .site-nav.open { display: block; }

  .site-nav > ul { display: block; }
  .site-nav > ul > li { padding: .45rem 0; }

  /* on a phone the groups are always open - a second tap to reach Approach
     is one tap too many when the whole menu is already a deliberate act. The
     group name stays, as a label rather than a control. */
  .sub-toggle {
    pointer-events: none;
    font-size: .75rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: .06em;
    color: var(--ink-soft); padding: 0 0 .25rem;
  }
  .sub-toggle::after { display: none; }
  .site-nav .sub {
    display: block; position: static;
    border: 0; box-shadow: none; background: none;
    padding: 0; min-width: 0;
  }
  .site-nav .sub a { padding: .4rem 0; }

  .site-header .bar { position: relative; }
}

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

/* full-bleed image bed with the headline over it */
.hero-full {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  padding: 6rem 0 3.2rem;
  overflow: hidden;

  /* The band is shaped to the hero photograph rather than to a fixed height,
     so the frame shows close to whole instead of being re-cropped by the
     browser. min-height keeps the text room on a narrow screen, where the
     ratio alone would give a band far too short; max-height stops the hero
     eating a wide screen and pushing everything below the fold. */
  /* width must be stated: with aspect-ratio set and min-height winning on a
     narrow screen, the box otherwise derives its *width* from that height and
     runs 980px wide inside a 390px phone, scrolling the whole page sideways */
  width: 100%;
  aspect-ratio: 2200 / 982;
  min-height: 30rem;
  max-height: 78vh;
}

.hero-media { position: absolute; inset: 0; z-index: -2; background: var(--navy); }

.hero-media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* .42 was tuned for photographs. The trajectory chart is fine detail on a
     pale slide and disappears at that level, so the bed sits a little
     brighter and the gradient below does the work of keeping text legible. */
  opacity: .44;
  transition: opacity 1.6s ease-in-out;
}
.hero-media img.off { opacity: 0; }

/* R9: the CI gradient over the photograph, in both themes, with a scrim under
   it so the text stays legible whatever frame is showing.

   Two layers, and both are load-bearing:

   - The scrim, listed first so it sits on top. It is strongest at the bottom,
     which is where the headline, standfirst and buttons are.
   - The CI gradient at 85%, navy through purple to magenta, running left to
     right. Navy is on the left, which is the side the text sits on - that is
     not a coincidence, because the headline and the links in this hero are
     magenta, and magenta type over the magenta end of the gradient would
     vanish.

   Both measured from the rendered page rather than reasoned about: see the
   contrast figures in the R8-R9 pull request. */
/* The default: a navy scrim with one soft magenta glow at the lower left.
   Flat shading, no gradient ramp across the frame. */
.hero-full::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to top, rgba(0,23,48,.96) 8%, rgba(0,23,48,.55) 48%, rgba(0,23,48,.72) 100%),
    radial-gradient(52rem 30rem at 14% 112%, rgba(249,45,132,.34) 0%, rgba(249,45,132,0) 64%);
}

/* `surfaces: gradient` in _config.yml. Navy is on the left, which is the side
   the text sits on - not a coincidence: the headline and links in this hero
   are magenta, and magenta type over the magenta end of the ramp would
   vanish. The scrim above it is strongest at the bottom, where the text is.
   Measured from the rendered pixels of all three hero frames, worst case:
   headline 3.64:1 (large text, needs 3), standfirst 10.25:1, link 4.63:1. */
body.surfaces-gradient .hero-full::after {
  background:
    linear-gradient(to top, rgba(0,23,48,.90) 0%, rgba(0,23,48,.58) 46%, rgba(0,23,48,.50) 100%),
    linear-gradient(120deg,
      rgba(0,23,48,.85) 0%,
      rgba(117,16,148,.85) 55%,
      rgba(249,45,132,.85) 100%);
}

/* The hero keeps its dark image bed in both themes - a pale wash over the
   same photographs would leave dark text fighting the imagery. So its text
   is pinned light here rather than inherited from --ink, which flips. */
.hero-inner { width: 100%; color: #ffffff; }

/* The headline is the brand magenta in BOTH themes. The bed behind it stays
   navy either way, where #f92d84 reaches 4.9:1 - comfortably past the 3:1
   large text needs - so there is no reason for the two themes to differ here,
   and the asymmetry only existed because light mode asked for colour first.

   The link uses the same brand magenta rather than the pale pink that used to
   sit here, which was a tint invented for legibility and belonged to no
   palette. It is underlined, so colour is not the only thing marking it. */
.hero-full h1 { color: var(--magenta); }
.hero-full a { color: var(--magenta); }
.hero-full a:hover { color: #ffffff; }

/* Same reasoning for the pull-quote: it is large text, so the brand magenta
   clears the 3:1 bar on white, and it gives the light pages a colour moment
   mid-scroll instead of a long run of navy on white. */
:root[data-theme="light"] .claim { color: var(--magenta); }

.hero-full h1 {
  font-size: clamp(1.9rem, 5.6vw, 3.3rem);
  /* 500, not 600: Circular has one cut here and `font-synthesis: none` is
     set, so a 600 would quietly render as 500 anyway. Saying 500 means the
     stylesheet describes what actually happens. */
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin: 0 0 1.2rem;
  /* the headline carries its own line break; this only stops a long
     replacement headline from running the full width of the page */
  max-width: 26ch;
  text-wrap: pretty;
}

.hero-full .standfirst {
  font-size: clamp(1.05rem, 2vw, 1.22rem);
  color: #cdd8e4;   /* fixed: the hero bed is dark in both themes */
  max-width: 54ch;
  margin: 0;
}

.hero-cta { margin: 1.4rem 0 0; font-weight: 500; }

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

/* ---------- sections ---------- */

main { padding-bottom: 1rem; }

/* R3: band padding 4rem on a phone, 6rem from 48rem up. R2: no rule between
   bands. The border was there because neighbouring bands often shared a
   background and nothing else separated them; now that no two adjacent bands
   share one, the colour change is the separation and a line on top of it
   reads as a seam. */
.band { padding: 4rem 0; }
@media (min-width: 48rem) {
  .band { padding: 6rem 0; }
}

/* Only the vertical padding. The shorthand used to set left and right to 0,
   which overrode .wrap's own 1.5rem gutter on the same element, so on a phone
   every inner page's title and lede sat hard against the screen edge while the
   body text below them did not. */
.page-head { padding-top: 2.5rem; padding-bottom: 1rem; }

/* R3: headings are set in Circular and sized on the CI's scale. The CI steps
   H1 24 / H2 18 / body 10 pt, a ratio of 1.8 between H2 and body; this site
   had 1.2 (h2 at 20.8px on a 17px body), which is why section heads barely
   registered. Weight 500 because that is the CI's heading weight and the only
   Circular cut licensed here; `font-synthesis: none` stops a browser faking a
   heavier one. */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-synthesis: none;
  letter-spacing: -0.022em;
}

h1 { font-size: clamp(2.25rem, 5vw, 3rem); line-height: 1.08; }

.page-head h1 {
  font-size: clamp(2.25rem, 5vw, 3rem);
  margin: 0 0 .8rem;
}

h2 { font-size: clamp(1.75rem, 3vw, 2.125rem); line-height: 1.1; margin: 0 0 1rem; }
h3 { font-size: 1.15rem; margin: 2rem 0 .5rem; }

p, li { margin: 0 0 1.05rem; }
ul, ol { padding-left: 1.25rem; }
li { margin-bottom: .5rem; }
strong { font-weight: 600; }

.lede {
  font-size: 1.18rem;
  line-height: 1.5;
  font-weight: 500;
  letter-spacing: -0.012em;
  /* The page head spans the full 64rem on an Article page (R4), so the title
     lines up with the text column below it instead of being indented 192px
     past it. The lede is prose, though, and has no business being 1,024px
     wide, so it keeps the reading measure while its container does not. */
  max-width: var(--measure);
}

.muted { color: var(--ink-soft); }
.small { font-size: .86rem; }

.eyebrow {
  color: var(--accent);
  font-weight: 600;
  font-size: .88rem;
  margin: 0 0 .6rem;
}

hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

/* pull-quote used once per page at most */
.claim {
  font-size: clamp(1.25rem, 2.6vw, 1.6rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.32;
  max-width: 28ch;
  margin: 0;
  padding-left: 1.1rem;
  border-left: 3px solid var(--accent);
}

/* ---------- three-column grid (the scaling logic, footer) ---------- */

.cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2.2rem;
}

@media (max-width: 48rem) { .cols { grid-template-columns: 1fr; gap: 1.8rem; } }

.cols h3 { margin-top: 0; }

.cols .num {
  display: block;
  color: var(--accent);
  font-weight: 600;
  font-size: .88rem;
  margin-bottom: .35rem;
}

/* ---------- entry lists (projects, publications, news) ---------- */

.entrylist ul { list-style: none; padding: 0; margin: 0 0 1.5rem; }

.entrylist li {
  padding: .9rem 0;
  border-top: 1px solid var(--rule);
  font-size: .95rem;
  line-height: 1.5;
  margin: 0;
}

.entrylist li a { overflow-wrap: anywhere; }
.entrylist h2 { margin: 2.6rem 0 .4rem; }

.entrylist .self,
.entrylist .org {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* ---------- news ---------- */

.posts { list-style: none; padding: 0; margin: 0; }

.posts li {
  padding: 1.1rem 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

/* year dividers on the archive */
.posts li.year-break {
  border-top: 0;
  padding: 2rem 0 .2rem;
}
.posts li.year-break:first-child { padding-top: 0; }
.posts li.year-break span {
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink-soft);
}

.posts .meta {
  display: flex;
  align-items: center;
  gap: .7rem;
  flex-wrap: wrap;
  margin: 0 0 .3rem;
  font-size: .82rem;
  font-weight: 500;
}

/* what kind of update this is */
.posts .type {
  color: var(--accent);
  text-transform: lowercase;
}

.posts .date { color: var(--ink-soft); }

.posts .pin {
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  padding: .05rem .4rem;
  font-size: .74rem;
}

.posts h3 { margin: 0 0 .3rem; font-size: 1.08rem; }
.posts h3 a { color: var(--ink); text-decoration: none; }
.posts h3 a:hover { color: var(--accent); }
.posts .sum { margin: 0; color: var(--ink-soft); font-size: .95rem; }

/* ---------- the Article template (R4) ----------

   An inner page used to be a single 40rem column centred in a 64rem wrap, so
   on a 1,280px screen it was a narrow ribbon with 320px of nothing either
   side, next to a home page that used the full width. The text column stays
   at the reading measure, because that is a reading decision and has not
   changed; what changes is that the facts move out of the flow and into a
   column beside it, which uses the space instead of leaving it empty.

   The sidebar comes AFTER the main column in the markup, and sits to its
   right. Both of those are deliberate:

   - After, because the facts are reference material and the text is what the
     reader came for. That was settled when the facts box was moved below the
     project text, and it still holds for anyone reading in source order: a
     screen reader, a phone, a printout.
   - Right, so that visual order and DOM order agree. A sidebar placed left
     while coming second in the markup reads in one order and tabs in
     another, which is the kind of mismatch that only ever bites the people
     least able to work around it.

   Below 64rem the grid collapses to one column and the facts fall below the
   text, which is where they already were.  */
.article { display: grid; gap: 2.2rem; }
.article-main { min-width: 0; }

@media (min-width: 64rem) {
  .article {
    grid-template-columns: minmax(0, var(--measure)) 18rem;
    gap: 4rem;
    align-items: start;
  }
  /* `top` clears the sticky header; `max-height` plus its own scrollbar stops
     a long facts list from being cut off on a short window. */
  .article-side {
    position: sticky; top: 6rem;
    max-height: calc(100vh - 8rem);
    overflow-y: auto;
  }
}

/* In the sidebar the facts are the column, not a box inside it: the border
   and padding that set them apart mid-text would just draw a box around a
   box. A single rule down the left edge ties them to the text they belong
   to. */
@media (min-width: 64rem) {
  .article-side .facts {
    margin: 0;
    border: 0; border-left: 2px solid var(--rule);
    border-radius: 0;
    padding: .2rem 0 .2rem 1.1rem;
    background: none;
    /* One column, label above value. The two-column form needs 8rem for the
       label before the value even starts, which leaves about 170px in an
       18rem sidebar: "Analysis of Life Cycle Greenhouse Gas Emissions and
       Removals of EU Buildings" came out six lines tall. Stacked, every
       value gets the full width of the column. */
    display: block;
    font-size: .88rem;
  }
  .article-side .facts dt {
    font-size: .75rem; font-weight: 600;
    letter-spacing: .04em; text-transform: uppercase;
    margin-top: .95rem;
  }
  .article-side .facts dt:first-child { margin-top: 0; }
  .article-side .facts dd { margin: .12rem 0 0; line-height: 1.45; }
}

.post-body { max-width: var(--measure); }

/* One rule for every heading in a body, so a section that follows a list, a
   figure or a facts box is never flush against it. "Collaborators" and
   "Client" used to sit directly under the block above. */
.post-body h2 { margin-top: 2.5rem; }
.post-body > :first-child { margin-top: 0; }
.post-date { color: var(--ink-soft); font-size: .9rem; font-weight: 500; }
.post-type { color: var(--accent); font-weight: 600; font-size: .9rem; }

/* ---------- project list and project pages ---------- */

.projects,
.openings { list-style: none; padding: 0; margin: 0 0 1rem; }

.projects li,
.openings li {
  padding: 1.15rem 0;
  border-top: 1px solid var(--rule);
  margin: 0;
}

.projects .meta,
.openings .meta {
  display: flex; gap: .7rem; flex-wrap: wrap;
  margin: 0 0 .3rem; font-size: .82rem; font-weight: 500;
}
.projects .date,
.openings .date { color: var(--accent); }
.projects .funder,
.openings .funder { color: var(--ink-soft); }

.projects h3,
.openings h2 { margin: 0 0 .3rem; font-size: 1.08rem; }
.projects h3 a,
.openings h2 a { color: var(--ink); text-decoration: none; }
.projects h3 a:hover,
.openings h2 a:hover { color: var(--accent); }

.projects .sum,
.openings .sum { margin: 0; color: var(--ink-soft); font-size: .95rem; }
.projects .role { margin: .3rem 0 0; font-size: .88rem; color: var(--ink-soft); }

/* key facts on a project page */
.facts {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: .1rem 1.2rem;
  margin: 0 0 2rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-size: .93rem;
}
.facts dt { color: var(--ink-soft); font-weight: 500; }
.facts dd { margin: 0; }
.facts dd a { overflow-wrap: anywhere; }

/* The facts box now sits below the project text rather than above it, so it
   needs air at the top and reads as a summary of what was just described. */
.facts-after { margin: 2.6rem 0 2rem; }

/* An opening states its terms before the prose, because the pay line is
   required by law and should not be something a reader has to hunt for. */
.opening-facts { margin: 0 0 2rem; }

/* The "Show all" control on a collapsed list. A button, not a link, because
   it changes what is on the page rather than going anywhere. */
.more-btn {
  display: inline-block; margin: .6rem 0 0;
  padding: .35rem 0; border: 0; background: none;
  font: inherit; font-size: .88rem; font-weight: 500;
  color: var(--accent); cursor: pointer;
}
.more-btn:hover { text-decoration: underline; }

/* Footer navigation (L17). The nav column holds the groups side by side on a
   wide screen and wraps on a narrow one.
   The flex row that used to be here is gone: R5 makes `.foot-nav` a
   `display: contents` wrapper so its groups become columns of the footer
   grid itself, which is declared further down with the rest of the grid. */
.foot-navhead {
  margin: 0 0 .5rem; font-size: .8rem; font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase; color: var(--ink-soft);
}
/* every list in the footer: the nav groups and the Follow column alike, so
   the one outside .foot-nav does not sprout bullets */
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin: 0 0 .35rem; }
.site-footer .foot-navhead + ul a,
.foot-nav a, .site-footer .foot-navhead + p a { font-size: .92rem; }
.opening-facts dd strong { color: var(--ink); }

/* The citation lives as the last row of the facts box. It is smaller than the
   rows above because it is reference material rather than a fact to scan, and
   the copy button the script adds sits under it on its own line. */
.facts-cite { font-size: .86rem; line-height: 1.5; color: var(--ink-soft); }
.facts-cite .citeas-copy { display: block; margin-top: .55rem; }

/* ---------------------------------------------------- project page header */
/* The project image sits behind the title. It reuses the home hero's frame
   and gradient, but shorter, because a project head carries a title, a lede
   and a row of tags rather than a full standfirst and a call to action. */
.proj-hero {
  aspect-ratio: auto;
  min-height: 22rem;
  max-height: 34rem;
  padding: 5rem 0 2.4rem;
}
/* White, not the magenta the home hero uses: a project title is long and sits
   over a busier frame, where magenta on photograph loses too much contrast. */
.proj-hero h1 { color: #ffffff; font-size: clamp(1.75rem, 1.1rem + 2.1vw, 2.9rem); }
.proj-hero .lede { color: rgba(255,255,255,.92); max-width: 44rem; }
.proj-hero .post-date { margin-bottom: .6rem; }
.proj-hero .tags { margin-top: 1rem; }
/* Brighter than the home hero's .44: there the image is a bed behind a short
   headline, here it is the project's own picture and should read as one. */
.proj-hero .hero-media img { opacity: .58; object-position: top left; }

/* The caption belongs to the image but cannot sit on top of it, so it goes
   directly underneath, where an attribution line is still clearly attached. */
.hero-cap {
  margin: .75rem auto 0;
  font-size: .82rem; line-height: 1.5;
  color: var(--ink-soft);
}

@media (max-width: 34rem) {
  .facts { grid-template-columns: 1fr; gap: 0; }
  .facts dt { margin-top: .7rem; }
}

/* ---------- tags, jump links ---------- */

.tags { display: flex; flex-wrap: wrap; gap: .4rem; margin: .9rem 0 0; }

.tag {
  font-size: .78rem; font-weight: 500;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  padding: .12rem .5rem;
}

.jump {
  font-size: .88rem; font-weight: 500;
  margin: 0 0 1.4rem;
}

/* ---------- embedded documents ---------- */

.doc { margin: 1.8rem 0 2.4rem; }

.doc-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: .85rem 1rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-bottom: 0;
  border-radius: var(--radius-ui) var(--radius-ui) 0 0;
}

.doc-title { margin: 0; font-weight: 600; font-size: .98rem; }
.doc-note { margin: .1rem 0 0; font-size: .84rem; color: var(--ink-soft); }

.doc-dl {
  flex: 0 0 auto;
  padding: .45rem .9rem;
  background: var(--btn-bg);
  color: var(--btn-ink);
  font-size: .86rem; font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius-ui);
}
.doc-dl:hover { background: var(--btn-bg-hover); color: var(--btn-ink); }

.doc-view {
  border: 1px solid var(--rule);
  border-radius: 0 0 var(--radius-ui) var(--radius-ui);
  background: #fff;
  overflow: hidden;
}
.doc-view object { width: 100%; height: 100%; display: block; border: 0; }

.doc-fallback {
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--ink-soft);
  background: var(--surface);
}

/* phones rarely render an inline PDF - show the bar, hide the dead frame */
@media (max-width: 40rem) {
  .doc-view { display: none; }
  .doc-bar { border-bottom: 1px solid var(--rule); border-radius: var(--radius-ui); }
}

.doc-line {
  padding: .7rem 0;
  border-top: 1px solid var(--rule);
  margin: 0;
  font-size: .95rem;
}

/* ---------- figures ---------- */

.fig { margin: 2rem 0; }

.fig-link { display: block; }

.fig img {
  display: block; width: 100%; height: auto;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
}

.fig figcaption {
  margin-top: .6rem;
  font-size: .86rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

.fig-num { font-weight: 600; color: var(--ink); margin-right: .35rem; }
.fig-src { display: block; margin-top: .15rem; font-size: .8rem; }
.fig-dl { margin-left: .5rem; font-size: .8rem; font-weight: 500; }

/* ---------- people ---------- */

/* One size of card, whatever is in it. `grid-auto-rows: 1fr` makes every row
   the height of the tallest card in the grid, and the flex column inside each
   card then lets the portrait-and-text block take the slack, so the links row
   sits on the same line across the whole grid. Without both halves the cards
   step up and down as the roles wrap to one, two or three lines. */
.people {
  list-style: none; padding: 0;
  display: grid;
  /* 21rem, not 15. At 15rem the text beside the portrait is about 150px
     wide, which breaks a role like "Head of Office, Stakeholder and
     Communications Manager" over three lines and makes the whole grid as tall
     as its longest job title. 21rem gives two columns in a 64rem band, and a
     text column wide enough for those roles to run to one or two lines. */
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
  grid-auto-rows: 1fr;
  gap: .8rem;
  margin: 1.2rem 0 0;
}
.people li { margin: 0; }   /* padding and border are set with the card, below */

/* One column, so there is nothing beside a card for it to line up with, and
   the equal row height only adds dead space under the shortest card. */
@media (max-width: 40rem) {
  .people { grid-auto-rows: auto; }
}

/* Top-aligned, not centred. The cards are all one height now, so centring
   would put each name at a different point down its card depending on how
   many lines the role runs to, and a row of names at five different heights
   reads as five misaligned cards. Aligned to the top, the portraits and the
   names line up across the row and the slack falls at the bottom. */
.people a {
  display: flex; align-items: flex-start; gap: .85rem;
  padding: .7rem .85rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  text-decoration: none;
  color: var(--ink);
}
.people a:hover { border-color: var(--accent); }

/* Person cards. A square portrait, not a circle: a 1:1 crop of a headshot
   survives squaring, and a circle eats the shoulders. */
.people .pc-face {
  width: 4.5rem; height: 4.5rem; flex: 0 0 auto;
  border-radius: var(--radius-ui); object-fit: cover; object-position: top center;
  background: var(--surface);
  overflow: hidden;
}
/* The portrait is wrapped (see `.pc-shot` below), so the <img> fills the
   wrapper and the wrapper carries the size and the corners. */
.people .pc-shot img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
}

/* Shown instead of a portrait while a person has no photograph. Never a
   stock face: see docs/06. */
.people .pc-initials {
  display: flex; align-items: center; justify-content: center;
  font-size: 1.25rem; font-weight: 600; letter-spacing: .03em;
  color: var(--ink-soft); border: 1px solid var(--rule);
}

/* The links row sits outside the card's own link, so it is its own set of tab
   stops rather than a link inside a link. It therefore has to opt out of the
   card styling that `.people a` applies: those rules make a bordered block,
   which is right for the card and wrong for a row of small links. */
.pc-links {
  display: flex; flex-wrap: wrap; justify-content: flex-start;
  gap: .85rem; margin: .45rem 0 0; padding: 0 .85rem;
}
.people .pc-links a {
  display: inline; padding: 0; gap: 0;
  /* and not the `flex: 1 1 auto` the card's own link carries, which would
     make each of these grow to fill the row and strand them a card's width
     apart instead of sitting together on the left */
  flex: 0 0 auto;
  background: none; border: 0; border-radius: 0;
  font-size: .8rem; color: var(--accent); text-decoration: none;
}
.people .pc-links a:hover { text-decoration: underline; }

/* The card and its links read as one block. */
.people li {
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  padding-bottom: .55rem;
}
.people li:hover { border-color: var(--accent); }
.people a { background: none; border: 0; border-radius: 0; flex: 1 1 auto; }
.people a:hover { border-color: transparent; }

.person-txt { min-width: 0; }
.people .who { display: block; font-weight: 600; font-size: .95rem; }
.people .what { display: block; color: var(--ink-soft); font-size: .85rem; }

/* a person's own page */
.person-head { display: flex; gap: 1.5rem; align-items: flex-start; flex-wrap: wrap; }
.person-head > div { flex: 1 1 18rem; min-width: 0; }

.person-head .portrait {
  width: 8.5rem; height: 8.5rem; flex: 0 0 auto;
  border-radius: var(--radius-ui); object-fit: cover; object-position: top center;
  background: var(--surface);
  display: block; overflow: hidden;
}
.person-head .portrait img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
}

/* Portraits in black and white, from `portraits: bw` in _config.yml by way of
   a class on <body>. It is done here rather than in the image files so the
   originals stay in colour: a portrait gets reused in a slide, a press note or
   a partner's page, and a greyscale JPEG cannot be un-greyed.
   `grayscale(1)` and not `saturate(0)`: grayscale weights the channels by
   luminance, so skin tones keep their tonal separation instead of flattening.
   Only portraits. Project and publication imagery is untouched. */
body.portraits-bw .people .pc-face,
body.portraits-bw .people .pc-shot img,
body.portraits-bw .person-head .portrait,
body.portraits-bw .person-head .portrait img {
  filter: grayscale(1);
}

.person-links { margin: .9rem 0 0; font-size: .9rem; display: flex; flex-wrap: wrap; gap: 1rem; }

/* the lessons list, ported from the personal site */
.lessons { margin: 1.5rem 0 0; }
.lessons dt { font-weight: 600; font-size: 1rem; margin-top: 1.7rem; }
.lessons dt:first-child { margin-top: 0; }
.lessons dd {
  margin: .3rem 0 0;
  padding-left: .95rem;
  border-left: 2px solid var(--rule);
  color: var(--ink-soft);
}

/* ---------- partners ---------- */

.partners {
  list-style: none; padding: 0;
  display: flex; flex-wrap: wrap; gap: .6rem;
  margin: 1.1rem 0 0;
}
.partners li { margin: 0; }

.partners a, .partners .unknown {
  display: flex; flex-direction: column;
  width: 11.5rem;
  background: #fff;
  border-radius: var(--radius-ui);
  text-decoration: none;
  color: var(--navy);
  overflow: hidden;
}
.partners a:hover { outline: 2px solid var(--accent); outline-offset: -2px; }

/* On a phone the tiles are a fixed 11.5rem, and two of them plus the gap are
   wider than the content column, so the flex row above collapsed to a single
   tall column of logos (Martin, 31 Oct 2026). Below 34rem the list becomes a
   two-column grid and the tiles give up their fixed width to fit it. */
@media (max-width: 34rem) {
  .partners { display: grid; grid-template-columns: repeat(2, 1fr); }
  /* `height: 100%` as well as `width: auto`: the grid stretches the <li>, but
     the tile is the <a> inside it, and without this a two-line name next to a
     one-line name leaves the shorter tile floating with a gap beneath it. */
  .partners a, .partners .unknown { width: auto; height: 100%; }
}

.partners .pa-logo {
  display: flex; align-items: center; justify-content: center;
  height: 3.6rem; padding: .7rem .9rem .4rem;
}
.partners .pa-logo img { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; }

.partners .pa-name {
  padding: 0 .8rem;
  font-size: .86rem; font-weight: 600; line-height: 1.25;
  text-align: center;
}
.partners .pa-where {
  padding: .15rem .8rem .7rem;
  font-size: .74rem; color: #5d6368; line-height: 1.3;
  text-align: center;
}
.partners .unknown {
  background: var(--surface); color: var(--ink-soft); font-weight: 400;
  padding: 1rem; text-align: center;
}

/* ---------- publication links ---------- */

/* Bold, not semibold: the author list is whole again and runs to 32 names on
   some entries, so the title has to be the thing the eye lands on. */

.projects.compact li { padding: .8rem 0; }

/* ---------- call to action ---------- */

.cta {
  display: inline-block;
  margin-top: .6rem;
  padding: .7rem 1.3rem;
  background: var(--btn-bg);
  color: var(--btn-ink);
  font-weight: 600;
  font-size: .95rem;
  text-decoration: none;
  border-radius: var(--radius-ui);
}
.cta:hover { background: var(--btn-bg-hover); color: var(--btn-ink); }

/* ---------- footer ---------- */

.site-footer {
  border-top: 1px solid var(--rule);
  margin-top: 3rem;
  padding: 2.4rem 0 3rem;
  font-size: .92rem;
}

/* R5: one row of five. The nav used to be a single 228px column holding
   every group, so About and Work wrapped above Media and the footer read as
   a ragged block. `display: contents` on the nav takes it out of the layout
   and promotes its groups to grid items, so each becomes its own column, and
   the column count here has to match: one wide column plus one per nav group
   plus Follow.
   (The nav element keeps its aria-label and its landmark; `display: contents`
   dropped elements from the accessibility tree in browsers of a few years
   ago, but the links sit inside the footer's own contentinfo landmark either
   way, so nothing becomes unreachable.) */
.foot-grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(4, 1fr);
  gap: 2rem;
  align-items: start;
  margin-bottom: 1.6rem;
}
.foot-nav { display: contents; }

@media (max-width: 48rem) {
  .foot-grid { grid-template-columns: 1fr 1fr; gap: 1.4rem 1.2rem; }
  /* the address block spans both columns; the rest pair up beneath it */
  .foot-grid > :first-child { grid-column: 1 / -1; }
}

/* The bottom bar: the legal line on the left, the initiatives on the right,
   on one row where there is room and stacked where there is not. */
.foot-bar {
  display: flex; flex-wrap: wrap; gap: .4rem 1.4rem;
  justify-content: space-between; align-items: baseline;
  padding-top: 1.2rem;
  border-top: 1px solid var(--rule);
}
.foot-bar p { margin: 0; }

.foot-mark { height: 24px; width: auto; display: block; margin-bottom: .7rem; }
.site-footer p { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

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

.tabs { margin-top: 2.6rem; }

.tabbar { display: none; }

.tabs-on .tabbar {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: 1.4rem;
  border-bottom: 1px solid var(--rule);
  padding-bottom: .55rem;
}

.tabbar button {
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -.6rem;
  padding: .35rem .7rem .55rem;
  font: inherit;
  font-size: .95rem;
  font-weight: 500;
  color: var(--ink-soft);
  cursor: pointer;
}
.tabbar button:hover { color: var(--ink); }
.tabbar button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
.tabbar button span {
  display: inline-block;
  margin-left: .35rem;
  font-size: .78rem;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* with tabs on, each panel supplies its own heading only in the "all" view */
.tabs-on [data-panel] > h2:first-child { font-size: 1.05rem; color: var(--ink-soft); }

.more { margin: 1rem 0 0; font-size: .9rem; font-weight: 500; }

/* ---------- filter ---------- */

.filter {
  display: flex;
  align-items: center;
  gap: .8rem;
  margin: 0 0 1.4rem;
}

.filter input {
  flex: 1 1 auto;
  min-width: 0;
  padding: .55rem .8rem;
  font: inherit;
  font-size: .95rem;
  color: var(--ink);
  background: var(--field-bg);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
}
.filter input::placeholder { color: var(--ink-soft); }
.filter input:focus { border-color: var(--accent); outline: none; }
.filter .count { flex: 0 0 auto; font-variant-numeric: tabular-nums; }

/* ---------- deliverable cards ---------- */

.deliverables {
  list-style: none; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: .8rem;
  margin: 1.3rem 0 0;
}
.deliverables li { margin: 0; }

.deliverables a {
  display: flex; gap: .9rem; height: 100%;
  padding: .8rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  text-decoration: none;
  color: var(--ink);
}
.deliverables a:hover { border-color: var(--accent); }

.dl-thumb {
  flex: 0 0 auto;
  width: 3.4rem; height: 4.6rem;
  background: #fff;
  border-radius: var(--radius-ui);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.dl-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: top left; display: block; }
.dl-noimg { font-size: .7rem; font-weight: 700; color: var(--ink-soft); letter-spacing: .04em; }

.dl-txt { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.dl-title { font-weight: 600; font-size: .95rem; line-height: 1.35; }
.dl-note { font-size: .82rem; color: var(--ink-soft); }
.dl-open { margin-top: auto; font-size: .82rem; color: var(--accent); font-weight: 500; }

/* ---------- listing table ---------- */

table.listing { width: 100%; border-collapse: collapse; font-size: .93rem; }
table.listing th {
  text-align: left; padding: .5rem .7rem;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-soft); font-size: .8rem; font-weight: 600;
}
table.listing td {
  padding: .6rem .7rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}
/* The logo alone did not say who a partner was - several of these marks are
   symbols rather than wordmarks - so the name sits beside it. */
table.listing td.t-partner { width: 19rem; }

table.listing td.t-partner a {
  display: flex; align-items: center; gap: .8rem;
  text-decoration: none; color: inherit;
}

table.listing td.t-partner .t-logo {
  flex: 0 0 6.5rem;
  display: flex; align-items: center; justify-content: center;
  height: 3rem; padding: .35rem .5rem;
  background: #fff; border-radius: var(--radius-ui);
}
table.listing td.t-partner .t-logo img { max-height: 100%; max-width: 100%; width: auto; }

table.listing td.t-partner .t-name { font-weight: 600; line-height: 1.25; }
table.listing td.t-partner a:hover .t-name { color: var(--accent); }
table.listing tr:hover td { background: var(--surface-2); }

@media (max-width: 34rem) {
  table.listing td.t-partner { width: auto; }
  table.listing td.t-partner a { flex-direction: column; align-items: flex-start; gap: .4rem; }
  table.listing th:nth-child(2), table.listing td:nth-child(2) { display: none; }
}

/* ---------- key findings on a project page ---------- */

/* authored as a markdown list inside <div class="findings" markdown="1"> */
.findings ul { list-style: none; padding: 0; margin: 1.3rem 0 1.6rem; }

.findings li {
  position: relative;
  padding-left: 1.4rem;
  margin-bottom: 1.1rem;
}

.findings li::before {
  content: "";
  position: absolute; left: 0; top: .62em;
  width: 7px; height: 7px;
  background: var(--accent);
  border-radius: var(--radius-ui);
}

/* the lead sentence carries the point; the rest explains it */
.findings li > strong:first-child,
.findings li > p:first-child > strong:first-child {
  display: block;
  color: var(--accent);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: -0.012em;
  margin-bottom: .15rem;
}

.findings li p { margin: 0; }
.findings li p + p { margin-top: .5rem; }

/* ---------- home sections ---------- */

.pillars h3 { font-size: 1.1rem; margin-bottom: .4rem; }

.band-note {
  color: var(--ink-soft);
  max-width: 54ch;
  margin: 0 0 1.2rem;
}

/* The closing band, on the CI gradient in both themes (R2). It is the one
   place magenta appears as a large fill, and it is the end of the gradient
   rather than a flat magenta panel. Everything in it is white, so the
   semantic tokens are redefined on the band itself rather than each child
   being given a colour: anything dropped in here inherits the right one. */
/* The closing band. The default is flat `--surface`, which keeps it distinct
   from the `--bg` band above it and asks nothing of the tokens: the page's
   own ink, links and buttons all work on it unchanged. */
.cta-band { background: var(--surface); }

/* `surfaces: gradient` in _config.yml puts the CI gradient here instead.
   Everything in the band then has to be white, so the semantic tokens are
   redefined on the band itself rather than on each child: anything dropped
   in here inherits the right colour.

   `--bg` is redefined too, and that is not cosmetic. Anything inside that
   paints itself with the page colour - the engagement cards do - would
   otherwise be a white panel in light theme while the band has just set the
   ink to white: white text on a white card, invisible. A translucent navy
   gives those panels a bed that works with white ink in both themes and
   keeps the gradient readable through them. Measured: white on this bed is
   7.73:1 at the gradient's worst point, and 5.76:1 on the band itself. */
body.surfaces-gradient .cta-band {
  background: var(--gradient-text-bed);
  --bg:        rgba(0, 23, 48, 0.22);
  --ink:       #ffffff;
  --ink-soft:  rgba(255, 255, 255, 0.82);
  --accent:    #ffffff;
  --accent-hover: rgba(255, 255, 255, 0.82);
  --rule:      rgba(255, 255, 255, 0.28);
  --surface:   rgba(255, 255, 255, 0.10);
  --surface-2: rgba(255, 255, 255, 0.06);
  --field-bg:  rgba(255, 255, 255, 0.12);
  --btn-bg:    #ffffff;
  --btn-ink:   #001730;
  --btn-bg-hover: rgba(255, 255, 255, 0.86);
  color: var(--ink);
}

/* Alternating bands (L2/L4). Without this the home page is one unbroken
   column and nothing tells the eye where a section ends. */
.band.alt { background: var(--surface); }

/* Latest and Recent publications side by side on a wide screen. They were two
   full-width bands of text in sequence, which is most of why the page read as
   a single column of prose. Stacked below 64rem. */
/* `.home-split` is gone. It put "Latest" and "Recent publications" side by
   side; publications took a band of their own on 1 Nov 2026 and "Latest"
   went full width on 2 Nov, so nothing was left in it but one column of a
   two-column grid, which is how three cards ended up crammed into half the
   page with the other half empty. */

/* The newsletter as the last row of the offer band rather than a band of its
   own: it is the smaller ask for a reader not ready for the larger one. */
.home-news { margin-top: 2.6rem; padding-top: 1.8rem; border-top: 1px solid var(--rule); }

/* The four ways of working, on the home page. Cards rather than a list,
   because each one is a different conversation to start and the mail link at
   the foot of each is the point of the band. */
/* Three by two from 64rem since W6 (1 Nov 2026), which is six cards and
   echoes the six facts at the top of the page. Two columns from 40rem, one
   below. A fixed count rather than auto-fit: the descriptions are uneven
   enough that letting the columns find their own width makes the longest
   card tower over the rest. */
.engage {
  list-style: none; padding: 0; margin: 1.8rem 0 1.4rem;
  display: grid; gap: 1rem;
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 64rem) { .engage { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 40rem) { .engage { grid-template-columns: 1fr; } }

/* The strand a card belongs to, above its title. Small, uppercase and in the
   accent, the same shape as the fact labels on the home page, so the two
   bands that bracket the page read as a pair. It is a link where the strand
   has a heading on /approach/ to point at. */
.engage-label {
  margin: 0 0 .35rem !important;
  font-size: .7rem; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent) !important;
}
.engage-label a { color: inherit; text-decoration: none; }
.engage-label a:hover { text-decoration: underline; }
/* In the list shape on /expertise/ the label runs inline before the title,
   so it needs none of the block spacing above. */
.findings .engage-label { display: inline; margin: 0 !important; }

/* The two lines under the grid, and the last row. */
.engage-more { margin: .6rem 0 0; font-size: .92rem; color: var(--ink-soft); }
.engage-more + .engage-more { margin-top: .3rem; }
.engage-last {
  display: grid; gap: 1.6rem;
  margin-top: 2.6rem; padding-top: 1.8rem;
  border-top: 1px solid var(--rule);
}
@media (min-width: 40rem) { .engage-last { grid-template-columns: 1fr 1fr; gap: 2.4rem; } }
/* The newsletter block already carries this rule from its own band; inside
   the last row the row draws the line instead, once, for both blocks. */
.engage-last .home-news { margin-top: 0; padding-top: 0; border-top: 0; }
.engage-join h3 { margin: 0 0 .5rem; font-size: 1.05rem; }
.engage-join p { margin: 0 0 .4rem; font-size: .92rem; color: var(--ink-soft); }
.engage-join .more { margin: 0; }
.engage li {
  margin: 0; padding: 1.15rem 1.2rem 1rem;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  display: flex; flex-direction: column;
}
.engage h3 { margin: 0 0 .5rem; font-size: 1.05rem; }
.engage p { margin: 0; font-size: .92rem; line-height: 1.55; color: var(--ink-soft); }
/* pushed to the foot so the links line up across cards of unequal length */
/* The worked examples. Smaller than the description and above the call to
   action, because they are evidence for what was just claimed rather than
   the next thing to do. `margin-top: auto` moves to this line instead of
   .engage-go, so the examples and the action stay together at the foot of
   the card and the cards line up however uneven the descriptions are. */
.engage .engage-eg {
  margin-top: auto; padding-top: .7rem;
  font-size: .84rem; line-height: 1.5;
}
.engage .engage-go { padding-top: .4rem; }
.engage .engage-go a { font-size: .88rem; font-weight: 500; }

/* The same examples in the list shape on /expertise/, where they follow the
   description inline rather than sitting in a card. */
.findings .engage-eg { color: var(--ink-soft); }

.band-lede { max-width: var(--measure); }

/* The hero's primary action. Uses the --btn-* set rather than the brand
   constants, as docs/05 requires: those tokens already carry the contrast
   decision (navy on magenta in dark, white on the darker magenta in light),
   and writing the hex here would have been wrong in one of the two themes. */
/* Q1.5, 4 Nov 2026. The hero sits on a dark photograph in BOTH themes, so
   it is not a light surface and the light theme's button tokens do not
   belong to it. Those are navy on navy-white, which on the hero's navy-tinted
   picture reads as plain text: the primary action on the home page, all but
   invisible, and now to every visitor who has not chosen dark.

   The fix is the tokens, not the rule: the hero redefines the --btn-* set to
   the values the dark theme uses, and the button below keeps reading them.
   Nothing here is a hex. Measured on the hero bed: navy on magenta is 4.9:1,
   which is AA for the button's weight and size. */
.hero-full {
  --btn-bg:      var(--magenta);
  --btn-ink:     var(--navy);
  --btn-bg-hover:var(--magenta-light);
}

/* `.hero-full a` sets every link in the hero to magenta and is the more
   specific selector, so a bare .hero-btn rendered as magenta text on a
   magenta button: a solid pink rectangle with the label invisible. Matching
   its specificity is the fix. */
.hero-full a.hero-btn {
  display: inline-block;
  padding: .55rem 1.1rem;
  margin-right: 1.1rem;
  background: var(--btn-bg);
  color: var(--btn-ink);
  border-radius: var(--radius-ui);
  font-weight: 600;
  text-decoration: none;
}
.hero-full a.hero-btn:hover,
.hero-full a.hero-btn:focus { background: var(--btn-bg-hover); color: var(--btn-ink); }

/* ---------- newsletter ---------- */

.newsletter {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.1rem;
  padding: 1.6rem;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-ui);
}
.newsletter h2 { margin: 0 0 .4rem; }
.newsletter p { margin: 0; }

.nl-form { display: flex; gap: .6rem; flex-wrap: wrap; }
.nl-form input {
  flex: 1 1 14rem; min-width: 0;
  padding: .6rem .8rem;
  font: inherit; font-size: .95rem;
  color: var(--ink);
  background: var(--field-bg);
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
}
.nl-form input:focus { border-color: var(--accent); outline: none; }
.nl-form button {
  flex: 0 0 auto;
  padding: .6rem 1.2rem;
  font: inherit; font-size: .95rem; font-weight: 600;
  color: var(--btn-ink); background: var(--btn-bg);
  border: 0; border-radius: var(--radius-ui); cursor: pointer;
}
.nl-form button:hover { background: var(--btn-bg-hover); }

.nl-fallback { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.nl-fallback .cta { margin-top: 0; }
.nl-alt { margin: .7rem 0 0; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- footer alignment ---------- */

/* `align-items: end` used to be here. It pushed the columns down to their own
   different baselines, which is what made the footer look uneven; R5 sets
   `start` where the grid is declared. */
.foot-mark { height: 30px; }

/* ---------- legal pages ---------- */

.foot-legal a { color: var(--ink-soft); }
.foot-legal a:hover { color: var(--ink); }
.foot-legal span { margin: 0 .3rem; opacity: .5; }

.legal h1 { margin-bottom: .4rem; }

.legal h2 {
  font-size: 1rem;
  font-weight: 500;   /* see .hero-full h1 */
  letter-spacing: .01em;
  margin: 2.2rem 0 .35rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}

/* the English reading of each German legal heading */
.legal .gloss {
  font-weight: 400;
  font-size: .86rem;
  color: var(--ink-soft);
  white-space: nowrap;
}

.legal p { margin: 0 0 .9rem; }

/* shown only while mandatory fields are still unfilled */
.legal-warn {
  background: rgba(249, 45, 132, .12);
  border-left: 3px solid var(--accent);
  padding: .9rem 1.1rem;
  margin: 1.4rem 0 0;
  font-size: .95rem;
}
.legal-warn code {
  background: var(--surface-2);
  padding: .1rem .3rem;
  border-radius: var(--radius-ui);
  font-size: .9em;
}

/* ---------- services ---------- */

/* each service is a two-column block: the offer, and the proof beside it */
.svc-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 15rem;
  gap: 3rem;
  align-items: start;
}

@media (max-width: 52rem) {
  .svc-grid { grid-template-columns: 1fr; gap: 1.6rem; }
}

.service .eyebrow a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(249, 45, 132, .4);
}
.service .eyebrow a:hover { border-bottom-color: var(--accent); }

.svc-main h2 { margin: 0 0 .5rem; font-size: 1.45rem; letter-spacing: -0.02em; }

.svc-lede {
  font-size: 1.08rem;
  font-weight: 500;
  margin: 0 0 1.1rem;
  max-width: 44ch;
}

.svc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 48ch;
}

.svc-list li {
  position: relative;
  padding-left: 1.15rem;
  margin-bottom: .4rem;
  color: var(--ink-soft);
}

.svc-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .72em;
  width: .42rem;
  height: 1px;
  background: var(--accent);
}

.svc-note { margin-top: 1rem; max-width: 46ch; font-style: italic; }

/* the proof column */
.svc-side {
  border-left: 1px solid var(--rule);
  padding-left: 1.4rem;
}

@media (max-width: 52rem) {
  .svc-side {
    border-left: 0;
    border-top: 1px solid var(--rule);
    padding: 1.1rem 0 0;
  }
}

.svc-label {
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 .3rem;
}

.svc-side p + .svc-label { margin-top: 1.5rem; }

.svc-example { margin: 0 0 .35rem; font-weight: 500; line-height: 1.35; }
.svc-example a { text-decoration: none; }
.svc-example a:hover { text-decoration: underline; }

/* ---------- testimonials ---------- */

.quotes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 2rem;
}

.quote {
  margin: 0;
  background: var(--surface);
  border-radius: var(--radius-ui);
  padding: 1.6rem 1.6rem 1.3rem;
}

.quote blockquote {
  margin: 0 0 1rem;
  font-size: 1.04rem;
  line-height: 1.5;
}

.quote blockquote p { margin: 0 0 .8rem; }
.quote blockquote p:last-child { margin-bottom: 0; }

/* quotation marks sit on the first and last paragraph, so a two-paragraph
   quote still opens and closes once rather than around every paragraph */
.quote blockquote p:first-child::before { content: "\201C"; }
.quote blockquote p:last-child::after   { content: "\201D"; }

.quote figcaption {
  font-size: .92rem;
  font-weight: 500;
  padding-top: .9rem;
  border-top: 1px solid var(--rule);
}
.quote figcaption .muted { font-weight: 400; }

/* a collaborator's own organisation, under their role on the team card */
.people .where {
  display: block;
  font-size: .82rem;
  color: var(--ink-soft);
  opacity: .85;
}

#teamlist h2 {
  margin: 2.8rem 0 .3rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
  font-size: 1.15rem;
}
#teamlist h2 + .muted { margin-top: 0; max-width: 44ch; }

/* ---------- partner tally ---------- */

/* three counts per partner: projects · publications · news */
.tally { display: inline-flex; gap: .3rem; }

.tally-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.65rem;
  padding: .12rem .3rem;
  border-radius: var(--radius-ui);
  background: rgba(249, 45, 132, .16);
  font-size: .82rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}

/* a dash is a claim of none, so it should not look like a filled count */
.tally-n.is-zero {
  background: transparent;
  color: var(--ink-soft);
  font-weight: 400;
  opacity: .6;
}

.t-tally { white-space: nowrap; }
.tally-key { margin: 0 0 1.2rem; max-width: 52ch; }
.tally-key .k { white-space: nowrap; }

/* ---------- theme toggle ---------- */

/* One logo file per theme; the stylesheet shows the right one.

   Both rules are written against :root so they carry two classes' worth of
   specificity. They have to: the components that place a logo set their own
   `display`, and `.brand img` (0,1,1) outweighed the plain `.logo-light`
   (0,1,0) this used to be. The effect was that in the dark theme the header
   showed both logos, one under the other, and stood 25px taller on a phone
   and 30px on a desktop than in the light theme, which hid one correctly
   because its rule happened to carry the theme attribute. Switching theme
   moved the whole page down and back up (Martin, 31 Oct 2026). The footer
   was never affected, because `.foot-mark` is (0,1,0) and lost the tie on
   source order instead of winning it.

   `:not([data-theme="light"])` rather than `[data-theme="dark"]`: the
   attribute is always on <html>, but if it ever went missing both logos
   would show again, and this way the dark pair is the fallback. The visible
   logo needs no rule of its own; the components already give it one, and an
   <img> with no `display` is visible anyway. */
:root:not([data-theme="light"]) .logo-light { display: none; }
:root[data-theme="light"] .logo-dark { display: none; }

/* the fixed cluster: work-in-progress note, then the theme toggle */
.corner {
  position: fixed;
  right: 1.1rem;
  bottom: 1.1rem;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: .5rem;
}

.wip {
  margin: 0;
  padding: .45rem .8rem;
  font-size: .82rem;
  font-weight: 600;
  color: var(--accent);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  box-shadow: 0 2px 14px rgba(0, 0, 0, .28);
  white-space: nowrap;
}

:root[data-theme="light"] .wip { box-shadow: 0 2px 14px rgba(0, 23, 48, .14); }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .45rem .8rem .45rem .6rem;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 2px 14px rgba(0, 0, 0, .28);
}

.theme-toggle:hover { border-color: var(--accent); }

/* a half-filled circle: the usual shorthand for "switch appearance" */
.tt-icon {
  width: .85rem;
  height: .85rem;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  background: linear-gradient(to right, currentColor 50%, transparent 50%);
}

:root[data-theme="light"] .theme-toggle {
  box-shadow: 0 2px 14px rgba(0, 23, 48, .14);
}

@media (max-width: 34rem) {
  /* on a phone the two would fill the width, so the toggle sheds its label
     and the note shortens rather than wrapping across the screen */
  .corner { right: .8rem; bottom: .8rem; gap: .4rem; }
  .theme-toggle { padding: .5rem; }
  .tt-label { position: absolute; left: -9999px; }
  .wip { font-size: .76rem; padding: .4rem .65rem; }
}

@media print {
  .corner { display: none; }
}

/* White surfaces that carry artwork - partner tiles, figures, the PDF
   viewer, deliverable thumbnails - are white in both themes, because they
   are the paper the artwork sits on. On the light page they would otherwise
   dissolve into the background, so they take an edge there. */
.partners a,
table.listing td.t-partner .t-logo,
.dl-thumb {
  border: 1px solid var(--tile-edge);
}

/* a long DOI is one unbreakable token and pushed the page sideways on a phone */
.doi { overflow-wrap: anywhere; }

/* ---------- partner world map ---------- */

.worldmap { margin: 0 0 2.2rem; }
.worldmap svg { display: block; width: 100%; height: auto; }

/* The land is a quiet ground; the pins are the content. */
.wm-land path {
  fill: var(--surface);
  stroke: var(--rule);
  stroke-width: .6;
  vector-effect: non-scaling-stroke;
}

:root[data-theme="light"] .wm-land path {
  fill: #eef1f5;
  stroke: rgba(0, 23, 48, .18);
}

.wm-pin circle {
  fill: var(--magenta);
  fill-opacity: .82;
  stroke: var(--bg);
  stroke-width: 1.2;
  transition: fill-opacity .15s ease;
}
.wm-pin:hover circle { fill-opacity: 1; }

.wm-count {
  text-anchor: middle;
  dominant-baseline: middle;
  font-size: 7px;
  font-weight: 700;
  fill: #ffffff;
  pointer-events: none;
}

.worldmap figcaption { margin-top: .7rem; max-width: 60ch; }

/* ---------- onward links ---------- */

/* Two routes out of the home page after the testimonials: the argument, or
   the offer. Sized as blocks rather than inline links, because at this point
   on the page a reader is deciding where to go next, not skimming. */
.onward {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.1rem;
}

@media (max-width: 40rem) { .onward { grid-template-columns: 1fr; } }

.onward a {
  display: block;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  text-decoration: none;
  color: inherit;
}
.onward a:hover { border-color: var(--accent); }

.onward-t {
  display: block;
  font-weight: 600;
  margin-bottom: .25rem;
}
.onward-t::after { content: " →"; color: var(--accent); }

.onward-d { display: block; font-size: .9rem; line-height: 1.45; }

/* ---------- cite this ---------- */

/* The citation already assembled, so nobody has to build it from the page.
   Set in the monospace-ish measure of a reference rather than body prose. */
.citeas {
  margin: 2.4rem 0 0;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-ui);
  background: var(--surface-2);
}

.citeas-h {
  margin: 0 0 .45rem;
  font-size: .78rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-soft);
}

.citeas-t {
  margin: 0;
  font-size: .9rem; line-height: 1.55;
  overflow-wrap: anywhere;
}

.citeas-copy {
  margin-top: .7rem;
  font: inherit; font-size: .8rem; font-weight: 500;
  color: var(--accent); background: none;
  border: 0; padding: 0; cursor: pointer;
}
.citeas-copy:hover { text-decoration: underline; }

/* ---------- data & tools ---------- */

.tools { list-style: none; padding: 0; margin: 1.4rem 0 0; display: grid; gap: .9rem; }

.tools li {
  margin: 0;
  padding: 1.1rem 1.2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-ui);
  display: block;
}
.tools li:hover { border-color: var(--accent); }

.tool-top {
  display: flex; align-items: baseline; gap: .6rem;
  margin-bottom: .35rem;
}
.tool-state {
  font-size: .72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent);
}
.tool-year { font-size: .8rem; color: var(--ink-soft); }

.tool-title {
  display: block;
  font-weight: 600; font-size: 1.02rem; line-height: 1.35;
  color: var(--ink); text-decoration: none;
}
.tool-title:hover { color: var(--accent); }

.tool-sum { display: block; margin-top: .3rem; font-size: .9rem; line-height: 1.5; }

.tool-links { display: flex; gap: 1rem; margin-top: .6rem; }
.tool-links a { font-size: .82rem; font-weight: 500; }

/* a second affiliation under the role line on a person page */
.person-also { margin: .35rem 0 0; max-width: 46ch; }

/* ---------- project grid ---------- */

.pgrid {
  list-style: none; padding: 0; margin: 1.6rem 0 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.8rem 1.4rem;
}
@media (max-width: 62rem) { .pgrid { grid-template-columns: repeat(2, 1fr); } }
/* Two columns all the way down to a very narrow phone (Martin, 31 Oct 2026),
   matching the facts grid above it. One full-width card per screen turned six
   projects into six screens of scrolling, and the picture is the point of
   this grid: two of them side by side read as a body of work, one at a time
   reads as a list. Below 22rem two columns leave no room for a title, so it
   drops to one, which is the same breakpoint the facts use. */
@media (max-width: 40rem) {
  .pgrid { gap: 1.2rem .9rem; }
}
@media (max-width: 22rem) { .pgrid { grid-template-columns: 1fr; } }

.pgrid li { margin: 0; }
.pgrid a { display: block; text-decoration: none; color: inherit; }

.pcard-img {
  display: block; aspect-ratio: 4 / 3; width: 100%;
  border: 1px solid var(--rule); border-radius: var(--radius-ui);
  overflow: hidden; background: var(--surface);
  /* --surface, not #fff: a photograph covers this frame completely once
     loaded, so the colour is only ever seen while the image is still
     arriving, and white flashed against the dark theme. */
}
/* Images are cropped from the top left, not the centre. A report cover,
   a chart or a map carries its title and its key in the top left, and
   centre-cropping a portrait image into a landscape frame cuts exactly
   that away. Portraits of people stay centred, below, because faces sit
   in the middle of the frame. */
.pcard-img img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top left; }

/* a project without a picture yet keeps the grid even rather than collapsing */
.pcard-img-empty { background: var(--surface); position: relative; }
.pcard-img-empty::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(135deg, transparent 0%, var(--surface-2) 100%);
}
.pgrid a:hover .pcard-img { border-color: var(--accent); }

/* Q1.2, 4 Nov 2026. These three rules now say what a project card actually
   renders as. They did not before: the post-card block shared their class
   names and is written later, so it won every property the two both set, and
   the values below are what was reaching the page rather than what was
   written here. Marked, because the difference is where the bug was:

     display        block        -> flex, with the post block's gap and wrap
     meta size      .78rem       -> .82rem
     title weight   600          -> 700
     title size     1.04rem      -> 1.05rem
     summary size   .89rem       -> .92rem
     summary        plain block  -> clamped to three lines

   Whether a project card wants a three-line clamp is a design question and
   not this phase's: the clamp is kept because it is what the grid has looked
   like, and it is on the list for the design phase. */
.pcard-meta {
  display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap;
  margin: .75rem 0 .25rem;
  font-size: .82rem; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .05em;
}
.pcard-title { display: block; font-weight: 700; font-size: 1.05rem; line-height: 1.3; }
.pgrid a:hover .pcard-title { color: var(--accent); }
.pcard-sum {
  margin-top: .35rem;
  font-size: .92rem; line-height: 1.5; color: var(--ink-soft);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pcard-tags { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .6rem; }
.pcard-tags span {
  font-size: .7rem; padding: .18rem .5rem;
  border: 1px solid var(--rule); border-radius: 999px; color: var(--ink-soft);
}

/* ---------- theme filter ---------- */

.themebar { display: flex; flex-wrap: wrap; gap: .45rem; margin: .4rem 0 0; }
.themebar button {
  font: inherit; font-size: .84rem; cursor: pointer;
  padding: .4rem .85rem; border-radius: 999px;
  border: 1px solid var(--rule); background: none; color: var(--ink-soft);
}
.themebar button:hover { color: var(--ink); border-color: var(--ink-soft); }
.themebar button.on {
  background: var(--accent); border-color: var(--accent);
  color: var(--btn-ink); font-weight: 600;
}
/* Two bars above the publications list, one filtering and one sorting, are
   two rows of chips that look alike. The label says which is which; on the
   projects page there is only one bar and no label, and nothing changes. */
.themebar-label {
  align-self: center; font-size: .84rem; color: var(--ink-soft);
  margin-right: .15rem;
  /* the two labels are different words, so the chips after them would start
     at two different x positions; this lines the rows up */
  min-width: 2.6rem;
}
@media (max-width: 30rem) { .themebar-label { flex: 0 0 100%; margin: 0; } }

/* ---------- expertise ---------- */

/* ---------- approach: the three strands ----------

   Image and text side by side, swapping sides down the page so the three read
   as a sequence. The text keeps `--measure` so the line length does not grow
   with the column; the picture takes the rest. */
.strand {
  display: grid; grid-template-columns: 1fr 1.25fr; gap: 2.6rem;
  align-items: start;
}
@media (max-width: 52rem) { .strand { grid-template-columns: 1fr; gap: 1.2rem; } }
/* Each band holds exactly one .strand, so nothing here can alternate them by
   position: an :nth-of-type rule would count bands, and silently pick the
   wrong ones the day a band is added above. The side is marked in the page
   instead, on the one strand that is flipped. */
.strand.flip .strand-img { order: 2; }
@media (max-width: 52rem) { .strand.flip .strand-img { order: 0; } }

.strand-img { margin: 0; position: sticky; top: 1.5rem; }
@media (max-width: 52rem) { .strand-img { position: static; } }
.strand-img img {
  display: block; width: 100%; aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--rule); background: var(--surface);
  border-radius: var(--radius-img);
}
.strand-img figcaption { margin-top: .4rem; line-height: 1.4; }
.strand-body { min-width: 0; max-width: var(--measure); }
.strand-body h2 { margin-top: 0; }

/* ---------- the picture treatment (R7) ----------

   Black and white with the navy lifted into the shadows, so photographs from
   half a dozen sources read as one set. Chosen from a sheet of four
   treatments rendered on five real photographs; this is B.

   `lighten` means no pixel ends up darker than the tint: the blacks become
   navy and everything above them is left alone. That is why the tint is
   #16325a and not the CI's own #001730 - at #001730 almost nothing moved,
   because the CI navy is darker than the shadows in most of these pictures.
   A single layer, so the highlights stay neutral; a second `multiply` layer
   would tint those too and make it a duotone rather than a tone.

   Two values to turn, and only these two:
     --photo-tint           the navy lifted into the shadows
     --photo-tint-strength  how far. 1 is the full lift; lower leaves the
                            shadows nearer black.

   On by default (`photo_treatment: mono` in _config.yml); `plain` turns it
   off and every picture renders as shot.

   Portraits are not in here. They have their own switch (`portraits: bw`),
   because a row of faces and a row of buildings do not want the same
   treatment and should not be tied to one word. */
:root {
  --photo-tint: #16325a;
  --photo-tint-strength: 1;
}
/* The same tint in both themes: one treatment, not two. A lighter navy was
   tried for dark, to stop a photograph dissolving into a page that is already
   navy, and it read as blue rather than as black and white. The picture's own
   border is what separates it from the surface instead. */

/* Which pictures this reaches.

   `.tone` wraps a single image and nothing else. It sat on the <figure> at
   first, which also holds the photographer's credit, and the overlay covered
   the credit line.

   `.pcard-img` and `.pc-shot` already wrap a photograph on their own, so
   they are listed here rather than having a `.tone` span threaded into four
   templates. Between them that is the project cards on /projects/ and the
   home page, the portraits on /team/, in the home page row and on a person's
   own page, the Approach strands and the Expertise fields.

   NOT the heroes, which was tried and measured and does not work. The tint
   is a `lighten` layer, so it raises the darkest part of a picture - which on
   a hero is exactly the part the scrim darkened to carry white text over it.
   Measured on the home hero against the lightest 5% of the backdrop behind
   each text block: the standfirst falls from 8.45:1 to 4.23:1, under the 4.5
   that body text needs, and the headline from 2.82:1 to 1.12:1. A treatment
   that costs a page its text is not a treatment.

   Portraits keep their own switch as well (`portraits: bw`), which decides
   whether they are black and white at all. This adds the navy on top of
   whatever that leaves, so the two compose rather than fight.

   Deliberately not included:
     - logos and the signet, which are brand marks; tinting one is a mistake
     - charts and figures (`.fig`), where the colours carry data
     - publication covers, which are title pages and designed tiles rather
       than photographs, and carry their own gradient and type
     - `.pc-initials`, the lettered square shown where a portrait is missing:
       it is type on a token colour, not a picture */
.tone { display: block; position: relative; isolation: isolate;
        border-radius: var(--radius-img); overflow: hidden; }
/* `.pc-shot` is a <span> around the portrait, not the <img> itself: a
   pseudo-element never renders on a replaced element, so an overlay needs a
   real box to sit in. */
body.photo-mono .pcard-img,
body.photo-mono .pc-shot { position: relative; isolation: isolate; }
body.photo-mono .tone img,
body.photo-mono .pcard-img img,
body.photo-mono .pc-shot img { filter: grayscale(1) contrast(1.05); }
body.photo-mono .tone::after,
body.photo-mono .pcard-img::after,
body.photo-mono .pc-shot::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--photo-tint);
  opacity: var(--photo-tint-strength);
  mix-blend-mode: lighten;
}
/* The opt-out, set per project with `hero_tone: false`. Eight project heroes
   are maps, charts or screenshots, and greyscale takes the colour coding out
   of them: a pie-chart map of the EU building stock becomes eleven shades of
   one grey. Written here rather than as a list of slugs, so a new project
   declares it in its own front matter. */
body.photo-mono .pcard-img.no-tone img { filter: none; }
body.photo-mono .pcard-img.no-tone::after { content: none; }

/* ---------- expertise: the strand index ----------

   Three groups, one per strand, instead of eight links in a row. A flat list
   said nothing about how the fields relate; this shows the shape of the page
   before the reader scrolls into it. Three columns on a laptop, one on a
   phone, where three narrow columns would break every field name in half. */
.xindex-strands {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6rem 2.4rem;
  margin: 0 0 1rem;
}
@media (max-width: 52rem) { .xindex-strands { grid-template-columns: 1fr; gap: 1.2rem; } }
.xindex-strand { min-width: 0; }
.xindex-head {
  margin: 0 0 .4rem;
  font-size: .72rem; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent);
}
.xindex-strand .xindex { margin: 0; gap: .35rem .9rem; }

/* The heading of each strand band. `--measure` keeps the intro to a readable
   line length while the fields below it use the full width. */
.xstrand-head { margin: 0 0 2.4rem; }
.xstrand-head h2 { margin: 0 0 .4rem; }
.xstrand-intro { max-width: var(--measure); margin: 0 0 .6rem; color: var(--ink-soft); }
.xstrand-head .more { margin: 0; }

/* The Global South band carries the two evidence lines but no field, so its
   body needs the width the fields' text column normally gives it. */
.xglobal-south { max-width: var(--measure); }

/* The photograph credit, under the picture. Small and quiet: it is a
   requirement of using the image, not something to read. */
.xfield-img figcaption { margin-top: .4rem; line-height: 1.4; }

.xindex {
  display: flex; flex-wrap: wrap; gap: .5rem 1.1rem;
  margin: 0 0 1rem; font-size: .9rem;
}

.xfield {
  display: grid; grid-template-columns: 1fr 1.3fr; gap: 2.2rem;
  align-items: start; padding: 2.6rem 0; border-top: 1px solid var(--rule);
}
/* alternate the image side so the page reads as a sequence, not a list */
.xfield:nth-child(odd) .xfield-img { order: 2; }
@media (max-width: 46rem) {
  .xfield { grid-template-columns: 1fr; gap: 1.2rem; padding: 2rem 0; }
  .xfield:nth-child(odd) .xfield-img { order: 0; }
}

/* A <figure> since R7: the photograph and its credit. The frame moved from
   the figure to the image, because the figure now also holds the caption and
   `overflow: hidden` on an aspect-ratio box would have clipped it. */
.xfield-img {
  margin: 0;
  position: sticky; top: 1.5rem;
}
@media (max-width: 46rem) { .xfield-img { position: static; } }
.xfield-img img {
  display: block; width: 100%; aspect-ratio: 3 / 2;
  object-fit: cover; object-position: top left;
  border: 1px solid var(--rule); background: var(--surface);
  /* --surface, not #fff: a photograph covers this frame completely once
     loaded, so the colour is only ever seen while the image is still
     arriving, and white flashed against the dark theme. */
}
.xfield-img-empty { background: var(--surface); }

.xfield h3 { margin: 0 0 .55rem; font-size: 1.3rem; }
.xlede { margin: 0 0 1rem; font-size: 1.02rem; line-height: 1.55; }
.xlist { margin: 0 0 1rem; padding-left: 1.1rem; }
.xlist li { margin: .25rem 0; font-size: .92rem; line-height: 1.5; color: var(--ink-soft); }
.xeg { margin: 1rem 0 .4rem; font-size: .9rem; }
.xeg-l {
  display: block; font-size: .74rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-soft); margin-bottom: .2rem;
}
.xfor { margin-top: .5rem; }

/* Two links sharing a footer row (see `foot_with` in _config.yml). */
.foot-sep { opacity: .45; margin: 0 .15rem; }

/* The "we are growing" banner on /team/ (R8). This is what --tint is for: a
   small highlight, a paragraph at most. It is the only blush on the page. */
.banner {
  background: var(--tint);
  color: var(--ink);
  border-radius: var(--radius-ui);
  padding: .75rem 1rem;
  margin: 0 0 1.4rem;
  font-size: .92rem;
  line-height: 1.5;
}
.banner a { font-weight: 500; }

/* ---------- the team row (M2) ----------

   One row that scrolls sideways and snaps card by card. The browser does the
   scrolling; the only script is the two arrows, and only where there is a
   mouse. What this replaced was pages of two with dots, a counter, a play
   button and autoplay, and a band that changed height when the last page
   held one person.

   The card widths are the point: 78% on a phone shows one card and a slice
   of the next, which is what tells a reader the row moves. 42% and 29% do
   the same at the two wider sizes.

   On a phone the row bleeds to the screen edge - negative margin out to the
   gutter, the same padding back in, and `scroll-padding-inline` so a snapped
   card lands at the gutter and not against the glass. From 64rem the row
   fits inside the column and the bleed is removed. */
.reel { position: relative; }

.reel .people {
  --card: 78%;
  --reel-gut: max(16px, env(safe-area-inset-left, 0px));
  display: flex; gap: 1rem;
  grid-template-columns: none;     /* .people is a grid elsewhere */
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--reel-gut);
  margin: 0 calc(-1 * var(--reel-gut));
  padding: 0 var(--reel-gut) .25rem;
  scrollbar-width: none;           /* the cards themselves show it scrolls */
  list-style: none;
}
.reel .people::-webkit-scrollbar { display: none; }
@media (min-width: 40rem) { .reel .people { --card: 42%; } }
@media (min-width: 64rem) {
  .reel .people {
    --card: 29%;
    margin-inline: 0; padding-inline: 0; scroll-padding-inline: 0;
  }
}
.reel .people > li {
  flex: 0 0 var(--card);
  scroll-snap-align: start;
  min-width: 0;
  /* the grid rules elsewhere give these a row height; in the row they size
     themselves, and every card is the same height because the flex line is */
  height: auto;
}
@media (prefers-reduced-motion: reduce) { .reel .people { scroll-behavior: auto; } }

/* The card stood up: the portrait fills the width as a 1:1 crop and the text
   sits under it. Two places use it, and they are listed together so the two
   cannot drift: the home page reel, where each card is most of the screen,
   and the grid on /team/ since 1 Nov 2026, where it matches the project and
   publication cards and gives the portraits room.

   The flat card is still the right shape elsewhere - a thumbnail beside the
   text suits a dense list - so this is a variant rather than the default. */
.reel .people > li,
.people.stacked > li {
  background: none; border: 0; border-radius: 0; padding: 0;
}
.reel .people > li:hover,
.people.stacked > li:hover { border-color: transparent; }
.reel .people a,
.people.stacked a {
  display: block;
  padding: 0; gap: 0;
  background: none; border: 0; border-radius: 0;
}
.reel .people .pc-face,
.people.stacked .pc-face {
  width: 100%; height: auto; aspect-ratio: 1 / 1;
  border-radius: var(--radius-img);
  display: block;
}
.reel .people .pc-initials,
.people.stacked .pc-initials { display: grid; place-items: center; font-size: 2rem; }
.reel .people .person-txt,
.people.stacked .person-txt { display: block; margin-top: .7rem; }
.reel .people .who,
.people.stacked .who { font-weight: 700; line-height: 1.25; }
.reel .people .what,
.people.stacked .what { margin-top: .2rem; }
.reel .people .where,
.people.stacked .where { margin-top: .35rem; }
/* In the reel the contact row belongs on a card you are reading, not on one
   you are swiping past, and the whole card is already a link to the person's
   page. On /team/ the row stays: that page is where someone goes to write to
   a colleague, which is the one thing a portrait grid cannot do for them. */
.reel .people .pc-links { display: none; }
.people.stacked .pc-links { margin: .5rem 0 0; }

/* Three across on a desktop, two on a phone, the same count and the same
   floor as the projects and the publications. `auto-fill` with a minimum is
   what the flat grid wants, because its cards carry a job title beside a
   thumbnail and need 21rem to do it; a stood-up card is as wide as its
   portrait and wants a fixed count instead. */
/* ---------- "Where we work" on /team/ (T1) ----------

   Text left, pictures right from 56rem; pictures under the text below that,
   which is where a phone wants them: the address and the link are what
   someone on a phone is usually after.

   Its own rules rather than the expertise field's, which this borrowed at
   first: that layout alternates the picture's side by `nth-child`, so it
   would have flipped the moment anything was added above. */
.place { display: grid; gap: 1.6rem; align-items: start; }
@media (min-width: 56rem) {
  .place { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: 2.6rem; }
}
/* With no pictures in the data the text takes the whole band rather than
   half of it beside an empty column. */
.place-notext { grid-template-columns: minmax(0, 1fr) !important; }
.place-one h3 { margin: 0 0 .6rem; font-size: 1.15rem; }
/* Two places since 2 Nov 2026: Climate Lab today, beside the photographs,
   and the 2027 move spanning the full width under them. Full width because
   the pictures are all of the first place: stacked in the text column the
   second one left half a screen of empty surface to its right.

   A rule above it, so it does not read as more paragraphs of the first, and
   the date label above each name carries which is which. */
.place-next {
  grid-column: 1 / -1;
  margin-top: .4rem; padding-top: 1.6rem;
  border-top: 1px solid var(--rule);
}
/* Its paragraphs in two columns on a wide screen. Full-width prose here runs
   to about 140 characters a line, which is roughly twice what anyone reads
   comfortably. One column below 56rem, where the band is single-column
   anyway. `break-inside` keeps a paragraph whole rather than splitting one
   across the gap. */
@media (min-width: 56rem) {
  .place-next .place-text { columns: 2; column-gap: 2.6rem; }
  .place-next .place-text p { break-inside: avoid; margin-top: 0; }
  /* The address follows the columns, so it needs the space the last
     paragraph's own margin cannot give it across a column break. */
  .place-next .place-address { margin-top: .4rem; }
}
.place-when {
  margin: 0 0 .3rem !important;
  font-size: .7rem; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent);
}
.place-one p { margin: 0 0 .8rem; }
.place-address { color: var(--ink-soft); font-size: .92rem; line-height: 1.5; }
.place-one .more { margin: 0; }

/* One wide picture, then two side by side beneath it. On a narrow phone the
   two drop under each other, because a 3:2 photograph at half a phone's
   width is a stamp. */
.place-shots {
  display: grid; gap: .9rem;
  grid-template-columns: 1fr 1fr;
}
.place-wide { grid-column: 1 / -1; }
@media (max-width: 26rem) { .place-shots { grid-template-columns: 1fr; } }
.place-shot { margin: 0; }
.place-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The frame is on the image, not the figure: the figure also holds the
   caption, and `overflow: hidden` on an aspect-ratio box would clip it. */
.place-wide img { aspect-ratio: 16 / 9; }
.place-small img { aspect-ratio: 3 / 2; }
.place-shot .tone { border: 1px solid var(--rule); background: var(--surface); }
.place-shot figcaption { margin-top: .4rem; line-height: 1.4; }

.people.stacked {
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: auto;
  gap: 2rem 1.4rem;
}
@media (max-width: 62rem) { .people.stacked { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 46rem) { .people.stacked { gap: 1.5rem 1.1rem; } }
@media (max-width: 22rem) { .people.stacked { grid-template-columns: 1fr; } }

/* Arrows, added by the script and only under a fine pointer, so a phone
   never carries a control it cannot use. Centred on the portraits rather
   than on the whole card, so they do not drift down as a name wraps. */
.reel-arrow {
  display: grid; place-items: center;
  position: absolute; top: calc(var(--face-h, 0px) / 2);
  transform: translateY(-50%);
  width: 2.6rem; height: 2.6rem; border-radius: 50%;
  border: 1px solid var(--rule); background: var(--bg); color: var(--ink);
  cursor: pointer; opacity: .85; transition: opacity .2s;
  z-index: 2;
}
.reel-arrow:hover { opacity: 1; border-color: var(--ink-soft); }
.reel-arrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* No disabled state: the row loops, so neither arrow ever reaches an end. */
.reel-prev { left: -1.3rem; }
.reel-next { right: -1.3rem; }
/* ---------- pictures, and their corners ----------

   One radius for everything that shows a picture, applied here in one place
   rather than left to each component, so a new image surface inherits it by
   being on this list instead of by someone remembering a number.

   `overflow: hidden` on the wrapper matters as much as the radius: an <img>
   with square corners inside a rounded box pokes out at the corners, and the
   result reads as a rendering fault rather than as a style. */
.pcard-img,
.pcard-img img,
.pcard-img-empty,
.xfield-img img,
.xfield-img-empty,
.people .pc-face,
.person-head .portrait,
.dl-thumb,
.dl-thumb img,
.fig img,
.tile,
.cite-thumb,
.cite-thumb img,
.cite-tile {
  border-radius: var(--radius-img);
}
.pcard-img, .dl-thumb { overflow: hidden; }

/* ---------- publication entries ----------

   M1. Title first, then the authors small and muted, then venue, year and
   DOI. The entry used to open with the author list, which on a long paper is
   thirty-two names, and on a phone pushed the title six lines down.

   On a phone the cover sits beside the title and the authors and the venue
   run full width underneath; from 40rem the cover is taller and everything
   stacks beside it. `grid-template-areas` says that in one place rather than
   in four rules.

   The class names are `cite-`, not `pub-`: see _includes/pubitem.html. */
/* Cards, not rows (Martin, 1 Nov 2026), the same shape as the project grid:
   picture on top, then the title, the authors, and the venue and year last.

   Three columns on a desktop, two on a phone, one only under 22rem, which is
   the breakpoint the facts and the projects both use. A row layout put the
   thumbnail beside the text and gave each entry the full width of the page;
   sixty-four of those is a very long page, and the title pages never had
   room to be looked at.

   The markup did not change. The entry was already a thumbnail, a title, an
   authors line and a meta line in one <li>, placed by grid areas; this lays
   the same four out in a column instead. Sort and filter keep working for
   the same reason: both act on the <li> in place and neither knows or cares
   how it is drawn. */
.pubs {
  list-style: none; margin: 1.4rem 0 0; padding: 0;
  display: grid; gap: 2rem 1.4rem;
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 62rem) { .pubs { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 46rem) { .pubs { gap: 1.5rem 1.1rem; } }
@media (max-width: 22rem) { .pubs { grid-template-columns: 1fr; } }
.pubs li { margin: 0; min-width: 0; display: flex; flex-direction: column; }

/* 3/4 upright, which is what every thumbnail already is: all sixty-four are
   600x800, so nothing is cropped and a title page is shown whole. */
.cite-thumb {
  display: block; width: 100%; aspect-ratio: 3 / 4;
  margin-bottom: .75rem;
  text-decoration: none;   /* the drawn tile's own text is not a link label */
  background: var(--surface);
  border: 1px solid var(--rule);
  overflow: hidden;
}
.cite-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pubs li:hover .cite-thumb { border-color: var(--accent); }

.cite-title {
  font-weight: 700; line-height: 1.3;
  color: var(--ink); text-decoration: none;
  min-width: 0; overflow-wrap: anywhere;
}
.cite-title:hover { color: var(--accent); text-decoration: underline; }

.cite-authors {
  margin: .35rem 0 0;
  font-size: .88rem; line-height: 1.45; color: var(--ink-soft);
  min-width: 0; overflow-wrap: anywhere;
}
/* The venue and year sit at the foot of the card whatever the title's
   length, so the row reads along one line rather than stepping up and down
   with each title. */
.cite-meta {
  margin: .35rem 0 0; padding-top: .35rem; margin-top: auto;
  font-size: .85rem; line-height: 1.45; color: var(--ink-soft);
  min-width: 0; overflow-wrap: anywhere;
}

/* The tile drawn here, for an entry with no image file. It is not a blank
   box with a label: it carries the gradient bar of the designed tiles, so an
   entry waiting for its picture belongs to the same set as the rest. */
/* The drawn tile, for a publication with no title page on file. White with
   its text on it, which is what the sixty-four designed tiles are: they are
   images, so they stay white in both themes, and a drawn tile that followed
   the theme would be the one odd card in the grid. Same reasoning as the
   logo tiles and the figures, which are white by design because they are the
   paper the artwork sits on.

   The gradient strip is gone (Martin, 2 Nov 2026). It was the one piece of
   CI colour on an otherwise plain card and it read as decoration on a
   placeholder, which is the last place that earns it. */
.cite-tile {
  display: flex; flex-direction: column; justify-content: space-between;
  width: 100%; height: 100%; padding: .7rem .75rem;
  background: #fff; color: #001730;
  border: 1px solid var(--tile-edge);
}
/* The same magenta as the post tile: the two placeholders were aligned on
   2 Nov and splitting them again over one colour would undo that. */
.cite-tile-kind {
  font-size: .68rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: #be208b;
}
.cite-tile-year {
  font-size: 1.05rem; font-weight: 700; color: #001730;
}

/* ---------- the publications control row ----------

   One row: the filter box takes the width that is left, the sort select sits
   beside it, and both wrap onto two lines under about 30rem. Two rows of
   pill buttons with their labels on separate lines is what this replaced. */
.pubcontrols {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin: 0 0 2rem;
}
.pubcontrols .filter { flex: 1 1 14rem; min-width: 0; margin: 0; }
.sortwrap { display: flex; align-items: center; gap: .4rem; margin: 0; }
.sortwrap label { font-size: .9rem; color: var(--ink-soft); }
.sortwrap select {
  font: inherit; font-size: .95rem;
  padding: .55rem .7rem;
  border: 1px solid var(--rule); border-radius: var(--radius-ui);
  background: var(--bg); color: var(--ink);
}
.sortwrap select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The thumbnail on a publication's own page, at the top of the fact column. */
.cite-figure { margin: 0 0 1.1rem; }
.cite-figure img {
  display: block; width: 100%; max-width: 13rem;
  border: 1px solid var(--rule); border-radius: var(--radius-img);
  background: var(--surface);
}
.cite-figure figcaption { margin-top: .4rem; line-height: 1.45; }

/* The hero eyebrow was removed on 4 Nov 2026 (Martin). It sat above the
   headline and read "Space for people and planet."; the headline makes the
   claim on its own. Its rule and the measurements behind it are in the
   history, in the commit that removed it, if it ever comes back: the short
   version is that a line much past thirty characters will not hold one line
   on a 320px phone in capitals at .08em of tracking. */

/* ---------- the evidence band ----------

   Six facts, one per perspective, from a single building up to the whole
   world's stock. Three columns from 64rem, two below that, one under 30rem,
   so the set reads 3 x 2 on a laptop and 2 x 3 on a phone.

   The value and the sentence share one paragraph rather than sitting in two
   boxes, because several sentences continue the value and do not start a new
   one: "50-107 million homes could be created within Europe's existing
   buildings". Splitting them would break the sentence in half. */
/* `.evidence`, renamed from `.facts` on 4 Nov 2026 (Q1.1). The `<dl>` at the
   top of this file, which states the key facts of a partner, a publication,
   a project or an opening, had the same class. This block is written later,
   so it won: every partner page laid its labels and values out in three
   columns, because `grid-template-columns: repeat(3, 1fr)` below replaced
   the `8rem 1fr` meant for them. Only partner pages showed it, and only on a
   wide screen: the other three `<dl>`s sit inside `.article-side`, whose own
   rule is more specific, and under 34rem everything falls to one column. */
.evidence {
  list-style: none; padding: 0; margin: 1.8rem 0 1.4rem;
  display: grid; gap: 2rem 2.4rem;
  grid-template-columns: repeat(3, 1fr);
  /* These three were never written for this list. It took them from the
     `<dl>` rules above while it shared their class, because that block sets
     them and this one did not override them, and the band has looked like
     this ever since. The visual check caught the home page changing when the
     class was renamed: 95px of band became 125px, with the two hairlines
     gone and the text a size larger.

     They are carried over as they were rather than reconsidered. Whether a
     rule above and below this band is wanted, and at what size it should be
     set, is a design decision, and Phase Q does not make those. Listed for
     the design phase. */
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  font-size: .93rem;
}
@media (max-width: 64rem) { .evidence { grid-template-columns: repeat(2, 1fr); gap: 1.8rem 2rem; } }
/* The order says "1 under 30rem" but also asks for 2 x 3 at 390px, and 390px
   is under 30rem. The acceptance test wins, so the single column starts at
   22rem instead: two columns hold down to 360px, one below that. */
@media (max-width: 22rem) { .evidence { grid-template-columns: 1fr; gap: 1.6rem; } }
@media (max-width: 46rem) { .evidence { gap: 1.5rem 1.3rem; } }
.evidence li { margin: 0; min-width: 0; }

.fact-perspective {
  margin: 0 0 .45rem;
  font-size: .72rem; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--accent);
}
.fact-body { margin: 0 0 .5rem; }
.fact-val {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 2.4vw, 1.85rem); font-weight: 500;
  line-height: 1.15; letter-spacing: -.01em;
  color: var(--ink);
  /* Deliberately NOT tabular-nums. These are headline figures of different
     lengths, not a column of digits that has to line up, and Circular's
     tabular figures are wide enough to space the whole line out: measured at
     296px against 261px for the same string. Tabular figures are for tables. */
  margin-right: .3rem;
}
.fact-src { margin: 0; font-size: .85rem; line-height: 1.45; }
.fact-src a { color: var(--ink-soft); }
.fact-src a:hover { color: var(--accent); }

/* Four pillars, not three. `.cols` is a three-column grid, which would leave
   the fourth alone on a second row. */
.cols.pillars { grid-template-columns: repeat(4, 1fr); gap: 1.8rem; }
@media (max-width: 64rem) { .cols.pillars { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 40rem) { .cols.pillars { grid-template-columns: 1fr; } }

/* ---------- the avoid / reduce / regenerate hierarchy ----------

   Numbered, because the order is the argument: regeneration is third and
   never pays for a step skipped above it. The numbers are information here,
   not decoration, which is the only reason they are there. */
.hierarchy { list-style: none; counter-reset: step; padding: 0; margin: 1.6rem 0 1.8rem; }
.hierarchy > li {
  counter-increment: step;
  position: relative;
  padding-left: 2.6rem;
  margin-bottom: 1.6rem;
}
.hierarchy > li::before {
  content: counter(step);
  position: absolute; left: 0; top: .05em;
  display: flex; align-items: center; justify-content: center;
  width: 1.8rem; height: 1.8rem;
  border: 1px solid var(--accent); border-radius: 50%;
  font-family: var(--font-display); font-size: .9rem; color: var(--accent);
}
.hierarchy h3 { margin: 0 0 .35rem; font-size: 1.1rem; }
.hierarchy p { margin: 0 0 .4rem; }
.hierarchy .muted { font-size: .9rem; }

/* The guardrail. It is the definition of the word "regeneration" on this
   site, so it is set apart from the paragraphs around it. */
.guardrail {
  border-left: 3px solid var(--accent);
  padding: .1rem 0 .1rem 1.1rem;
  margin: 0 0 1.6rem;
}

.account { padding-left: 1.2rem; }
.account li { margin-bottom: .5rem; }

/* The field's method name, under the question that heads it. */
.xfield-name {
  margin: -.3rem 0 .5rem;
  font-size: .82rem; font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------- news and events (Phase N) ----------

   One page for both, so the controls stack: a tab row with the filter box
   beside it, then a grid of cards. The tab row reuses `.themebar` from
   /projects/ rather than inventing a second set of pills. */
.newsbar {
  display: flex; flex-wrap: wrap; gap: .8rem 1.2rem;
  align-items: center; justify-content: space-between;
  padding-bottom: 1.1rem; border-bottom: 1px solid var(--rule);
}
.newsbar .themebar { margin: 0; flex: 1 1 auto; }
.newsbar .filter { margin: 0; flex: 1 1 14rem; max-width: 22rem; }
/* The count beside a tab's name. Quiet, and tabular so the numbers do not
   jiggle the labels as they change. */
.kindbar small {
  font-size: .78rem; font-weight: 400; margin-left: .3rem;
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
.kindbar .on small { color: inherit; opacity: .75; }

.pcards {
  list-style: none; margin: 1.6rem 0 0; padding: 0;
  display: grid; gap: 1.8rem 1.4rem;
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 62rem) { .pcards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 40rem) { .pcards { grid-template-columns: 1fr; gap: 1.5rem; } }
.pcard { margin: 0; min-width: 0; }
.pcard a {
  display: grid; gap: .55rem;
  text-decoration: none; color: inherit;
}

/* The picture, or a tile where there is none. Every post could carry a
   `hero`; none does yet, so the tile is what the page actually shows. */
.pcard-shot, .pcard-tile {
  display: block; width: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--radius-img); overflow: hidden;
}
@media (max-width: 40rem) { .pcard-shot, .pcard-tile { aspect-ratio: 21 / 9; } }
.pcard-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The drawn tile. White with its text on it, matching the publications
   placeholder, which in turn matches the sixty-four designed tiles that are
   white images and stay white in both themes (Martin, 2 Nov 2026).

   The CI gradients are gone: a tile is what a card shows when it has no
   picture, and four different gradients made the cards with nothing to show
   the loudest things on the page. */
.pcard-tile {
  position: relative;
  display: flex; align-items: flex-end; justify-content: space-between;
  padding: .8rem .9rem;
  background: #fff; color: #001730;
  border: 1px solid var(--tile-edge);
}
/* Magenta, for some flavour on an otherwise plain card (Martin, 2 Nov 2026).
   #be208b, not the CI's #f92d84: the tile is white in both themes, and
   #f92d84 on white is 3.66:1, under the 4.5 that small text needs. #be208b
   is the magenta the light theme already uses for exactly this reason, a
   blend of the CI magenta and purple, and measures 5.57:1 here. */
.pcard-kind {
  font-size: .72rem; letter-spacing: .09em; text-transform: uppercase;
  font-weight: 700; color: #be208b;
}
.pcard-big {
  font-size: 1.9rem; font-weight: 700; line-height: .9; text-align: right;
  color: #001730;
}
.pcard-big span {
  display: block; margin-top: .25rem;
  font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 600; color: #52616f;
}

/* Q1.2, 4 Nov 2026: `post-card-*`, not `pcard-*`. Project cards use the
   same three names, and these rules are written later, so they quietly
   restyled a grid they were never meant to touch. */
.post-card-meta {
  display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap;
  font-size: .82rem; color: var(--ink-soft);
  /* These three came from the project-card block while the two shared a
     class name. That block is written earlier, so it lost every property
     both set and kept the ones only it set - and these reached the page.
     The uppercase is the visible one: a post card's date renders
     "3 NOVEMBER 2026". Carried over so the cards do not move. */
  margin: .75rem 0 .25rem;
  text-transform: uppercase; letter-spacing: .05em;
}
.pcard-chip {
  font-size: .72rem; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 700; color: var(--accent);
}
.post-card-title { font-size: 1.05rem; font-weight: 700; line-height: 1.3; }
.pcard a:hover .post-card-title { color: var(--accent); }
/* Three lines, so a long summary cannot make one card twice the height of
   its neighbours. */
.post-card-sum {
  color: var(--ink-soft); font-size: .92rem; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
  /* From the project-card block too, for the same reason. */
  margin-top: .35rem;
}

/* The lead card on the unfiltered All tab: full width, picture beside the
   text. It goes back to an ordinary card inside a tab or under a filter. */
.pcard-lead { grid-column: 1 / -1; }
@media (min-width: 40rem) {
  .pcard-lead a {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    column-gap: 1.6rem; align-content: center;
  }
  .pcard-lead .pcard-shot, .pcard-lead .pcard-tile { grid-row: 1 / span 3; align-self: start; }
  .pcard-lead .post-card-title { font-size: 1.45rem; }
}

/* ---------- "Coming up" ---------- */
.soon { background: var(--surface); padding: 1.6rem 0 1.8rem; }
.soon h2 {
  margin: 0 0 .8rem;
  font-size: .8rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent);
}
/* One row, always (Martin, 4 Nov 2026). Two columns meant three items came
   out 2 + 1, with a hole where the fourth would be. A strip of what is
   coming up should read as one line you run your eye along.

   Items that do not fit scroll, and ui.js advances them on a timer. Scroll
   snapping makes a dragged or nudged row land on an item rather than
   halfway through one, and the scrollbar is hidden because the row moves by
   itself and a bar under three cards is noise. */
.soon ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; gap: .8rem;
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  scroll-behavior: smooth;
  /* The card at the right edge is cut by the row, which is how a carousel
     says there is more. Fading the last few millimetres makes that read as
     intended rather than as a card that ran out of room. A browser without
     mask-image simply shows the hard edge, which is what it showed before. */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}
.soon ul::-webkit-scrollbar { display: none; }
.soon li {
  margin: 0; background: var(--bg); border-radius: var(--radius-ui);
  scroll-snap-align: start;
  /* Just under three across on a wide screen since 4 Nov 2026, not three
     exactly. Three exactly filled the row to the pixel: nothing to scroll,
     so the carousel had nothing to do and the first card could never come
     round again on the right. Sizing them a shade wider makes the row
     overflow by part of a card, which is both the signal that it goes on and
     the thing that lets it loop. Narrower would have been worse than either:
     three cards would have left a gap at the right instead. */
  flex: 0 0 calc((100% - 1.6rem) / 2.8);
  min-width: 0;
}
@media (max-width: 64rem) { .soon li { flex-basis: calc((100% - .8rem) / 1.9); } }
@media (max-width: 40rem) { .soon li { flex-basis: 86%; } }
/* Stationary for anyone who asked for less movement; the row still scrolls
   by hand. */
@media (prefers-reduced-motion: reduce) { .soon ul { scroll-behavior: auto; } }

/* The applications card: the text is picked out, not the card (Martin,
   4 Nov 2026). A filled chip made that card a different object beside the
   others; every card now carries the same plain date chip, the same title
   and the same place underneath, and the one that is urgent says so in the
   colour of its title. */
.soon li.soon-apply .t { color: var(--accent); }

.soon li a {
  display: grid; grid-template-columns: 4.2rem minmax(0, 1fr);
  gap: 1rem; align-items: center;
  text-decoration: none; color: inherit; padding: .7rem .9rem;
}
/* The date chip, flat since 4 Nov 2026. It carried the navy-to-purple
   duotone, which is the treatment Martin took off the placeholders on
   2 November; it had no business surviving here. A date is a label, not
   artwork: it sits on the band's own surface with a rule around it and the
   accent on the month, so it reads at a glance and belongs to the theme
   rather than to a gradient that only works on one. */
.cal {
  display: grid; place-items: center; text-align: center;
  border-radius: var(--radius-ui); padding: .5rem 0; line-height: 1;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule);
}
.cal b { display: block; font-size: 1.5rem; font-weight: 700; }
.cal span {
  display: block; margin-top: .22rem;
  font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent);
}
/* A date known only to the month sets the month where the day would be, so
   it needs the smaller size or it overruns the tile. */
.cal.tbc b { font-size: .95rem; }
.soon .t { display: block; font-weight: 700; line-height: 1.3; }
.soon .w { display: block; margin-top: .15rem; font-size: .86rem; color: var(--ink-soft); }
.soon-reg { margin: 0; padding: 0 .9rem .7rem; font-size: .88rem; }
