/* ==========================================================================
   SREO Consulting — site styles
   Editorial system re-toned to the SREO logo: navy #242460, cyan #0A90D2
   ========================================================================== */

@font-face { font-family: 'Public Sans'; src: url('./assets/fonts/sans-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Public Sans'; src: url('./assets/fonts/sans-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Public Sans'; src: url('./assets/fonts/sans-600.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'Public Sans'; src: url('./assets/fonts/sans-700.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'Libre Caslon Display'; src: url('./assets/fonts/serif-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }

/* The two faces the reader sees before the web fonts arrive. Each is a font already on
   their machine, stretched and re-seated so that it occupies exactly the space the real
   face will: the swap then changes the shapes of the letters and nothing else, and no
   line of text moves. The numbers are measured rather than derived: size-adjust is the
   ratio the browser itself gives for six real lines from these pages set in both faces,
   which counts the kerning and shaping that a sum of advance widths in the font's tables
   does not (that arithmetic said 102.90 and 88.87, and left headlines wrapping
   differently). Each override is then the real font's own metric divided by that ratio,
   so the borrowed face keeps the same line box while its letters are the wrong width.
       Public Sans over Arial       width ratio 1.0383, ascender .950, descender .225
       Libre Caslon over Georgia    width ratio 0.8352, ascender .970, descender .266
   Liberation Sans and Helvetica are listed with Arial because they carry Arial's metrics.
   If none of the named fonts is on the machine the face is simply unusable and the family
   list below falls through to the plain stack, as it did before. */
@font-face {
  font-family: 'Public Sans Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 102.70%;
  ascent-override: 92.50%;
  descent-override: 21.91%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Caslon Fallback';
  src: local('Georgia');
  size-adjust: 83.50%;
  ascent-override: 116.17%;
  descent-override: 31.86%;
  line-gap-override: 0%;
}

:root {
  /* Brand, sampled from the SREO logo */
  --navy: #242460;
  --navy-deep: #1B1B4A;
  --navy-soft: #3A3A80;
  --cyan: #0A90D2;
  /* Accent text on every light ground. Bound by the darkest of them,
     --surface-contact #ECEDF3, where this clears 4.5:1 with 4.99. The previous
     #0B76AD sat at 4.27 there and 4.35 on --surface, so the 12px section labels
     on both grey bands failed SC 1.4.3, as did the 9px .tag on --cyan-tint.
     Same hue and saturation as --cyan (200deg, 88%), one step deeper in lightness. */
  --cyan-deep: #0A6B9C;
  --cyan-bright: #6FC3EF;
  --cyan-tint: #E6F3FB;

  /* Surfaces and text */
  --background: #F8F7F3;
  --surface: #EFEFF3;
  --surface-contact: #ECEDF3;
  --text-primary: #1C1C3E;
  --text-muted: #4E506A;   /* a shade darker since 2026-09-30 (readability pass): 7.4:1 on the paper */
  --border: #DADAE3;
  /* The rule under an unfocused field is that field's only boundary, so it is held to
     the 3:1 of SC 1.4.11 on --surface-contact (3.14). The hairline --border is 1.19
     there and the previous #B4B5C6 was 1.73: fine for a decorative rule, not for the
     one thing that says "you can type here". */
  --input-border: #82849A;
  --input-error: #A23632;

  /* Inverse (on navy) */
  --inverse-text: #F4F3FB;
  --inverse-muted: #B9BAD9;
  --inverse-border: #4B4B85;

  /* Monochrome sub-palette: Team section only */
  --ink: #101014;
  --ink-raised: #1B1B21;
  --ink-glow: #26262E;
  --ink-line: #33333D;
  --ink-mark: #55555F;
  --ink-initial: #72727E;
  --ink-label: #9A9AA6;
  --ink-muted: #B4B4BE;
  --ink-text: #FFFFFF;

  /* Roles */
  --primary: var(--navy);
  --primary-hover: var(--navy-deep);
  --accent: var(--cyan-deep);
  --accent-bright: var(--cyan);
  /* The map's country fills (Part M): a slate mixed from the navy, 3.5:1 against the page and
     3.7:1 against the navy outline, so the shapes read as graphics at WCAG AA; the country in
     hand takes the deep cyan, 5.4:1. The old 9% navy tint sat at 1.15:1. */
  --map-fill: #7F81A0;
  --map-fill-active: var(--cyan-deep);

  /* Type */
  --sans: 'Public Sans', 'Public Sans Fallback', -apple-system, 'Segoe UI', Arial, sans-serif;
  --serif: 'Libre Caslon Display', 'Caslon Fallback', Georgia, 'Times New Roman', serif;
  --mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;

  /* Motion and depth */
  --ease-out: cubic-bezier(0.4, 0, 0.2, 1); /* = --ease-state, see motion.css */

  /* Depth. Three steps, no more, each one a tight contact shadow under the thing
     plus a wider ambient one, both cast in the page's own ink rather than black:
     a grey shadow on a warm paper ground reads as dirt. The steps are not a scale
     of prettiness but a scale of detachment — 1 is still attached to the page,
     2 hovers just off it, 3 has left it. Nothing on this site carries a resting
     shadow; a shadow is always an answer to the reader. See DESIGN.md, "Depth". */
  --shadow-ink: 28 28 62;
  --elev-1: 0 1px 2px rgb(var(--shadow-ink) / .06), 0 6px 16px rgb(var(--shadow-ink) / .06);
  --elev-2: 0 1px 2px rgb(var(--shadow-ink) / .04), 0 10px 26px rgb(var(--shadow-ink) / .08);
  --elev-3: 0 2px 8px rgb(var(--shadow-ink) / .06), 0 32px 64px rgb(var(--shadow-ink) / .18);
  /* Kept as the names the rest of the sheet already used. --shadow-1 is level 1
     unchanged; --shadow-2 was only ever the dialog, which is level 3. */
  --shadow-1: var(--elev-1);
  --shadow-2: var(--elev-3);

  --header-h: 96px;
  --header-h-compact: 76px;

  /* Scrollbar. The resting thumb is the hairline token, which measures 1.30:1 on the
     page: no fainter than the Windows thumb it replaces (#CDCDCD on #F0F0F0, 1.25:1),
     and the pointer that can drag it darkens it to 2.12:1 on the way. */
  --scroll-thumb: var(--border);
  --scroll-thumb-hover: #AEAFC0;
}

* { box-sizing: border-box; }

/* Where an anchor jump comes to rest. It is set once on the scroller rather than as a
   scroll-margin on each target, so a target added later cannot be forgotten; a target
   that needs more room still says so itself (the service blocks, the country cards).
   The header is in its compact state by the time any jump lands, because a jump means
   the page has scrolled, so the compact height plus 24px is the clearance. */
html { scroll-padding-top: calc(var(--header-h-compact) + var(--s-24)); }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

body {
  margin: 0;
  background: var(--background);
  color: var(--text-primary);
  font-family: var(--sans);
  font-size: 17px;   /* 16 until the readability pass of 2026-09-30 */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

button, input, textarea { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
a { color: inherit; text-decoration: none; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: .4; }   /* the full state set is under "Interaction states" at the end */
img { max-width: 100%; }

/* Selected text keeps its own colour and takes a wash of the accent behind it: at 15%
   the tint is light enough that every contrast on the page survives being selected,
   which a solid highlight cannot promise on six different grounds. */
::selection { background: color-mix(in srgb, var(--accent-bright) 15%, transparent); }

/* One focus ring, everywhere, and never a shadow: a shadow is painted under the next
   element and is dropped entirely in forced colours, where an outline survives.
   --accent-bright clears 3:1 on every ground the site has (3.03 on the contact band,
   3.97 on navy), which --accent, darker, does not manage on navy. The radius is the
   ring's, and an outline takes its corners from the element, so 2px is set here rather
   than on the element itself: it lasts only as long as the ring does. */
button:focus-visible, a:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible, summary:focus-visible {   /* summary and select: the Services folds and the Contact fields of 2026-10-14 */
  outline: 2px solid var(--accent-bright);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Scrollbars, only where there is a pointer that can grab one. A touch device scrolls
   with the finger and shows an overlay bar that fades; styling it there replaces that
   platform behaviour with a permanent gutter, which is a loss, not a gain. */
@media (hover: hover) and (pointer: fine) {
  html { scrollbar-color: var(--scroll-thumb) transparent; }   /* Firefox has no thumb hover */
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 5px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }
  /* A corner left unpainted shows the engine's own grey square where two bars meet. */
  ::-webkit-scrollbar-corner { background: transparent; }
}

p, h1, h2, h3, figure, dl { margin: 0; }
h1, h2 { font-family: var(--serif); font-weight: 400; letter-spacing: -.025em; }
h1 { font-size: 76px; line-height: 1.03; }
h2 { font-size: 52px; line-height: 1.08; }
h3 { font-size: 21px; font-weight: 500; line-height: 1.35; letter-spacing: -.02em; }
em { font-style: normal; }
ul, ol { padding: 0; margin: 0; list-style: none; }
p { max-width: 42.84em; }

/* Figures that are read against one another line up: every stat strip, both ledgers, the
   project cards' facts, the timeline and every year take tabular figures. Public Sans
   carries a tabular set (a digit is 0.28em wide in every weight); the mono stack is fixed
   width by nature. The display serif has no tabular set at all, so the stat numerals keep
   proportional figures whatever is asked of them, and app.js pins each count to its final
   width instead (see the Stat strip note in DESIGN.md). */
.stats, .ledger, .fw-ledger, .project-card dl, .timeline, .eyebrow, .step-number, .stats-source, .source-note {
  font-variant-numeric: tabular-nums;
}

.container { width: calc(100% - 128px); max-width: 1280px; margin-inline: auto; }
/* 72px between sections, from 96 (Part I, I4): the homepage ran to twice a competitor's
   height, and nine sections' worth of 24px each is the one density lever that costs no
   content. 72 is still a clear break at 1440 and reads as generous at 1024. */
.section { padding-block: var(--s-72); }

.eyebrow {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: .075em;
  text-transform: uppercase;
}
.small { font-size: 14px; line-height: 1.6; }
.muted { color: var(--text-muted); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }

.skip-link { position: absolute; top: -100px; left: 16px; z-index: 100; background: var(--primary); color: var(--background); padding: var(--s-12); }
.skip-link:focus { top: 10px; }

/* Buttons ---------------------------------------------------------------- */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  min-height: 48px;
  padding: var(--s-12) var(--s-20);
  border: 1px solid transparent;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
  border-radius: 0;
  transition: background-color .2s var(--ease-out), color .2s var(--ease-out), border-color .2s var(--ease-out), transform .2s var(--ease-out);
}
/* --btn-bg and --btn-fg follow each variant through hover so the pressed state (end of file)
   can darken whatever is showing by 6%, and the loading dots can take the label's colour. */
.primary { --btn-bg: var(--primary); --btn-fg: var(--inverse-text); background: var(--btn-bg); color: var(--btn-fg); }
.primary:hover { --btn-bg: var(--primary-hover); }
.secondary { --btn-bg: transparent; --btn-fg: var(--primary); border-color: var(--primary); background: var(--btn-bg); color: var(--btn-fg); }
.secondary:hover { --btn-bg: var(--primary); --btn-fg: var(--inverse-text); }
.inverse { --btn-bg: var(--background); --btn-fg: var(--navy); background: var(--btn-bg); color: var(--btn-fg); }
.inverse:hover { --btn-bg: #FFFFFF; }

.text-button {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 44px;
  padding: var(--s-4) 0;
  background: none;
  color: var(--text-primary);
  border: 0;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
  text-align: left;
  --btn-fg: var(--text-primary);
  transition: color .2s var(--ease-state);
}
.text-button:hover { color: var(--accent); }

/* Icons -------------------------------------------------------------------
   One inline sprite, built by tools/build-icons.js. Geometry lives here rather than on
   the symbols: stroke, fill, stroke-width and the caps are inherited SVG properties, so
   set once on .icon they reach the shadow content that <use> creates. Put them on the
   symbol paths instead and a rule like .ledger-row.is-open .chev { stroke: var(--accent) }
   would no longer reach the path.
   Stroke width is 1.5 in a 24-unit box, so it scales with the icon: 1px at 16, 1.5px at
   24. non-scaling-stroke would make the small ones heavy against 15px text. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.icon {
  width: 16px; height: 16px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Seats the icon on the text. `middle` centres it on the x-height, which tracks
     font-size; a fixed em offset from the baseline cannot, because the icon is a fixed
     pixel size while the text around it is not. The nudge is the optical correction from
     x-height centre to cap centre, measured in the browser. Only bites in running text:
     inside .button and .text-button the icon is a flex item and align-items centres it. */
  vertical-align: middle;
  position: relative;
  top: -0.09em;   /* measured, not guessed: middle leaves the icon 0.095em low at 12-21px */
}
/* Inside a flex control align-items already centres the icon, so the flow nudge must be
   taken back off or it rides 1.25px high. Measured before and after. */
.button > .icon, .text-button > .icon, .icon-link > .icon, .contact-email > .icon,
.icon-button > .icon, .ledger-head > .chev, .form-submit > .icon, .pillar ul a > .icon { top: 0; }
.button .icon { width: 20px; height: 20px; }
.icon-lg { width: 24px; height: 24px; }
/* An icon that is the whole control still needs a finger-sized target. */
/* A link whose affordance includes an icon still needs a finger-sized target, even
   where the text alone would be 16px tall. */
.icon-link { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; }
.icon-button { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; }

/* Link underlines --------------------------------------------------------------
   A 1px linear-gradient in currentColor on an inline .link-text span, growing from 0% to
   100% of its width from the left over 300ms. Not border-bottom and not text-decoration:
   on an inline box a background follows each line fragment, so a label that wraps is
   underlined line by line. A flex link turns its children into blocks, which would lose
   that, so there the inline span sits inside a .link-label wrapper that is the flex item.
   The current page and the current section keep the line drawn. */
.link-label { min-width: 0; }
.link-text {
  /* A background line cannot skip descenders the way text-decoration can, and at the
     inline box's own bottom it touched them (0px, measured). Vertical padding on an inline
     box moves the line down without changing the line box, so the layout does not move. */
  padding-bottom: 0.14em;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: left bottom;
  background-size: 0% 1px;
  transition: background-size 300ms var(--ease-state);
}
:is(a, button):focus-visible .link-text,
a[aria-current]:not([aria-current="false"]) .link-text, .desktop-nav a.is-spied .link-text { background-size: 100% 1px; }   /* .is-spied: a link to another page standing for the homepage section in view */
@media (hover: hover) {
  :is(a, button):hover .link-text { background-size: 100% 1px; }
  .contact-email:hover, .closing-email:hover { background-size: 100% 1px, 100% 1px; }
}
.contact-email:focus-visible, .closing-email:focus-visible { background-size: 100% 1px, 100% 1px; }

/* The icon nudge follows what the link does: a link that leaves the site moves out and up,
   one that goes forward moves right, one that goes further down the page moves down.
   Direct-child icons only, so it never reaches an arrow inside an E3 swap slot. */
:is(a, button) > .icon { transition: transform 200ms var(--ease-state); }
@media (hover: hover) {
  :is(a[href^="http"], a[href^="mailto:"]):hover > .icon { transform: translate(2px, -2px); }
  /* A link to a place on another page goes forward, not down: the reader leaves this
     page first. Without this, the inner pages' "Contact us" matched none of the three. */
  :is(a[href$=".html"], a[href*=".html#"], a[href*=".html?"], button[data-dialog], button[type="submit"]):hover > .icon { transform: translate(2px, 0); }   /* .html? since 2026-09-25: the Contact page links carry the service */
  a[href^="#"]:hover > .icon { transform: translate(0, 2px); }
  /* A file download comes down to the reader: the same direction as an in-page link. Until
     Part I the header had no plain-icon download button, so nothing had asked for this. */
  a[download]:hover > .icon { transform: translate(0, 2px); }
}

/* Arrow swap -------------------------------------------------------------------
   On the four named calls to action the arrow sits in a slot the size of the icon, with
   overflow hidden. On hover the arrow leaves along its own direction while a duplicate
   arrives from behind it, 300ms each, the arrival 40ms after the departure, so the arrow
   appears to loop. The label does not move.
   Each delay lives on the state being moved *to*, so leaving hover reverses the order as
   well: the duplicate leaves first and the original follows it back.
   Down and download arrows loop vertically. The brief described a sideways loop, which
   suits an arrow pointing right; on an arrow pointing down it reads as a mismatch. */
.icon-swap { position: relative; display: inline-block; flex: 0 0 auto; width: 16px; height: 16px; overflow: hidden; }
.button .icon-swap { width: 20px; height: 20px; }
.icon-swap > .icon { position: absolute; inset: 0; width: 100%; height: 100%; }
.icon-swap > .swap-a { transform: translate(0, 0); transition: transform 300ms var(--ease-state) 40ms; }
.icon-swap > .swap-b { transform: translate(-100%, 0); transition: transform 300ms var(--ease-state) 0ms; }
.icon-swap.is-down > .swap-b { transform: translate(0, -100%); }
@media (hover: hover) {
  :is(.button, .text-button):hover .icon-swap > .swap-a { transform: translate(100%, 0); transition-delay: 0ms; }
  :is(.button, .text-button):hover .icon-swap.is-down > .swap-a { transform: translate(0, 100%); }
  :is(.button, .text-button):hover .icon-swap > .swap-b { transform: translate(0, 0); transition-delay: 40ms; }
}

/* Header ----------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  z-index: 50;
  background: rgba(248, 247, 243, .92);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--ease-out), box-shadow .3s var(--ease-out);
}
/* The header is level 1, not 2: it is still part of the page, it has only started
   to move over it. The hairline in front of the two elevation shadows is the seam
   the border-bottom cannot draw on its own over a photograph. */
.site-header.is-scrolled { border-bottom-color: var(--border); box-shadow: 0 1px 0 rgb(var(--shadow-ink) / .02), var(--elev-1); }

/* Scroll progress. It sits at bottom: -1px, on the header's own hairline rule, so the
   rule already under the nav fills with accent rather than a second line appearing.
   Deliberately untransitioned: it tracks the reader's scroll, and easing it would make it
   lag behind their own thumb. Sticky is a positioned ancestor, so absolute resolves here. */
.scroll-progress {
  position: absolute; left: 0; right: 0; bottom: -1px;
  height: 1px; overflow: hidden; pointer-events: none;
}
.scroll-progress span {
  display: block; height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
}

.nav-shell {
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  transition: height .3s var(--ease-out);
}
.is-scrolled .nav-shell { height: var(--header-h-compact); }

.brand { display: inline-flex; align-items: center; flex-shrink: 0; }
.brand img { display: block; height: 62px; width: auto; transition: height .3s var(--ease-out); }
.is-scrolled .brand img { height: 50px; }

.desktop-nav { display: flex; align-items: center; gap: 28px; }
.desktop-nav a {
  position: relative;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  padding: var(--s-12) 0;
  color: var(--text-primary);
  transition: color .2s var(--ease-out);
}
.desktop-nav a:hover { color: var(--navy); }

/* One filled button above the fold, and it is the hero's "Discuss a project" (Part I, I1).
   The header's Contact is a text link in the navigation's own style with the E4 rule under
   it, and the capacity statement stays the outlined one beside it. Three competing buttons
   in the first screen had left the page's primary action the weakest of them. */
/* Contact us is the header's one filled button (2026-09-30, Allam): the action the whole site leads to.
   The capacity statement beside it is a quiet text link with its download mark. */
.nav-cta { min-height: 44px; padding: var(--s-10) var(--s-18); gap: var(--s-12); white-space: nowrap; }
.nav-cta .icon { width: 15px; height: 15px; }
/* Logo hover (2026-09-25, CEO review): each tile is a button; hovering or focusing it shows what the project
   record holds for that partner, one line under the tile. Built by tools/build-partners.js. */
.logo-tile { position: relative; }
button.logo-tile { appearance: none; border: 0; background: none; padding: 0; font: inherit; color: inherit; cursor: default; display: inline-flex; align-items: center; justify-content: center; min-height: 44px; }   /* a tap target even where the logo canvas scales under 44px */
.logo-tip { position: absolute; left: 50%; top: calc(100% + var(--s-10)); z-index: 30; transform: translateX(-50%); margin: 0; width: max-content; max-width: min(34ch, calc(100vw - 40px)); padding: var(--s-10) var(--s-12); background: var(--navy); color: var(--inverse-text); font-family: var(--sans); font-size: 13px; line-height: 1.45; letter-spacing: 0; text-transform: none; text-align: left; text-wrap: pretty; box-shadow: var(--elev-2, 0 8px 24px rgba(28, 28, 62, .18)); opacity: 0; visibility: hidden; transition: opacity var(--dur-1, 160ms) var(--ease-state), visibility 0s linear var(--dur-1, 160ms); pointer-events: none; }
.logo-tip strong { display: block; font-weight: 500; }
.logo-tip span { display: block; margin-top: var(--s-2); font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--cyan-bright); }
.logo-tile:hover > .logo-tip, .logo-tile:focus-visible > .logo-tip, .logo-tile:focus-within > .logo-tip { opacity: 1; visibility: visible; transition-delay: 0s; }
.logo-tile:nth-child(8n + 1) > .logo-tip { left: 0; transform: none; }   /* the first and last of each row of eight */
.logo-tile:nth-child(8n) > .logo-tip { left: auto; right: 0; transform: none; }
/* hidden tooltips still take horizontal room: the row clips sideways only, and on a phone (four tiles across)
   the first column opens from its left edge and the last from its right */
.trust { overflow-x: clip; }
@media (max-width: 767px) {
  .logo-tile:nth-child(4n+1) > .logo-tip { left: 0; right: auto; transform: none; }
  .logo-tile:nth-child(4n) > .logo-tip { left: auto; right: 0; transform: none; }
}
/* LinkedIn in the header beside Contact us (Haytham Sarraf, 2026-09-25); the footer keeps its own */
.nav-linkedin { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; margin-left: var(--s-8); --mark-fg: var(--text-muted); --mark-line: var(--border); --mark-accent: var(--navy); }
/* The Contact page (2026-09-25): the form lifted from the homepage, with the offices beside it */
.contact-page { padding-top: var(--s-56); }
/* (the capacity statement link's own top margin went with the old layout on 2026-10-06: it sits in the ways ledger now) */
.form-context { margin-top: var(--s-16); font-family: var(--mono); font-size: 12px; letter-spacing: .04em; color: var(--navy); }
/* The capacity statement, one click from every page. Hamza Qattan asked on 2026-09-22 for
   the download to be more visible than a text link two screens down; here it sits beside
   the contact button as its quieter twin, outlined rather than filled so the one action the
   page most wants still reads first. Between 1024 and 1279px the header has room for the
   arrow but not the words, so the words go to assistive technology only and the title
   carries them for a hover; below 1024px it moves into the menu with its words back. */
.nav-download { display: inline-flex; align-items: center; gap: var(--s-8); min-height: 44px; font-size: 14px; font-weight: 500; white-space: nowrap; color: var(--text-primary); }
.nav-download .icon { width: 16px; height: 16px; color: var(--accent); }
.menu-toggle { display: none; }
.mobile-nav[hidden] { display: none; }

/* Hero ------------------------------------------------------------------- */
.hero-content { padding-top: var(--s-48); padding-bottom: var(--s-48); }
.hero-eyebrow { display: flex; justify-content: space-between; color: var(--text-muted); margin-bottom: var(--s-36); }
.hero-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 48px; align-items: end; }
.hero h1 em { color: var(--accent); }
/* Global Insight in the dark blue, Local Access in the light (Daniel Seckman, 2026-09-30) */
.hero h1 .hero-global { color: var(--navy); }
.hero-aside { padding-bottom: var(--s-6); }
.hero-aside p { font-size: 17px; line-height: 1.65; color: var(--text-muted); max-width: 380px; text-wrap: pretty; }
.hero-actions { display: flex; align-items: flex-start; flex-direction: column; gap: 14px; margin-top: var(--s-28); }
.hero-actions .primary { min-width: 214px; }
.hero-actions .text-button { font-size: 13px; }

/* overflow: clip, not hidden — the same distinction .ground-up draws, for a second
   reason. hidden makes a box a scroll container, and the drift inside it then measures
   itself against that box instead of the window: a scroll container that never scrolls
   holds the picture at the midpoint of its travel forever. clip creates no such thing. */
.field-photo { position: relative; height: 400px; overflow: clip; background: var(--surface); }   /* the 3px cyan rule on its top edge came off on 2026-09-24 at Allam's request */
.field-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 45%;
  /* no filter: the site's grade, saturate(.72) contrast(1.02), is baked into the files by
     tools/build-photos.js (Part M), so nothing repaints a full-bleed picture */
  display: block;
}
.field-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(36, 36, 96, .06) 0%, rgba(36, 36, 96, 0) 30%, rgba(27, 27, 74, .55) 100%);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .js .field-photo img { animation: photo-settle 14s var(--ease-out) both; }
  @keyframes photo-settle { from { transform: scale(1.05); } to { transform: scale(1); } }
}
/* The hero holds three frames (brief F3), stacked; only the first exists for a reader
   without script, in static review, or under reduced motion — the rest are switched on
   in motion.css. Paint order inside the hero, bottom to top: the frames at 0, 1 and 2
   (the one arriving on top), the scrim at 3, the reveal cover at 4, the caption at 5.
   The bands keep one picture and none of this. */
.hero-frame:not(:first-child) { display: none; }
.field-photo::after { z-index: 3; }
.field-photo figcaption {
  position: absolute;
  z-index: 5;
  left: max(64px, calc((100% - 1280px) / 2));
  right: max(64px, calc((100% - 1280px) / 2));
  bottom: 24px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  color: var(--inverse-text);
}
/* The veil the hero takes on as it recedes (motion.css, 2026-09-30): flat navy over the frames
   and under the scrim and the caption, at nothing until the page scrolls it in. */
.hero-dim { position: absolute; inset: 0; z-index: 3; background: var(--navy); opacity: 0; pointer-events: none; }
/* The recede's two sides (2026-09-30): paper-coloured, as wide as the page's gutter, at nothing until the
   scroll grows them in from the edges (motion.css). Above the scrim, under the caption. */
.hero-side { position: absolute; top: 0; bottom: 0; z-index: 4; width: var(--hero-recede, 0px); background: var(--background); transform: scaleX(0); pointer-events: none; }
.hero-side-l { left: -1px; transform-origin: 0 50%; }
.hero-side-r { right: -1px; transform-origin: 100% 50%; }
.field-photo figcaption > span { background: var(--navy); padding: var(--s-8) var(--s-12); font-size: 12px; }
.field-photo figcaption > span:last-child { font-family: var(--serif); font-size: 15px; letter-spacing: 0; }

/* Photo bands (full-bleed, same grade as the hero photo) ------------------- */
.photo-band { position: relative; margin: 0; height: 32vh; min-height: 260px; max-height: 440px; overflow: clip; background: var(--surface); }
.photo-band img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 40%); }   /* the grade is in the files (Part M) */
.photo-band::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(180deg, rgba(36, 36, 96, .06) 0%, rgba(36, 36, 96, 0) 30%, rgba(27, 27, 74, .55) 100%); }
.photo-band figcaption { position: absolute; z-index: 1; left: max(64px, calc((100% - 1280px) / 2)); right: max(64px, calc((100% - 1280px) / 2)); bottom: 24px; display: flex; justify-content: space-between; align-items: center; gap: 20px; color: var(--inverse-text); }
.photo-band figcaption > span { background: var(--navy); padding: var(--s-8) var(--s-12); font-size: 12px; }
.photo-band figcaption > span:last-child { font-family: var(--serif); font-size: 15px; letter-spacing: 0; }
/* The approved stock photographs inside the pages (2026-10-06, Allam's Shutterstock picks). Graded in the files
   like every other photograph (tools/build-photos.js), cut to their focus by object-position. */
/* Each service opens with a photograph at the head of its detail column, 16:9. */
.service-photo { position: relative; margin: 0 0 var(--s-40); aspect-ratio: 16 / 9; overflow: clip; background: var(--surface); }
.service-photo picture, .service-photo img { display: block; width: 100%; height: 100%; }
.service-photo img { object-fit: cover; object-position: var(--focus, 50% 50%); }
/* Three places on Where we work, before "How we work": 4:3 each, the place beneath in the label face. */
.places { padding-block: 0 var(--s-88); }
.places-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-12); }
.place-photo { margin: 0; }
.place-photo picture { display: block; aspect-ratio: 4 / 3; overflow: clip; background: var(--surface); }
.place-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.place-photo figcaption { margin-top: var(--s-12); color: var(--text-muted); }
@media (max-width: 767px) {
  .places { padding-block: 0 var(--s-56); }
  .places-row { grid-template-columns: 1fr; }
}

/* The picture layer -------------------------------------------------------
   Every photograph sits in a <picture> stretched over its figure rather than left
   to flow inside it. That buys two things the figure cannot give on its own, both
   of its pseudo elements being spoken for already (the reveal cover, the scrim):
   a surface for the grain below and for the navy the motion layer lifts off it
   (motion.css, "Photographs arrive in navy"), and one box to move for the scroll
   drift, which leaves the image free to keep its own settle. isolation: isolate
   holds both of those blends inside the picture, so they reach the photograph and
   nothing of the page behind it. */
:is(.field-photo, .photo-band) picture { position: absolute; inset: 0; display: block; isolation: isolate; }

/* Grain -------------------------------------------------------------------
   One fine grey grain over every photograph, so files shot on different cameras in
   different countries share a surface. The tile is a fixed size in CSS pixels and
   is never scaled with the image: that is what keeps it reading as the page's own
   texture rather than as noise in the picture. It is decoration and nothing else,
   so it takes no pointer, and it stands down where it would be a liability — on
   paper, and under forced colours, where an overlay the system cannot recolour
   would be left sitting on a photograph it also cannot. */
:is(.field-photo, .photo-band) picture::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url('./assets/texture-grain.svg');
  background-repeat: repeat;
  background-size: 240px 240px;
  mix-blend-mode: overlay;
  opacity: .1;
  pointer-events: none;
}
@media print, (forced-colors: active) {
  :is(.field-photo, .photo-band) picture::after { content: none; }
}

/* The band answers the pointer --------------------------------------------
   A photo band carries no link and hides no writing, so there is nothing here to
   click and nothing that only a hover would reveal. What it does is lean in: the
   scrim holding the caption eases back and a cyan rule draws itself under the line of
   writing (the grade used to come up too; since Part M no picture carries a filter, so
   that part went). Both are decoration, which is why this is
   allowed to be hover-only — a reader who never hovers is not missing anything.
   Not offered to a finger: on a touch screen the first tap would spend itself on
   the hover state instead of the scroll. */
@media (hover: hover) and (pointer: fine) {
  .photo-band::after { transition: opacity var(--dur-2) var(--ease-state); }
  .photo-band:hover::after { opacity: .8; }
  .photo-band figcaption > span:last-child { position: relative; }
  .photo-band figcaption > span:last-child::after {
    content: '';
    position: absolute;
    left: 12px; right: 12px; bottom: 4px;
    height: 1px;
    background: var(--cyan-bright);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-2) var(--ease-state);
  }
  .photo-band:hover figcaption > span:last-child::after { transform: scaleX(1); }
}

/* Trust ------------------------------------------------------------------ */
.trust { padding-top: var(--s-32); padding-bottom: var(--s-24); }   /* 40 until 2026-09-30: the logos and the figures read as one block of proof */
.trust-heading { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.trust-heading > .eyebrow { color: var(--text-muted); font-size: 11px; }
.logo-row { display: grid; grid-template-columns: repeat(8, 1fr); gap: var(--s-36) 32px; align-items: center; padding-top: var(--s-28); }   /* two rows of eight since 2026-09-30 */
.logo-tile { height: 76px; display: flex; align-items: center; justify-content: center; }
.logo-tile img {
  display: block;
  width: auto;
  max-width: 100%;
  height: 62px;   /* 52 until 2026-09-30: the two rows stand as large as the figures beside them; the files are drawn at 2x */
  object-fit: contain;
  filter: none;   /* the brand's own colour at rest since 2026-10-14 (Daniel Seckman); grey was the resting state before */
  mix-blend-mode: multiply;
  opacity: 1;
  transition: filter .35s var(--ease-out), opacity .35s var(--ease-out);
}
.logo-tile:hover img, .logo-tile:focus-visible img { filter: grayscale(1) brightness(var(--logo-ink, 1)); opacity: .82; }
/* Sixteen logos in colour; one grey under the pointer. Daniel Seckman asked on 2026-10-14 for
   "the opposite of what we have now": the wall stood grey and a logo took its colour on hover.
   The grey state keeps its weights. Taking the colour out of a row of brands does not make them
   weigh the same: UNICEF's cyan leaves ink at 0.44 of black where IOM's leaves 0.81,
   and next to each other in one grey the pale ones read as whispers. Each number below
   is the brightness that brings that logo's grey state down to a common weight of 0.70,
   measured from the file itself by tools/build-client-logos.py and printed by it.
   Darkening only: lifting a mark that is already dark enough would wash out a logo that
   was right to begin with. At rest there is no filter at all, so what stands on the page is
   the brand's own colour at its own weight, untouched. */
.logo-tile img[src$="unicef.png"]     { --logo-ink: .53; }
.logo-tile img[src$="wfp.png"]        { --logo-ink: .59; }
.logo-tile img[src$="unhcr.png"]      { --logo-ink: .72; }
.logo-tile img[src$="who.png"]        { --logo-ink: .73; }
.logo-tile img[src$="world-bank.png"] { --logo-ink: .74; }
.logo-tile img[src$="undp.png"]       { --logo-ink: .89; }
.logo-tile img[src$="nrc.png"]        { --logo-ink: .87; }   /* the second row (2026-09-30), from tools/build-one-logo.js */
.logo-tile img[src$="ilo.png"]        { --logo-ink: .60; }
.logo-tile img[src$="care.png"]       { --logo-ink: .69; }
.logo-tile img[src$="welthungerhilfe.png"] { --logo-ink: .63; }
/* iom.png and giz.png are already at or past the target and are left alone. */

/* Stats (2026-09-27) ------------------------------------------------------ */
/* Two figures on the homepage (projects, founded), three on Where we work. Each cell is one
   unit: the numeral large in the display serif, its label beside it in mono and seated on the
   numeral's baseline, the "+" small, raised and in the accent. The units centre in their
   cells between hairlines, so two read as a spread and three as a row. Below 768px the cells
   stack as ruled rows. Tabular figures come from .stats. */
.stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); border-top: 1px solid var(--border); padding-block: var(--s-36) 0; }   /* tighter, and no closing rule, since 2026-09-30 (Allam: "tighter stats strip"); section 01's heading follows */
.stat { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-end; gap: var(--s-8) var(--s-20); padding-inline: var(--s-24); border-left: 1px solid var(--border); }
.stat:first-child { border-left: 0; }
.stat-value { font-family: var(--serif); font-size: clamp(64px, 8.4vw, 128px); line-height: .78; letter-spacing: -.035em; color: var(--text-primary); white-space: nowrap; }
/* No fixed width here: app.js pins each count to its own final width while it runs. */
.stat-value span { display: inline-block; }
.stat-value .stat-plus { font-size: .56em; vertical-align: .6em; margin-left: .04em; letter-spacing: 0; color: var(--cyan-deep); }
.stat-meta { position: relative; font-family: var(--mono); font-size: 12px; line-height: 1.5; letter-spacing: .12em; text-transform: uppercase; }   /* the label button inherits this: the site's mono caps */
.stat .eyebrow { color: var(--text-muted); }
/* A label with nothing more to say is plain text: no hairline, no tooltip (Founded, since 2026-09-27). */
.stat-label { display: inline-flex; align-items: flex-end; min-height: 44px; margin-bottom: .06em; padding-bottom: var(--s-6); }

/* Timeline: the lower rule of the stat strip, drawn 2013 to 2026 --------- */
.timeline { padding-top: var(--s-32); }
.timeline-rail { position: relative; }
.timeline-line { display: block; height: 1px; background: var(--border); transform-origin: left center; }
.timeline-marks { position: relative; height: 54px; }
.timeline-marks li {
  position: absolute; left: var(--at); top: 0;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  transform: translateX(-50%); white-space: nowrap;
}
.timeline-marks li:first-child { transform: none; align-items: flex-start; }
.timeline-marks li:last-child { transform: translateX(-100%); align-items: flex-end; }
.tl-tick { width: 1px; height: 9px; background: var(--border); }
.tl-year { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; color: var(--text-primary); }
.tl-event { font-family: var(--mono); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); }

/* Section headings ------------------------------------------------------- */
.section-heading { display: flex; align-items: end; justify-content: space-between; gap: 48px; margin-bottom: var(--s-36); }
.section-heading > p { max-width: 366px; color: var(--text-muted); text-wrap: pretty; }
.section-index { color: var(--accent); margin-bottom: var(--s-24); }

/* Sticky section label ---------------------------------------------------
   Once the reader has scrolled past a section's heading, a copy of its mono
   label pins under the header and travels to the end of the section, where the
   next section's label takes over. app.js builds it from the label already in
   the page, so the wording has one source, and hides it from assistive
   technology because the real label is still in the document.
   The copy carries .container, so it lines up with the section's own content
   column at every width, and it paints the section's background: a bare label
   would have body text sliding through it. Desktop only, per DESIGN.md. */
.section-pin { display: none; }
/* A short window cannot spare 45px of fixed furniture, and browser zoom shrinks the
   viewport in CSS pixels without shrinking the band, so the height is a condition too. */
@media (min-width: 900px) and (min-height: 600px) {
  /* The band is 14 + 12 of padding around an 18px line, 44px: --pin-h, which the scroll
     padding below and the test suites depend on, is declared once in tokens.css. */
  /* .has-pin is set by app.js on the same pass that inserts the copy, rather than
     :has(> .section-pin): if a browser did not understand :has it would drop this
     rule, and the rail would then position itself against the page instead of the
     section. The scroll-padding rule below uses :has, where failing means only
     that the older offset stays. */
  .has-pin { position: relative; }
  /* The rail spans the whole section, so it must not intercept anything; the band
     itself must, or a click would land on a control the band is covering. Clipped,
     because the fade below the band would otherwise hang past the section's end and
     wash the top of the next one. overflow: clip does not create a scroll container,
     so the band still sticks to the page. */
  .section-pin { display: block; position: absolute; inset: 0; z-index: 20; overflow: clip; pointer-events: none; user-select: none; }
  .section-pin > .section-index {
    position: sticky;
    top: var(--header-h-compact);
    margin-block: 0;
    padding-block: var(--s-14) var(--s-12);
    /* visibility, not opacity alone: text at opacity 0 is still found by find-in-page,
       which would send the reader to a label they cannot see. It is held visible until
       the fade has finished, so the band still leaves gently. */
    opacity: 0;
    visibility: hidden;
    background: var(--pin-bg, var(--background));
    pointer-events: auto;
  }
  /* A section may set its own label smaller (Standards does). The band keeps one
     height on every section, so the extra class is there to outrank that. */
  .section-pin > .eyebrow.section-index { font-size: 13px; line-height: 18px; }   /* the band stays --pin-h (44px) with the 13px label */
  /* A short fade under the band, so a line passing beneath it reads as fading out
     rather than sliced. Same device as the dialog's sticky row, and it spares the
     page a second hairline directly under the header's own. */
  .section-pin > .section-index::after {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 100%;
    height: 20px;
    background: linear-gradient(var(--pin-bg, var(--background)), transparent);
    pointer-events: none;
  }
  /* Some sections carry .container themselves and others hold one inside. When
     the section is the container, the copy already spans the content column and
     must not inset itself a second time. */
  .container > .section-pin > .section-index { width: 100%; max-width: none; }
  .is-pinned > .section-pin > .section-index { opacity: 1; visibility: visible; }
  /* While a band is on screen it occupies the strip the browser would otherwise
     scroll a target into: a keyboard reader tabbing back up a section would land
     underneath it. .is-pinned is exactly that state. */
  html:has(section.is-pinned) { scroll-padding-top: calc(var(--header-h-compact) + var(--pin-h) + var(--s-24)); }
  .work-section { --pin-bg: var(--surface); }
  .contact-section { --pin-bg: var(--surface-contact); }
  .team { --pin-bg: var(--ink); }
  /* .closing is deliberately absent: it is one screen tall, so it never earns a band,
     and its background is a gradient over navy — a flat token here would be a safety
     net that painted a darker rectangle with a seam down it. */
  .ground-up { --pin-bg: var(--navy); }
}
/* A landscape sheet is wider than 900px, and a pinned label would print as a
   duplicate of a heading the reader can already see. */
@media print { .section-pin { display: none; } }

/* A host whose opening rule is drawn when it arrives; see motion.css, "Hairline
   draws". The positioning lives here rather than in the motion layer, so that the
   layout is the same whether or not that layer applies. */
[data-draw] { position: relative; }

/* Services ledger --------------------------------------------------------- */
/* Deliberately unlike the Sectors grid: full-width rows, serif titles, and a
   detail panel that expands in place. */
.ledger { border-top: 1px solid var(--border); }
.ledger-row { border-bottom: 1px solid var(--border); }
.ledger-row h3 { margin: 0; font: inherit; letter-spacing: normal; }
.ledger-head {
  display: grid;
  grid-template-columns: 56px minmax(0, 5fr) minmax(0, 5fr) 40px;
  align-items: baseline;
  gap: 24px;
  width: 100%;
  padding: var(--s-24) 0;
  background: none;
  border: 0;
  text-align: left;
  color: var(--text-primary);
  transition: color var(--dur-1) var(--ease-state);
}
.ledger-no { color: var(--accent); }
.ledger-title { font-family: var(--serif); font-size: 27px; line-height: 1.2; letter-spacing: -.02em; }
.ledger-line { font-size: 15px; line-height: 1.55; color: var(--text-muted); }
.chev { width: 18px; height: 18px; justify-self: center; align-self: center; fill: none; stroke: currentColor; color: var(--navy); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-2) var(--ease-state), color var(--dur-1) var(--ease-state); }
@media (hover: hover) {
  .ledger-head:hover .ledger-title { color: var(--accent); }
  .ledger-head:hover .chev { color: var(--accent); }
}
.ledger-row.is-open .ledger-title { color: var(--accent); }
.ledger-row.is-open .chev { color: var(--accent); transform: rotate(-180deg); }

/* 0fr to 1fr is the height animation; the inner box does the clipping. */
/* Expanding detail. The panel clips and its height animates, so the detail is
   revealed from the top: the content always has its natural height, and no blank
   band can grow ahead of it.
   Without JavaScript the panel keeps its natural height and every detail is readable.
   Without interpolate-size (Firefox today) it opens and closes instantly, which is a
   working control rather than a broken one. */
.ledger-panel { overflow: hidden; }
html.js .ledger-panel { height: 0; visibility: hidden; transition: visibility 0s linear var(--dur-2); }
html.js .ledger-row.is-open .ledger-panel { height: auto; visibility: visible; transition-delay: 0s; }
@supports (interpolate-size: allow-keywords) {
  html.js .ledger-panel {
    interpolate-size: allow-keywords;
    transition: height var(--dur-2) var(--ease-state), visibility 0s linear var(--dur-2);
  }
  html.js .ledger-row.is-open .ledger-panel { transition: height var(--dur-2) var(--ease-state), visibility 0s; }
}
.ledger-panel-inner {
  display: grid;
  grid-template-columns: 56px minmax(0, 5fr) minmax(0, 5fr) 40px;
  gap: 0 24px;
  padding-bottom: var(--s-8); /* clearance for the focus ring, which the clip would cut */
}
.ledger-col { grid-column: 2; padding-bottom: var(--s-28); }
.ledger-col + .ledger-col { grid-column: 3; }
.ledger-col > .eyebrow { color: var(--accent); display: block; padding-bottom: var(--s-12); border-bottom: 1px solid var(--border); margin-bottom: var(--s-12); }
.ledger-col li { font-size: 15px; line-height: 1.6; color: var(--text-muted); padding: var(--s-4) 0; }
.ledger-col li::before { content: ''; display: inline-block; width: 10px; height: 1px; background: var(--cyan); vertical-align: middle; margin-right: var(--s-12); }
.ledger-sectors { grid-column: 2 / 4; display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: baseline; padding: var(--s-16) 0 var(--s-28); border-top: 1px solid var(--border); font-size: 13px; color: var(--text-muted); max-width: none; }
.ledger-sectors .eyebrow { color: var(--text-muted); font-size: 10px; }
/* A service from the statement's list that this row also covers. Built like the sectors line,
   but in the service text colour and with an accent label, because it names work, not a sector. */
.ledger-also { grid-column: 2 / 4; display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: baseline; padding: var(--s-16) 0; border-top: 1px solid var(--border); font-size: 14px; color: var(--text-primary); max-width: none; }
.ledger-also .eyebrow { color: var(--accent); font-size: 10px; }
/* The two lines stack in the panel, so their labels share one width and the values align. */
.ledger-also > .eyebrow, .ledger-sectors > .eyebrow { flex: 0 0 20ch; white-space: nowrap; }
.ledger-also .also-items { flex: 1 1 0; min-width: 0; }
/* The added services are lists: each name is a flex item, so a line breaks between names, never
   inside one unless a single name is wider than the column. The dot is decoration, hidden from
   screen readers by the alt-text form of content. */
.also-items { display: flex; flex-wrap: wrap; column-gap: .5em; row-gap: 2px; }
.also-items li:not(:last-child)::after { content: '·'; content: '·' / ''; margin-left: .5em; color: var(--text-muted); }
.ledger-cta { grid-column: 2; justify-self: start; padding-bottom: var(--s-16); font-size: 13px; gap: 24px; }

/* Ground-Up approach (navy) --------------------------------------------- */
/* overflow: clip, not hidden. Both clip the texture below to the section, but
   hidden also makes the section a scroll container, and a sticky child then
   sticks to that container instead of the page: the sticky section label would
   never pin here. clip does not create one. Same reason on .closing. */
.ground-up { background: var(--navy); color: var(--inverse-text); padding: var(--s-72) 0 var(--s-32); position: relative; overflow: clip; }
/* Topographic contours, traced from a periodic height field so the tile repeats with
   no seam. Generated by tools/build-texture.js. Invisible at a glance, there on a
   close look. Never animated, never interactive. */
.ground-up::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url('./assets/texture-contour.svg');
  background-repeat: repeat;
  background-size: 600px 600px;
  opacity: .05;
  pointer-events: none;
}
.ground-up > .container { position: relative; }
.ground-up .section-index { color: var(--cyan-bright); }
.ground-up h2 { font-size: 56px; line-height: 1.08; }
.ground-up h2 em { color: var(--cyan-bright); }
.ground-up .section-heading { align-items: center; margin-bottom: var(--s-48); }
.ground-intro { width: 360px; }
.ground-intro p:first-child { font-size: 21px; line-height: 1.5; margin-bottom: var(--s-20); }
.ground-intro p + p { color: var(--inverse-muted); font-size: 16px; line-height: 1.6;  text-wrap: pretty; }
.process { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; }
.process li { position: relative; display: flex; flex-direction: column; border-top: 1px solid var(--inverse-border); padding-top: var(--s-24); }
.step-number {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.5;
  border: 1px solid #7A7AB8;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  margin-bottom: var(--s-28);
  background: var(--navy);
  color: var(--cyan-bright);
}
.process h3 { font-size: 19px; min-height: 46px; margin-bottom: var(--s-10); }
.process p { font-size: 16px; color: var(--inverse-muted); line-height: 1.6; max-width: 16.52em; min-height: 64px; margin-bottom: var(--s-20); text-wrap: pretty; }
.step-output { display: block; margin-top: auto; padding-top: var(--s-16); border-top: 1px solid var(--inverse-border); font-size: 10px; color: var(--cyan-bright); }
.ground-footer { display: flex; justify-content: space-between; margin-top: var(--s-48); padding-top: var(--s-24); border-top: 1px solid var(--inverse-border); color: var(--inverse-muted); }
.ground-footer span:last-child { font-size: 14px; font-family: var(--serif); color: var(--inverse-text); }

/* Geography -------------------------------------------------------------- */
/* Copy above; then the map with the country list beside it rather than under it (Part I,
   I4). The H2a frame is taller than the old world frame (1.66:1 against 2.11), so a full-
   width map would have grown the section; at two thirds of the width beside the list it
   is the same height as before and the section is 500px shorter. */
.geography { display: grid; grid-template-columns: 1fr; grid-template-areas: "copy" "map"; row-gap: 0; align-items: start; }   /* the heading's own 36px below it, as every section (2026-09-30) */   /* full-width map since 2026-09-25 (CEO review): the country list beside it is gone */
.geography-copy { grid-area: copy; }
.geography-list { grid-area: list; }
/* The sticky section label is 45px of band; the map starts that plus 16px further down so
   the label never sits on the map's top edge as the section pins (Part I, I3). */
.map-figure { grid-area: map; align-self: center; width: 100%; margin-top: var(--s-24); }   /* 24px under the section's label (92px until 2026-09-30); the section keeps no headline, as the CEO decided on 2026-09-25 */
/* The Venezuela inset (H2b): the one country outside the H2a frame, drawn at its own scale
   in a panel at the lower left, on the same ground as the map. */

/* Framework ledger: the rows behind "View all" and the button that opens them. Without
   script the rows are simply all there and the button is not. */
/* A row's own display would beat the hidden attribute, so hidden is said again here. */
.fw-row[hidden] { display: none; }
.fw-more, .list-more { margin-top: var(--s-20); font-size: 13px; }
html:not(.js) .fw-row[hidden] { display: grid; }
html:not(.js) .published-list li[hidden] { display: grid; }
html:not(.js) :is(.fw-more, .list-more) { display: none; }
/* Paper cannot click: every row prints, and the button that would open them does not. */
@media print {
  .fw-row[hidden] { display: grid; }
  .published-list li[hidden] { display: grid; }
  .fw-more, .list-more { display: none; }
}
/* On the black team band the link is white, as everything else there is. The audit found
   it at 1.16:1 in the page's ink. */
.team .section-more .text-button { color: var(--ink-text); }
/* The grouped list under the world map: one column per region, balanced by the browser. */
.country-regions { columns: 2; column-gap: 32px; margin-top: var(--s-10); }
/* the list is a sidebar now, and its top lines up with the map's top edge */
.geography-list { margin-top: calc(var(--pin-h) + var(--s-16)); }
.country-region { break-inside: avoid; margin-bottom: var(--s-28); }
.region-label { font-size: 10px; color: var(--text-muted); margin-bottom: var(--s-4); }
.country-regions .country-list { grid-template-columns: 1fr; max-width: none; margin-top: 0; }
.geography-copy > p:not(.eyebrow) { margin-top: var(--s-24); color: var(--text-muted); max-width: 560px; text-wrap: pretty; }
/* One line from 390px up; below that, "Where access / is hardest." rather than a lone last word. */
.geography h2 { text-wrap: balance; }
.list-label { margin-top: var(--s-32); color: var(--text-muted); font-size: 10px; }
.country-list { display: grid; grid-template-columns: repeat(2, 1fr); column-gap: 28px; margin-top: var(--s-10); border-top: 1px solid var(--border); max-width: 460px; }
.country-list li { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: var(--s-10) 0; border-bottom: 1px solid var(--border); font-size: 15px; transition: color var(--dur-1) var(--ease-state); }
.country-list li:hover, .country-list li.is-active { color: var(--accent); }
.geography-list .text-button { margin-top: var(--s-24); }

/* The map's ground --------------------------------------------------------
   The map was drawn on nothing: a border, then white. Given a ground it stops
   being a diagram on the page and becomes a chart of somewhere. The ground is the
   same contour tile the Ground-Up section stands on, held here at a size where it
   reads as terrain under the coastlines rather than as pattern, plus the faintest
   inset vignette so the sheet has an edge.
   The tile is painted through a mask rather than laid down as an image, because the
   one file on disk is stroked white for the navy band; masking lets the same file be
   inked in any colour the ground needs, so there is no second near-identical asset
   to keep in step. Where masks are not supported nothing is drawn at all, which is
   the map exactly as it was. */
/* The navy sheet (2026-09-25, CEO review): the map is a dark panel the full width of its column, other land as faint
   glass, SREO's countries in light stone, the country in hand in bright cyan. Tokens below; contrast: stone on navy 11:1,
   cyan-bright on navy 6.9:1, region names 8:1. */
.map-canvas { --map-sheet: #1E1E52; --map-land: rgba(255, 255, 255, .06); --map-coast: rgba(255, 255, 255, .2); --map-grid: rgba(255, 255, 255, .07); --map-fill: #E6E4EF; --map-fill-active: var(--cyan-bright); --map-outline: rgba(30, 30, 82, .9);
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 30%); grid-template-rows: auto 1fr; overflow: clip; border: 0; background: radial-gradient(900px 480px at 62% 30%, rgba(10, 144, 210, .18), transparent 70%), var(--map-sheet); box-shadow: none; }
/* The region chips (2026-09-29): a row across the top of the sheet, one square chip per region in
   the site's mono caps; the one in hand is filled cyan. Scrolls sideways on a phone. */
.map-regions { grid-column: 1 / -1; position: relative; z-index: 2; display: flex; gap: var(--s-8); padding: var(--s-16) var(--s-20); border-bottom: 1px solid rgba(255, 255, 255, .12); overflow-x: auto; scrollbar-width: none; }
.map-regions::-webkit-scrollbar { display: none; }
/* nine regions since 2026-09-30: from tablet width up they wrap onto a second line rather than scroll out of sight; a phone still swipes them */
@media (min-width: 768px) { .map-regions { flex-wrap: wrap; overflow-x: visible; } }
.map-region { flex: 0 0 auto; min-height: 44px; padding: 0 var(--s-16); border: 1px solid rgba(255, 255, 255, .22); border-radius: var(--radius-control); background: transparent; color: var(--inverse-muted); font-family: var(--mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; cursor: pointer; transition: color var(--dur-2) var(--ease-state), border-color var(--dur-2) var(--ease-state), background-color var(--dur-2) var(--ease-state); }
.map-region:hover { color: var(--inverse-text); border-color: rgba(255, 255, 255, .5); }
.map-region[aria-pressed="true"] { background: var(--cyan-bright); border-color: var(--cyan-bright); color: var(--navy-deep); }
/* The map half of the sheet: the svg (2026-09-27). */
.map-art { position: relative; z-index: 1; align-self: center; }
/* The sheet's edges fade into the page over 3px (Allam, 2026-09-29), so the navy block sits in
   the cream rather than being cut out of it. */
.map-canvas { -webkit-mask-image: linear-gradient(to right, transparent, #000 3px, #000 calc(100% - 3px), transparent), linear-gradient(to bottom, transparent, #000 3px, #000 calc(100% - 3px), transparent); -webkit-mask-composite: source-in; mask-image: linear-gradient(to right, transparent, #000 3px, #000 calc(100% - 3px), transparent), linear-gradient(to bottom, transparent, #000 3px, #000 calc(100% - 3px), transparent); mask-composite: intersect; }
@media print, (forced-colors: active) { .map-canvas { -webkit-mask-image: none; mask-image: none; } }
/* Above the ground, below the tooltip (z 30). */
.ops-map { position: relative; z-index: 1; display: block; width: 100%; height: auto; overflow: hidden; }
.map-land { fill: var(--map-land); stroke: var(--map-coast); stroke-width: .75; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* a region in hand (the chips, 2026-09-29): its countries stay bright, the others step back */
.map-figure.has-region .map-country:not(.in-region) .map-focus { fill-opacity: .32; }
/* The countries are the map's subject and carry it on their own: a filled outline at
   rest, cyan when they are asked about. There used to be a cyan pin on every one as
   well, and with both the map read as a chart with a chart drawn over it. The pins are
   still there for the tooltip and the list to pair with, but they show only for the
   country in hand. Suggested by Charlotta Lahnalahti on 2026-09-22; she was right. */
.map-focus { fill: var(--map-fill); stroke: var(--map-outline); stroke-width: 1; stroke-linejoin: round; vector-effect: non-scaling-stroke; cursor: pointer; transition: fill var(--dur-2) var(--ease-state), fill-opacity var(--dur-2) var(--ease-state); }
.map-focus:hover, .map-focus.is-active { fill: var(--map-fill-active); fill-opacity: 1; }
/* The map's words (Part J, J2) ----------------------------------------------------------------
   Region names are HTML laid over the svg, placed by the builder as percentages of the frame
   in open sea beside each cluster, so they are set in CSS pixels: 12px at every width, where
   the same text inside the svg was 8px at 1440 and 3px on a phone. Below 768px the names go;
   the list under the map has them. Venezuela, west of the frame, is listed and not drawn
   (the inset was tried and taken out on 2026-09-23). */
.map-labels { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.map-labels span { position: absolute; transform: translate(0, -.85em); font-family: var(--mono); font-size: 12px; line-height: 1.35; letter-spacing: .18em; text-transform: uppercase; color: rgba(248, 247, 243, .78); white-space: nowrap; }   /* 15:1 on the page; was --text-muted at 5.7:1 (Part M) */
.map-labels span[data-anchor="end"] { transform: translate(-100%, -.85em); text-align: right; }
@media (max-width: 767px) { .map-labels { display: none; } }
.map-dot { cursor: pointer; outline: none; }
.dot-hit { fill: transparent; stroke: transparent; stroke-width: 3; vector-effect: non-scaling-stroke; }
/* Invisible until its country is in hand, then it fades in. Opacity is the only thing
   that changes, and on purpose: opacity does not stop the core taking the pointer, so
   on a phone, where an outline is a few pixels across, the 9px core below is still
   what a finger finds. A first version also scaled it down at rest, which shrank that
   target to 3px at 320px wide; measured, and taken out. */
.dot-core { fill: var(--cyan-bright); stroke: var(--map-sheet, #1E1E52); stroke-width: 1.5; opacity: 0; transition: opacity var(--dur-1) var(--ease-state); }
.dot-ring { fill: none; stroke: var(--cyan-bright); stroke-width: 1; opacity: 0; transform-box: fill-box; transform-origin: center; pointer-events: none; }   /* the ripple grows under a resting pointer; it must never take it */
.map-dot:hover .dot-core, .map-dot.is-active .dot-core { opacity: 1; }
/* Two markers closer than a core's radius (Grenada and St Vincent and the Grenadines, 2026-10-07): the cores stop
   taking the pointer, so each marker's halved hit area (build-countries.js) answers for its own country. */
.map-dot.is-crowded .dot-core { pointer-events: none; }
.map-dot.is-crowded .dot-hit { stroke-width: 0; }   /* its 3px screen-space rim would reach the neighbour too */
/* The world viewBox is 1200 wide, so on a phone it shrinks about 3x. Markers keep a
   readable size there by growing in user units; r is a CSS geometry property in SVG 2. */
@media (max-width: 767px) {
  .ops-map .dot-core { r: 9px; }
  .ops-map .dot-ring { r: 10px; }
}
/* The panel (2026-09-27): the country in hand, in words, beside the map rather than floating
   over it. Region in the accent, the name in the display serif, the clients SREO has worked
   for there, and a way to its profile. Never a count of projects. A darker glass over the
   sheet and one hairline separate it from the map. */
.map-panel { position: relative; z-index: 2; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; padding: var(--s-48) var(--s-40); border-left: 1px solid rgba(255, 255, 255, .14); background: linear-gradient(90deg, rgba(14, 14, 48, .28), rgba(14, 14, 48, .62)); color: var(--inverse-text); }
.map-panel .eyebrow { color: var(--inverse-muted); }
.map-panel .map-panel-region { color: var(--cyan-bright); margin-bottom: var(--s-12); }
.map-panel-name { font-family: var(--serif); font-size: clamp(38px, 3.8vw, 56px); line-height: 1.02; letter-spacing: -.02em; margin-bottom: var(--s-28); }
.map-panel-block .eyebrow { margin-bottom: var(--s-6); }
.map-panel-clients, .map-panel-note { font-size: 16px; line-height: 1.55; color: var(--inverse-text); max-width: 30ch; }
.map-panel-note { margin-top: var(--s-12); }
.map-panel [hidden] { display: none; }
.map-panel .text-button { margin-top: var(--s-28); color: var(--inverse-text); }
.map-panel .text-button:hover { color: var(--cyan-bright); }
.map-panel-hint { margin-top: var(--s-40); font-family: var(--mono); font-size: 12px; line-height: 1.5; letter-spacing: .08em; color: var(--inverse-muted); }
.hint-touch { display: none; }
@media (hover: none) { .hint-pointer { display: none; } .hint-touch { display: inline; } }
.map-figure figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px; padding-top: var(--s-14); color: var(--text-muted); font-size: 10px; }

/* Selected work ---------------------------------------------------------- */
.work-section { background: var(--surface); border-block: 1px solid var(--border); }
.projects-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; }
.project-card {
  padding: var(--s-28);
  background: var(--background);
  border-top: var(--hairline) solid var(--border);   /* a grey hairline since 2026-09-25 (Allam); the 2px navy rule read too heavy */
  transition: transform .3s var(--ease-out), box-shadow .3s var(--ease-out);
}
.project-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-1); }
.project-top { display: flex; gap: 12px; justify-content: space-between; align-items: flex-start; color: var(--text-muted); }
.project-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 12px; padding-top: var(--s-2); }

/* In the public record, as a shelf (2026-09-30, Allam: "make it more sexy, it feels boring"; the
   report shelf, by poll). Built by tools/build-reports.js from content/published-reports.json:
   every report a cover on a shelf that runs to the window's edge, dragged, swiped or stepped
   through with the two buttons, a progress line beneath it. A cover is a report's cover, not a
   card: a spine down its left edge, a grain over it, the client at the top, the publisher's own
   title in the display serif, the publisher beneath, the country and years at the foot, and the
   country's outline from the map in section 01 behind them. Four tones in turn (navy, deep navy,
   cyan, soft navy); five placements of the outline, so the five Iraq covers differ. */
.work-section { overflow-x: clip; }   /* the shelf runs to the window's edge; nothing may scroll the page sideways */
.shelf { margin-top: var(--s-48); }
.cover-shapes { position: absolute; width: 0; height: 0; overflow: hidden; }
.shelf-rail {
  display: flex; gap: 24px;
  margin-inline: calc(50% - 50vw); padding: var(--s-20) calc(50vw - 50%) var(--s-28);
  /* where a cover comes to rest: the page's column. A percentage here would be the shelf's own width,
     so these are the column's gutters, and app.js sets the exact figure from the padding. */
  scroll-padding-inline: var(--s-64);
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none;
  /* the shelf itself: one hairline the covers stand on */
  background: linear-gradient(var(--border), var(--border)) 0 calc(100% - 14px) / 100% 1px no-repeat;
}
.shelf-rail::-webkit-scrollbar { display: none; }
.shelf-item { flex: none; scroll-snap-align: start; }
.cover {
  --cv-bg: var(--navy); --cv-accent: var(--cyan-bright); --cv-quiet: var(--inverse-muted);
  --cv-line: rgb(111 195 239 / .5); --cv-art: rgb(111 195 239 / .06);
  --cv-spine: inset 6px 0 0 rgb(0 0 0 / .22), inset 7px 0 0 rgb(255 255 255 / .06);
  position: relative; isolation: isolate; overflow: clip;
  display: flex; flex-direction: column; width: 256px; height: 352px;
  padding: var(--s-20) var(--s-20) var(--s-20) calc(var(--s-20) + var(--s-6));   /* the spine's 6px on top of the inset */
  background: var(--cv-bg); color: var(--inverse-text); text-decoration: none;
  box-shadow: var(--cv-spine), 0 18px 32px -20px rgb(27 27 74 / .55);
}
.cover[data-tone="1"] { --cv-bg: var(--navy-deep); }
.cover[data-tone="2"] { --cv-bg: var(--cyan-deep); --cv-accent: var(--cyan-tint); --cv-quiet: var(--cyan-tint); --cv-line: rgb(230 243 251 / .45); --cv-art: rgb(230 243 251 / .07); }
.cover[data-tone="3"] { --cv-bg: var(--navy-soft); }
/* the grain every photograph on the site carries, and a sheen that follows the pointer (app.js) */
.cover::before { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: url('./assets/texture-grain.svg') repeat 0 0 / 240px 240px; mix-blend-mode: overlay; opacity: .14; }
.cover::after { content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none; opacity: 0; background: radial-gradient(320px circle at var(--mx, 70%) var(--my, 20%), rgb(255 255 255 / .16), rgb(255 255 255 / 0) 65%); }
.cover-top { position: relative; z-index: 1; display: flex; justify-content: space-between; align-items: center; }
.cover-client, .cover-meta { font-family: var(--mono); font-size: 12px; line-height: 18px; letter-spacing: .08em; text-transform: uppercase; color: var(--cv-accent); }
.cover-open { display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid rgb(255 255 255 / .28); border-radius: 50%; }
.cover-open .icon { width: 15px; height: 15px; }
.cover-art { position: absolute; z-index: -1; right: -14%; bottom: -8%; width: 90%; height: 62%; overflow: visible; fill: var(--cv-art); stroke: var(--cv-line); stroke-width: 1.25; stroke-linejoin: round; }
.cover[data-art="1"] .cover-art { right: -24%; bottom: 2%; width: 104%; height: 66%; }
.cover[data-art="2"] .cover-art { right: 4%; bottom: -12%; width: 76%; height: 54%; }
.cover[data-art="3"] .cover-art { right: -8%; bottom: -2%; width: 92%; height: 60%; }
.cover[data-art="4"] .cover-art { right: -20%; bottom: -16%; width: 112%; height: 72%; }
.cover-title {
  position: relative; z-index: 1; margin-top: var(--s-28);
  font-family: var(--serif); font-size: 21px; line-height: 1.22; letter-spacing: -.005em; text-wrap: pretty;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6; line-clamp: 6; overflow: hidden;
}
.cover-publisher { position: relative; z-index: 1; margin-top: var(--s-10); font-size: 13px; line-height: 1.4; color: var(--cv-quiet); }
.cover-meta { position: relative; z-index: 1; margin-top: auto; display: flex; justify-content: space-between; gap: var(--s-12); }
/* A publication's own cover (2026-09-30): the picture fills the cover under the spine and the sheen; the
   ring stays at the top, the country and years sit on a navy fade at the foot, and the title, client and
   publisher, which the picture already shows, stay in the link for a screen reader only. */
.cover.has-image { background: #FFFFFF; }
.cover-img, .cover-img img { position: absolute; inset: 0; width: 100%; height: 100%; }
.cover-img { z-index: -1; }
.cover-img img { object-fit: cover; object-position: 50% 0; }
.cover.has-image::before { opacity: .06; }
.cover.has-image .cover-top { display: none; }   /* the ring would sit on the publisher's own logo; the whole cover is the link and still lifts */
.cover.has-image :is(.cover-client, .cover-title, .cover-publisher) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; margin: 0; }
.cover.has-image .cover-meta { margin: auto calc(-1 * var(--s-20)) calc(-1 * var(--s-20)) calc(-1 * (var(--s-20) + var(--s-6))); padding: var(--s-40) var(--s-20) var(--s-16) calc(var(--s-20) + var(--s-6)); background: linear-gradient(rgb(27 27 74 / 0), rgb(27 27 74 / .9)); color: var(--inverse-text); }
.cover:is(:hover, :focus-visible) .cover-open { background: var(--cyan-bright); border-color: var(--cyan-bright); color: var(--navy-deep); }
.cover:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }   /* the site's ring (e10) */
/* beneath the shelf: how far along it you are, and a step either way */
.shelf-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--s-32); margin-top: var(--s-8); }
.shelf-progress { position: relative; flex: 1; max-width: 420px; height: 2px; background: var(--border); overflow: hidden; }
.shelf-progress > span { position: absolute; left: 0; top: 0; height: 100%; width: calc(var(--shelf-v, .4) * 100%); background: var(--navy); transform: translateX(calc(var(--shelf-p, 0) * (1 / var(--shelf-v, .4) - 1) * 100%)); }
.shelf-nav { display: flex; gap: var(--s-10); }
.shelf-btn { display: grid; place-items: center; width: 48px; height: 48px; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-control); background: transparent; color: var(--text-primary); cursor: pointer; }   /* square, as every control on the site (partj) */
.shelf-btn .icon { width: 18px; height: 18px; }
.shelf-prev .icon { transform: rotate(180deg); }
.shelf-btn:hover:not(:disabled) { background: var(--navy); border-color: var(--navy); color: var(--inverse-text); }
/* disabled: the site's shared look (40%, not-allowed) */
.shelf-btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
html:not(.js) .shelf-bar { display: none; }
@media (hover: hover) and (pointer: fine) { .shelf-rail { cursor: grab; } }
.shelf-rail.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.shelf-rail.is-dragging .cover { pointer-events: none; }
@media (max-width: 1199px) { .shelf-rail { scroll-padding-inline: var(--s-40); } }
@media (max-width: 767px) {
  .shelf { margin-top: var(--s-32); }
  .shelf-rail { gap: 16px; scroll-padding-inline: var(--s-20); }
  .cover { width: 212px; height: 292px; padding: var(--s-16) var(--s-16) var(--s-16) calc(var(--s-16) + var(--s-6)); }
  .cover-title { margin-top: var(--s-20); font-size: 18px; -webkit-line-clamp: 5; line-clamp: 5; }
  .shelf-nav { display: none; }
  .shelf-progress { max-width: none; }
}
@media print {
  .shelf-rail { flex-wrap: wrap; overflow: visible; margin: 0; padding: 0; background: none; }
  .shelf-bar { display: none; }
  .cover { box-shadow: none; break-inside: avoid; }
}
/* The same reports, named where the engagement itself is listed: a row in a country's
   project list, and under the one service example that has one. In a mono meta line the
   link is marked by a drawn hairline, the way every other rule on the site is drawn, never
   by text-decoration; the service example's copy is a text-button and keeps that style. */
.report-link { color: var(--accent); }
.project-meta .report-link { padding-bottom: var(--hairline); background: linear-gradient(currentColor, currentColor) left bottom / 100% 1px no-repeat; }
@media (max-width: 767px) {
  .published-heading { flex-direction: column; align-items: flex-start; gap: 6px; }
}

/* Data glyph: a faint track of the whole shape, with the meaningful strokes drawing
   themselves in on hover. pathLength="100" normalises every path, so one dash length
   drives a five-bar chart, a country outline and an arc alike. */
.glyph { flex: 0 0 auto; width: 40px; height: 40px; }
.glyph path { fill: none; stroke-width: 1.5; stroke-linejoin: round; }
.glyph-track path { stroke: var(--border); stroke-linecap: round; }
.glyph-solid path { stroke: var(--navy); stroke-linecap: round; }
.glyph-draw path { stroke: var(--navy); stroke-linecap: butt; stroke-dasharray: 100; stroke-dashoffset: 100; }
@media (hover: hover) {
  .glyph-draw path { transition: stroke-dashoffset 500ms var(--ease-state); }
  .project-card:hover .glyph-draw path, .project-card:focus-within .glyph-draw path { stroke-dashoffset: 0; }
}
/* Touch has no hover, so the drawn state is simply the resting state there. */
@media (hover: none) {
  .glyph-draw path { stroke-dashoffset: 0; }
}
.project-top .eyebrow { font-size: 10px; }
.tag { font-family: var(--mono); font-size: 9px; line-height: 1.5; letter-spacing: .06em; text-transform: uppercase; border: 1px solid var(--border); padding: var(--s-4) var(--s-8); color: var(--accent); background: var(--cyan-tint); }
.project-card h3 { font-size: 24px; line-height: 1.3; margin-top: var(--s-24); min-height: 80px; max-width: 13.04em; }
.project-card dl { margin: var(--s-20) 0; }
.project-card dl > div { display: grid; grid-template-columns: 95px 1fr; gap: 8px; padding: var(--s-10) 0; border-bottom: 1px solid var(--border); align-items: baseline; }
.project-card dt { font-family: var(--mono); font-size: 10px; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.project-card dd { font-size: 13px; margin: 0; }
.project-result > .eyebrow { font-size: 10px; color: var(--accent); }
.project-result p { font-size: 16px; line-height: 1.6; margin-top: var(--s-12); color: var(--text-muted); }

/* Sectors ---------------------------------------------------------------- */
/* Twelve tiles since 2026-09-30 (Allam: "interactive, easy on the eye, premium"). Each is a link that
   asks the Contact page about its sector. At rest a hairline card on the paper; in hand (hover or
   keyboard focus) navy fills it, the words turn light, the number gives way to "Read more" (since 2026-10-05),
   the arrow's ring fills cyan and the other cards step back. The states are here; the travel (the
   fill rising, the tilt toward the pointer, the light that follows it, the cascade in) is in
   motion.css and app.js, so reduced motion and static review get the same states standing still. */
.sector-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.sector-grid > li { display: flex; }
.sector-tile {
  position: relative; isolation: isolate; overflow: clip;
  display: flex; flex-direction: column; justify-content: space-between; gap: var(--s-28);
  width: 100%; min-height: 204px; padding: var(--s-20) var(--s-20) var(--s-24);   /* a three-line name needs 192px; the margin keeps the font swap from moving the page */
  border: 1px solid var(--border); background: var(--background);
  color: var(--text-primary); text-decoration: none;
}
/* the fill, and the light that follows the pointer across it (motion.css moves both) */
.sector-tile::before { content: ''; position: absolute; inset: -1px; z-index: -2; background: var(--navy); transform: scaleY(0); transform-origin: 50% 100%; }
.sector-tile::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0;
  background: radial-gradient(260px circle at var(--mx, 70%) var(--my, 30%), rgb(111 195 239 / .20), rgb(111 195 239 / 0) 70%);
}
.sector-top { display: flex; justify-content: space-between; align-items: center; }
/* the number and the invitation share one line, one above the other in a clipped window */
.sector-label { display: block; height: 18px; overflow: clip; font-family: var(--mono); font-size: 12px; line-height: 18px; letter-spacing: .08em; text-transform: uppercase; }
.sector-label > span { display: block; }
.sector-num { color: var(--accent); }
.sector-ask { color: var(--cyan-bright); }
.sector-arrow { display: grid; place-items: center; width: 34px; height: 34px; border: 1px solid var(--border); border-radius: 50%; color: var(--text-muted); }
.sector-arrow .icon { width: 15px; height: 15px; }
.sector-name { font-family: var(--serif); font-size: 23px; line-height: 1.22; letter-spacing: -.005em; text-wrap: pretty; }
.sector-tile:is(:hover, :focus-visible) { color: var(--inverse-text); border-color: var(--navy); }
.sector-tile:is(:hover, :focus-visible)::before { transform: none; }
.sector-tile:is(:hover, :focus-visible) .sector-label > span { transform: translateY(-100%); }
.sector-tile:is(:hover, :focus-visible) .sector-arrow { background: var(--cyan-bright); border-color: var(--cyan-bright); color: var(--navy-deep); }
.sector-tile:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
@media (hover: hover) {
  .sector-grid:hover .sector-tile:not(:hover) { opacity: .55; }
}
/* Every tile coloured since 2026-09-30 (Daniel Seckman: "All of the Sectors should be colored ... the
   Dark Blue and the Lighter Blue for every other sector"): navy and cyan in a checkerboard, from the
   tile's row and column (data-tone, app.js; without script, every other tile). In hand, the other
   colour rises from the foot. Cyan is the deep one, so white type keeps 5.9:1 on it. */
.sector-tile { --tile-bg: var(--navy); --tile-fill: var(--cyan-deep); --tile-num: var(--cyan-bright); --tile-ask: var(--cyan-tint);
  background: var(--tile-bg); border-color: var(--tile-bg); color: var(--inverse-text); }
.sector-grid > li:nth-child(even) > .sector-tile:not([data-tone]), .sector-tile[data-tone="1"] { --tile-bg: var(--cyan-deep); --tile-fill: var(--navy); --tile-num: var(--cyan-tint); --tile-ask: var(--cyan-bright); }
.sector-tile::before { background: var(--tile-fill); }
.sector-num { color: var(--tile-num); }
.sector-ask { color: var(--tile-ask); }
.sector-arrow { border-color: rgb(255 255 255 / .32); color: var(--inverse-text); }
.sector-tile:is(:hover, :focus-visible) { color: var(--inverse-text); border-color: var(--tile-fill); }
/* Since 2026-10-05 a tile is a button that opens its sector's description (Daniel Seckman: "details under
   each of the sectors ... 5 sentences"; tools/build-sectors.js), so it sheds the button's own look. */
button.sector-tile { appearance: none; font: inherit; text-align: left; cursor: pointer; }
/* The panel: its head in the colour of the tile that opened it, the name in the display serif, then the
   five sentences on the paper and the way on to the Contact page. */
.sector-dialog { --tile-bg: var(--navy); --tile-num: var(--cyan-bright); width: min(640px, calc(100% - 40px)); padding: 0; border: 0; }
.sector-dialog[data-tone="1"] { --tile-bg: var(--cyan-deep); --tile-num: var(--cyan-tint); }
.sector-head { padding: var(--s-20) var(--s-24) var(--s-32) var(--s-32); background: var(--tile-bg); color: var(--inverse-text); }
.sector-head-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--s-24); }
.sector-head .eyebrow { color: var(--tile-num); }
.sector-head .close-dialog { color: var(--inverse-text); margin-right: calc(-1 * var(--s-10)); }
.sector-dialog h2 { margin: 0; font-size: 40px; line-height: 1.12; color: var(--inverse-text); text-wrap: balance; }
.sector-body { padding: var(--s-32); }
.sector-text { margin: 0 0 var(--s-32); max-width: 60ch; font-size: 18px; line-height: 1.65; color: var(--text-primary); text-wrap: pretty; }
.cross-cutting { display: flex; gap: 28px; flex-wrap: wrap; align-items: baseline; margin-top: var(--s-28); font-size: 14px; color: var(--text-muted); }
.cross-cutting > .eyebrow { font-size: 11px; margin-right: var(--s-8); }

/* Standards -------------------------------------------------------------- */

/* Team (monochrome band) -------------------------------------------------- */
.team { background: var(--ink); color: var(--ink-text); padding: var(--s-72) 0 var(--s-72); }
/* On the homepage the team is a strip (Part I, I4): the heading in the left third, the
   four leaders in a row on the right at a smaller, square size, and the link to the team
   page under both. The full 4:5 frames and the grid belong to team.html, where the whole
   team stands together. Below 1024px the strip stacks like the section it replaced. */
@media (min-width: 1024px) {
  body:not(.page-team) .team > .container { display: grid; grid-template-columns: 1fr 2fr; column-gap: 56px; align-items: start; }
  body:not(.page-team) .team .section-heading { display: block; margin-bottom: 0; }
  body:not(.page-team) .team .section-heading > p:last-child { margin-top: var(--s-20); }
  body:not(.page-team) .team .team-group { margin-top: 0; }
  body:not(.page-team) .team .section-more { grid-column: 1 / -1; margin-top: var(--s-36); }
}
body:not(.page-team) .team-lead { gap: 20px; }
body:not(.page-team) .team-lead .portrait { aspect-ratio: 1 / 1; width: min(100%, 152px); }   /* smaller since 2026-09-30, so eight fit in two rows (Daniel Seckman) */
body:not(.page-team) .team-lead { row-gap: var(--s-36); }
body:not(.page-team) .team-lead .portrait-img { --focus: 50% 10%; }   /* the square strip crops the 4:5 file; the head keeps its room at the top */
body:not(.page-team) .team-lead .person :is(h3, h4) { font-size: 20px; margin-top: var(--s-14); }
body:not(.page-team) .team-lead .portrait-initials { font-size: 56px; }
.team .section-index { color: var(--ink-label); }
.team .section-heading > p { color: var(--ink-muted); }
.team-group + .team-group { margin-top: var(--s-72); }
.team-label { color: var(--ink-label); font-weight: 400; font-size: 11px; padding-bottom: var(--s-14); margin-bottom: var(--s-28); border-bottom: 1px solid var(--ink-line); }
/* Four across, the same count as the grid below, since the leadership row gained its
   fourth member. What still sets the row apart is not width but weight: larger initials,
   a 30px name against the grid's 22, and a wider gutter. */
.team-lead { display: grid; grid-template-columns: repeat(4, 1fr); gap: 32px; }
.team-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 40px 24px; }
/* One locked 4:5 box for the empty frame and the photograph alike: no layout shift when photos arrive */
.portrait { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: radial-gradient(90% 70% at 50% 38%, var(--ink-glow) 0%, var(--ink-raised) 72%); }
.portrait::before {
  content: ""; position: absolute; inset: 12px; pointer-events: none;
  background:
    linear-gradient(var(--ink-mark), var(--ink-mark)) left top / 10px 1px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) left top / 1px 10px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) right top / 10px 1px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) right top / 1px 10px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) left bottom / 10px 1px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) left bottom / 1px 10px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) right bottom / 10px 1px no-repeat,
    linear-gradient(var(--ink-mark), var(--ink-mark)) right bottom / 1px 10px no-repeat;
}
.portrait-initials { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--serif); font-size: 112px; line-height: 1; letter-spacing: -.02em; color: var(--ink-initial); user-select: none; transition: color var(--dur-2) var(--ease-state); }
.team-grid .portrait-initials { font-size: 76px; }
.portrait-img { position: absolute; inset: 0; z-index: 1; display: block; width: 100%; height: 100%; object-fit: cover; object-position: var(--focus, 50% 22%); filter: grayscale(1) contrast(1.04); }
/* A portrait is a photograph and gets the same grain as every other photograph on the
   site. It cannot carry its own, an image having no pseudo elements, so the corner
   marks that make the empty frame are handed the job the moment a face covers them:
   same tile, same size, same strength as the field pictures. Live since 2026-09-24,
   when nine of the twelve portraits arrived; the three still empty need no CSS when
   theirs do. See the note beside each .person still without one for the markup. */
.portrait:has(.portrait-img) { isolation: isolate; }
.portrait:has(.portrait-img)::before {
  inset: 0;
  z-index: 2;
  background: url('./assets/texture-grain.svg') repeat 0 0 / 240px 240px;
  mix-blend-mode: overlay;
  opacity: .1;
}
@media print, (forced-colors: active) {
  .portrait:has(.portrait-img)::before { content: none; }
}
.person :is(h3, h4) { margin: var(--s-18) 0 0; font-family: var(--serif); font-weight: 400; font-size: 30px; line-height: 1.12; letter-spacing: -.02em; color: var(--ink-text); text-wrap: balance; }
.team-grid .person :is(h3, h4) { font-size: 22px; margin-top: var(--s-14); }
.person .eyebrow { margin-top: var(--s-8); font-size: 10px; line-height: 1.6; color: var(--ink-label); max-width: 30ch; }
.nowrap { white-space: nowrap; }
@media (hover: hover) {
  /* Garnish only: a veil lifts and the initials brighten. Nothing moves. */
  .portrait::after { content: ""; position: absolute; inset: 0; z-index: 2; background: var(--ink); opacity: .16; pointer-events: none; transition: opacity var(--dur-2) var(--ease-state); }
  .person:hover .portrait::after { opacity: 0; }
  .person:hover .portrait-initials { color: var(--ink-label); }
}

/* Contact ---------------------------------------------------------------- */
.contact-section { background: var(--surface-contact); border-top: 1px solid var(--border); padding: var(--s-64) 0; }
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 96px; }
.contact-grid h2 { font-size: 60px; line-height: 1.08; margin-bottom: var(--s-24); }
.contact-grid > div > p:not(.eyebrow) { color: var(--text-muted); max-width: 360px; }
/* The one promise the page makes about answering, kept where the enquiry is written
   rather than only in the line the form says after it has been sent. */
.contact-promise { margin-top: var(--s-10); font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--accent); }
.contact-email {
  display: inline-flex; align-items: center; gap: 50px; min-height: 44px;
  margin-top: var(--s-40);
  font-size: 21px;
  /* 9px, not 8: the 1px border this replaces was part of the box height */
  padding-bottom: var(--s-8);
  /* a resting rule, and over it an accent rule that draws from the left on hover */
  background-image: linear-gradient(var(--accent), var(--accent)), linear-gradient(var(--text-primary), var(--text-primary));
  background-repeat: no-repeat;
  background-position: left bottom, left bottom;
  background-size: 0% 1px, 100% 1px;
  transition: color .2s var(--ease-state), background-size 300ms var(--ease-state);
}
.contact-email:hover { color: var(--accent); }
/* The ways to reach SREO beside the form (2026-10-06): a short ledger, a hairline over each row, the label in the
   mono face and the way itself beside it; on a phone the label sits above. The email is the largest thing in it. */
.contact-ways { display: grid; max-width: 520px; margin: var(--s-40) 0 0; border-bottom: 1px solid var(--border); }
.contact-way { display: grid; grid-template-columns: 11em minmax(0, 1fr); align-items: center; gap: var(--s-4) var(--s-20); padding: var(--s-12) 0; border-top: 1px solid var(--border); }
.contact-way dt { font-family: var(--mono); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); }
.contact-way dd { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-8) var(--s-16); min-width: 0; margin: 0; }
.contact-ways .contact-email { gap: 0; margin: 0; padding-bottom: var(--s-4); font-family: var(--serif); font-size: 26px; line-height: 1.2; overflow-wrap: anywhere; }
.copy-email { min-height: 44px; padding: 0 var(--s-14); border: 1px solid var(--border); border-radius: var(--radius-control); background: transparent; color: var(--text-primary); font: 500 13px/1 var(--sans); cursor: pointer; }
.copy-email:hover, .copy-email.is-copied { border-color: var(--accent); color: var(--accent); }
html:not(.js) .copy-email { display: none; }
.contact-way-link { display: inline-flex; align-items: center; gap: var(--s-10); min-height: 44px; color: var(--text-primary); font-weight: 500; text-decoration: none; }
.contact-way-link:hover { color: var(--accent); }
.contact-way-meta { font-family: var(--mono); font-size: 12px; font-weight: 400; color: var(--text-muted); }
@media (max-width: 767px) {
  .contact-way { grid-template-columns: 1fr; padding: var(--s-14) 0; }
  .contact-ways .contact-email { font-size: 22px; }
}
/* Start, not stretch: when one field's error line wraps, app.js grows that field's
   margin, and a stretched sibling would carry its rule and its own error line down with
   the track while its input stayed put. */
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
/* Underlined fields, each with its label sitting in the field until there is
   something in it. The label is a real <label for>, never a placeholder standing in
   for one: a placeholder leaves the moment a reader starts typing, taking with it the
   only statement of what the field is. The hint stays a placeholder and appears once
   the label has moved out of its way. */
/* The gap under a field is a token because app.js adds to it when a field's error line
   needs a second row: the two-column row is 155px a side at 768px, where even a short
   sentence at 11px wraps. */
.form-field { --field-gap: 32px; position: relative; margin-bottom: var(--field-gap); padding-top: var(--s-24); }
.form-field label {
  position: absolute;
  left: 0;
  top: 0;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .04em;
  /* the same line box as the field's own text, so the two states differ in size and
     position and in nothing else */
  line-height: 1.5;
  color: var(--text-muted);
  /* It stays clickable in both places: a label that ignores the pointer would let a
     click on the lifted one fall through to nothing, and a label a reader cannot press
     to reach its field is only half a label. */
  cursor: default;
}
/* The resting position is the exception rather than the rule, so an engine that does
   not understand :has() simply leaves every label above its field, where they all sat
   before. */
.form-field:has(:is(input, textarea):placeholder-shown:not(:focus)) label {
  /* exactly where the typed text will sit: the input's own top plus its padding */
  top: 28px;   /* the field's 24px top padding plus the input's 4px */
  font-size: 16px;
  font-weight: 400;
  letter-spacing: 0;
  /* A resting label is the field's only name, so it keeps the secondary text token
     rather than a lighter placeholder grey, which sits at 2.8:1 on this band. */
  color: var(--text-muted);
  cursor: text;   /* it is sitting where the text will go */
}
.form-field input, .form-field textarea {
  /* Block, not the inline-block a control is born with: an inline textarea sits on the
     line's baseline and leaves a descender's worth of gap, 7.6px here, between its own
     bottom and the rule, which then reads as belonging to nothing. */
  display: block;
  border: 0;
  border-radius: 0;
  padding: var(--s-4) 0 var(--s-10);
  width: 100%;
  min-height: 44px;
  background: transparent;
  color: var(--text-primary);
  font-size: 16px;
  line-height: 1.5;
}
.form-field textarea { resize: vertical; }
/* The field's own rule sets a radius of 0, later in the sheet than the focus ring and at
   the same weight, so the ring's 2px corners are asked for again here. */
.form-field :is(input, textarea):focus-visible { border-radius: 2px; }
/* The hint waits for the label to leave. It is hidden by opacity rather than by a
   transparent colour, because a forced-colour mode replaces every colour with its own
   and would print the hint straight through the resting label; opacity it leaves alone. */
.form-field :is(input, textarea)::placeholder { color: var(--text-muted); opacity: 0; }
.form-field :is(input, textarea):focus::placeholder { opacity: 1; }
/* A browser filling the field in for the reader paints its own pale box behind the
   value. On a field with no box of its own that reads as a mistake, so the paint is
   covered with the band's colour from inside, which is the one thing that override
   does not reach. */
.form-field :is(input, textarea):autofill {
  -webkit-box-shadow: inset 0 0 0 1000px var(--surface-contact);
  -webkit-text-fill-color: var(--text-primary);
}
/* The rule under a field is a 2px line held at half height, so focus thickens it with
   a transform instead of a border width, which would shift the text sitting on it. */
.form-field::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--input-border);
  transform: scaleY(.5);
  transform-origin: bottom;
}
.form-field:hover::after { background: var(--navy-soft); }
.form-field:focus-within::after { background: var(--accent-bright); transform: scaleY(1); }
/* The mistake is a class on the field itself, set by the same script that finds it,
   so it does not depend on :has() the way the resting label does: an engine without
   :has() degrades to labels sitting above their fields, which is fine, but a mistake
   nobody can see is not. */
.form-field.has-error::after { background: var(--input-error); transform: scaleY(1); }
.form-field.has-error label { color: var(--input-error); }
/* What is wrong, in words, under the rule. It is what the field describes itself with
   (aria-describedby), so it is read out with the field and not only shown. It sits in
   the field's bottom margin rather than in the flow, so naming a mistake moves nothing:
   4px under the rule, which leaves 11px between it and whatever comes next. */
.field-error {
  position: absolute;
  left: 0;
  top: 100%;
  margin-top: var(--s-4);
  font-size: 12px;   /* the type floor (Part I, I2); a sentence in the text face, so it keeps sentence tracking */
  line-height: 1.5;
  letter-spacing: .01em;
  color: var(--input-error);
}
/* Forced colours drop every painted background, the rule included, and the field has
   no border of its own to fall back on. So there the rule stands down for a real
   border, in the colour the reader chose for text. */
@media (forced-colors: active) {
  .form-field::after { content: none; }
  .form-field :is(input, textarea) { border-bottom: 1px solid CanvasText; }
}
.form-bottom { display: flex; justify-content: flex-end; align-items: center; gap: 24px; }
.form-bottom .button { flex-shrink: 0; }

/* The spam trap must stay in the layout, so it is hidden the same way as .sr-only
   rather than with display:none, which some bots skip. */
.form-trap { position: absolute; width: 1px; height: 1px; padding: 0; margin: calc(-1 * var(--hairline)); overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* The status line is a live region, so it stays in the document and collapses to
   nothing while empty instead of being removed from the accessibility tree. */
.form-status { margin-top: 0; opacity: 0; }
.form-status:not(:empty) { margin-top: var(--s-20); opacity: 1; }
.form-status.is-error { color: var(--input-error); }
.form-status:focus { outline: 0; }   /* focus is moved here programmatically, not by the reader */

/* Sent: the button contracts to a square of its own height and the check draws in.
   JS supplies the two pixel widths, because width cannot transition from auto. */
/* Padding and gap are deliberately left alone while the button contracts: snapping
   them would jerk the label sideways in the frame before it has finished fading. */
.form-submit { position: relative; overflow: hidden; white-space: nowrap; }
.submit-check {
  position: absolute; inset: 0; margin: auto;
  width: 22px; height: 22px;
  fill: none; stroke: var(--inverse-text); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 100; stroke-dashoffset: 100;
  opacity: 0;
}
/* Three dots while the enquiry is in flight. They stand in for the label rather than
   beside it, so the button says one thing at a time. */
.submit-dots {
  position: absolute; inset: 0; margin: auto;
  display: flex; align-items: center; justify-content: center; gap: 5px;
  height: 4px; width: 26px;
  opacity: 0;
}
.submit-dots i { width: 4px; height: 4px; border-radius: 50%; background: var(--inverse-text); }
.form-submit.is-sending :is(.submit-label, .submit-mark) { opacity: 0; }
.form-submit.is-sending .submit-dots { opacity: 1; }
.form-submit.is-sent .submit-label, .form-submit.is-sent .submit-mark { opacity: 0; }
.form-submit.is-sent .submit-check { opacity: 1; stroke-dashoffset: 0; }
/* The sent button is disabled because the enquiry is already in, not because it is
   unavailable, so it keeps full strength rather than the global disabled fade. */
.form-submit:disabled { cursor: default; opacity: 1; }
/* :hover still matches a disabled button, so without this the inert check tile darkens
   under the pointer and reads as though it can still be pressed. */
.form-submit:disabled:hover { background: var(--primary); }
/* Forced colours flatten a painted background to Canvas, which is also the button's
   face, and leave SVG stroke alone, so the dots and the check would vanish. A system
   colour is the one background that survives the adjustment (currentColor does not); the
   check takes the button's own forced text colour, GrayText once it is disabled. This
   block sits after the rules above because it has to outrank them. */
@media (forced-colors: active) {
  .submit-dots i { background: ButtonText; }
  .submit-check { stroke: currentColor; }
}
html.js:not(.is-static) .form-status { transition: opacity var(--dur-2) var(--ease-state); }
html.js:not(.is-static) .form-submit { transition: width var(--dur-3) var(--ease-state), background-color var(--dur-1) var(--ease-state); }
html.js:not(.is-static) .submit-label { transition: opacity var(--dur-1) var(--ease-state); }
/* The mark must keep transform in its list. This selector outranks the site-wide
   .button .icon rule, so a shorthand naming only opacity would drop the
   hover nudge and leave this one arrow snapping while every other button's glides. */
html.js:not(.is-static) .submit-mark { transition: opacity var(--dur-1) var(--ease-state), transform var(--dur-1) var(--ease-state); }
html.js:not(.is-static) .form-submit.is-sent .submit-check {
  transition: opacity var(--dur-1) var(--ease-state) 120ms, stroke-dashoffset 280ms var(--ease-state) 120ms;
}
/* Reduced motion is handled globally in motion.css, which now takes the properties out
   of transition-property rather than only shortening the duration. */

/* Contextual cursor label ---------------------------------------------------
   A pill that follows the pointer over the surfaces whose meaning is not written on
   them: a photograph, and a card whose engagement opens. The system cursor is left
   exactly as it is; this travels beside it, 16px down and to the right, and appears
   nowhere else. It is decorative and duplicated by the words already on the page, so
   it is aria-hidden and built in app.js, which means it does not exist at all for a
   reader without JavaScript, on a touch device, or under reduced motion. */
.cursor-label {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 45;                 /* over the page and the chapter bar, under the header */
  padding: var(--s-8) var(--s-12);
  border-radius: 999px;
  background: color-mix(in srgb, var(--navy) 90%, transparent);
  color: var(--inverse-text);
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;        /* it must never be the thing under the pointer */
  user-select: none;
  opacity: 0;
  visibility: hidden;
}
.cursor-label.is-on { opacity: 1; visibility: visible; }
/* Nothing to follow: a finger is not a pointer, and forced colours would flatten the
   pill to the page's own background. */
@media (hover: none), (pointer: coarse), (forced-colors: active) {
  .cursor-label { display: none; }
}

/* Brand marks --------------------------------------------------------------
   Real brand glyphs from simple-icons (see tools/build-icons.js), never the brand's
   colour. A 20px glyph in a 40px ring inside a 44px target: the ring is drawn by ::before
   at inset 2px, so the visible circle is 40 while the hit area meets the 44 minimum.
   The ring and glyph take the surface's muted and border tokens, set per surface below,
   so one component reads correctly on the light contact band and the navy footer.
   X and YouTube are in the sprite and take this same markup with a different <use>. */
.brand-marks { display: flex; flex-wrap: wrap; gap: 8px; margin-left: calc(-1 * var(--s-2)); }
.brand-link {
  --mark-fg: var(--text-muted);
  --mark-line: var(--border);
  --mark-accent: var(--accent);
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  color: var(--mark-fg);
  transition: color 250ms var(--ease-state);
}
.brand-link::before {
  content: '';
  position: absolute; inset: 2px;
  border: 1px solid var(--mark-line);
  border-radius: 50%;
  transition: border-color 250ms var(--ease-state), background-color 250ms var(--ease-state);
}
.brand-glyph { position: relative; width: 20px; height: 20px; fill: currentColor; stroke: none; }
.brand-link:focus-visible { color: var(--mark-accent); }
.brand-link:focus-visible::before { border-color: var(--mark-accent); background-color: color-mix(in srgb, var(--mark-accent) 8%, transparent); }
@media (hover: hover) {
  .brand-link:hover { color: var(--mark-accent); }
  .brand-link:hover::before { border-color: var(--mark-accent); background-color: color-mix(in srgb, var(--mark-accent) 8%, transparent); }
}
footer .brand-link { --mark-fg: var(--inverse-muted); --mark-line: var(--inverse-border); --mark-accent: var(--cyan-bright); }
footer .brand-marks { margin: 0; }
.contact-section .brand-marks { margin-top: var(--s-24); }

/* Closing call to action ------------------------------------------------- */
.closing {
  position: relative;
  overflow: clip;
  background:
    radial-gradient(720px 360px at 88% 45%, rgba(10, 144, 210, .22), transparent 70%),
    var(--navy);
  color: var(--inverse-text);
  padding: var(--s-104) 0;
  /* the 3px cyan top rule came off on 2026-09-25 at Allam's request, after the hero's */
}
.closing::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1px);
  background-size: 26px 26px;
  opacity: .45;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, .9));
  mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, .9));
}
.closing > .container { position: relative; }
.closing-grid { display: grid; grid-template-columns: 7fr 5fr; gap: 64px; align-items: center; }
.closing .section-index { color: var(--cyan-bright); }
.closing h2 { font-size: 60px; line-height: 1.06; max-width: 8.71em; }
.closing h2 em { color: var(--cyan-bright); }
.closing p:not(.eyebrow) { margin-top: var(--s-24); color: var(--inverse-muted); font-size: 17px; max-width: 28.15em; }
.closing-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 22px; justify-self: end; }
.closing-button { min-width: 260px; min-height: 60px; font-size: 16px; padding: var(--s-16) var(--s-24); }
.closing-email {
  font-family: var(--serif);
  font-size: 26px;
  letter-spacing: -.01em;
  color: var(--inverse-text);
  padding-bottom: var(--s-8);   /* the replaced 1px border was part of the box height */
  background-image: linear-gradient(var(--inverse-text), var(--inverse-text)), linear-gradient(var(--cyan-bright), var(--cyan-bright));
  background-repeat: no-repeat;
  background-position: left bottom, left bottom;
  background-size: 0% 1px, 100% 1px;
  transition: color .2s var(--ease-state), background-size 300ms var(--ease-state);
}
.closing-email:hover { color: var(--cyan-bright); }
.closing-signal { display: inline-flex; align-items: center; gap: 12px; margin: 0; font-family: var(--mono); font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--inverse-muted); }
.signal-dot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: var(--cyan-bright); flex-shrink: 0; }
/* The ring around the dot is drawn and then left alone. It used to pulse, once every
   2.4 seconds, forever. Two things were wrong with that. The motion system's first rule
   is that there is no decorative animation, and its second is that nothing runs longer
   than a second, with one named exception; this was a third, undeclared. And the line it
   sits beside reads "18 countries of operation since 2013", which is a fact about the
   past, not a live feed: a pulse there promises a liveness the page does not have, and
   spends the reader's attention forever to do it. The ring says the same thing standing
   still. */
.signal-dot::after { content: ''; position: absolute; inset: -6px; border-radius: 50%; border: 1px solid var(--cyan-bright); opacity: .45; }

.source-note { grid-column: 1 / -1; margin-top: var(--s-28); font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--text-muted); }

/* Framework agreements ---------------------------------------------------- */
/* A hairline ledger, not cards: the client carries the row, the scope explains it, and the
   years sit in the mono column so they can be scanned down the page. Nothing is interactive. */
.frameworks { border-top: 1px solid var(--border); }
.fw-ledger { border-top: 1px solid var(--border); }
.fw-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 6fr) minmax(0, 1.6fr) 128px; gap: 24px; align-items: baseline; padding: var(--s-16) 0; border-bottom: 1px solid var(--border); }
.fw-client { font-family: var(--serif); font-size: 24px; line-height: 1.2; letter-spacing: -.02em; }
.fw-scope { font-size: 15px; line-height: 1.55; color: var(--text-muted); }
.fw-place { font-size: 10px; color: var(--text-muted); }
.fw-years { display: flex; justify-content: flex-end; gap: 10px; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.fw-current { color: var(--accent); }

/* Footer (2026-09-27) ----------------------------------------------------- */
/* The page's signature, not a sitemap. Deep navy with the contour ground of the field
   sections in white at 5% and a cyan glow low on the left. Left: the mark, and the address to
   write to set in the display serif. Right: the four pages as a ledger of large serif rows,
   numbered and ruled, answering the pointer with the site's own underline and a small step
   forward. Under a hairline: the legal line, the capacity statement, the legal notice,
   LinkedIn and a way back to the top. No offices column since 2026-09-27. */
.site-footer { position: relative; overflow: clip; background: radial-gradient(900px 520px at 6% 100%, rgba(10, 144, 210, .16), transparent 70%), var(--navy-deep); color: var(--inverse-text); padding: var(--s-88) 0 var(--s-28); }
@supports ((mask-image: url('./assets/texture-contour.svg')) or (-webkit-mask-image: url('./assets/texture-contour.svg'))) {
  .site-footer::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: #FFFFFF; opacity: .05;
    -webkit-mask-image: url('./assets/texture-contour.svg'); mask-image: url('./assets/texture-contour.svg');
    -webkit-mask-repeat: repeat; mask-repeat: repeat;
    -webkit-mask-size: 520px 520px; mask-size: 520px 520px;
  }
  @media print, (forced-colors: active) { .site-footer::before { content: none; } }
}
.site-footer > .container { position: relative; }
.footer-top { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--s-64); padding-bottom: var(--s-72); }
.footer-identity { display: flex; flex-direction: column; justify-content: space-between; gap: var(--s-48); }
.footer-brand { align-self: flex-start; }
/* Since 2026-09-30 (Daniel Seckman's review): the logo large, filling the left of the footer; Contact us and
   the address on the right, where the page list was; the closing band above it is gone. */
.footer-top { align-items: end; }
.footer-brand { display: block; width: min(100%, 420px); }   /* a definite box, so the lazy logo's own proportions hold its space before it loads (it grew the footer 73px on arrival) */
.site-footer .footer-brand img { width: 100%; height: auto; }   /* outranks the header's .brand img heights, which the footer's logo shares a class with (on a phone they squashed it to 42px) */
.footer-contact { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-20); justify-self: end; }
.footer-cta { min-width: 214px; }
.footer-write .eyebrow { color: var(--inverse-muted); margin-bottom: var(--s-8); }
.footer-email { display: inline-flex; align-items: center; gap: var(--s-10); min-height: 44px; font-family: var(--serif); font-size: clamp(28px, 2.6vw, 36px); line-height: 1.2; letter-spacing: -.01em; color: var(--inverse-text); transition: color var(--dur-2) var(--ease-state); }
.footer-email .icon { width: 20px; height: 20px; flex: 0 0 auto; }
.footer-nav { display: flex; flex-direction: column; border-top: 1px solid var(--inverse-border); }
.footer-nav a { display: grid; grid-template-columns: 56px minmax(0, 1fr) 24px; align-items: center; padding: var(--s-18) 0; box-shadow: inset 0 -1px 0 var(--inverse-border); color: var(--inverse-text); transition: color var(--dur-2) var(--ease-state); }   /* the row rule is a shadow, not a border: no link on the site underlines with a border */
.footer-no { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; color: var(--inverse-muted); transition: color var(--dur-2) var(--ease-state); }
.footer-name { justify-self: start; font-family: var(--serif); font-size: clamp(32px, 3.4vw, 46px); line-height: 1.12; letter-spacing: -.015em; transition: transform var(--dur-2) var(--ease-state); }
.footer-nav .icon { width: 22px; height: 22px; color: var(--inverse-muted); justify-self: end; }
@media (hover: hover) {
  .footer-email:hover, .footer-nav a:hover, .footer-nav a:hover .footer-no, .footer-nav a:hover .icon { color: var(--cyan-bright); }
  .footer-nav a:hover .footer-name { transform: translateX(6px); }
  .footer-meta a:not(.brand-link):hover { color: var(--inverse-text); }
}
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--s-12) var(--s-32); padding-top: var(--s-20); border-top: 1px solid var(--inverse-border); font-size: 12px; color: var(--inverse-muted); }   /* 12px: at 13 the 16px icons sat 0.9px off their text's centre */
.footer-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-28); }
.footer-meta a:not(.brand-link) { color: var(--inverse-muted); transition: color var(--dur-2) var(--ease-state); }
.footer-up { display: inline-flex; align-items: center; gap: var(--s-8); min-height: 44px; }
.footer-up .icon { transform: rotate(180deg); transition: transform var(--dur-2) var(--ease-state); }
@media (hover: hover) { .footer-up:hover .icon { transform: rotate(180deg) translateY(2px); } }

/* Dialogs ---------------------------------------------------------------- */
dialog { width: min(720px, calc(100% - 40px)); max-height: 85vh; padding: var(--s-32); background: var(--background); color: var(--text-primary); border: 1px solid var(--border); box-shadow: var(--shadow-2); }
dialog::backdrop { background: rgba(27, 27, 74, .72); }
/* The dialog scrolls once the list is long, so its top row stays put: the close button has to
   remain reachable from the bottom of the list. The negative offsets let the sticky row cover
   the dialog's own padding as it passes underneath. */
.dialog-top { position: sticky; top: -32px; z-index: 1; display: flex; justify-content: space-between; align-items: center; margin: calc(-1 * var(--s-32)) calc(-1 * var(--s-32)) var(--s-28); padding: var(--s-32) var(--s-32) var(--s-12); background: var(--background); }
/* A short fade under the sticky row, so a line passing beneath it reads as fading out rather
   than sliced. Nothing is drawn at rest: the gradient sits below the row it belongs to. */
.dialog-top::after { content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 16px; background: linear-gradient(var(--background), transparent); pointer-events: none; }
.dialog-top .eyebrow { color: var(--accent); }
.close-dialog { font-size: 28px; background: none; border: 0; min-width: 44px; min-height: 44px; color: var(--primary); }
dialog h2 { font-size: 40px; margin-bottom: var(--s-24); }
dialog > p { color: var(--text-muted); margin-bottom: var(--s-24); }
/* Two columns where there is room, one where there is not: the count follows the width of the
   dialog rather than the window, which on a tablet is far narrower than the viewport. Groups are
   of very different lengths and none may split, so a third column would leave long gaps. */
.client-groups { columns: 300px; column-gap: 32px; }
.client-groups > div { break-inside: avoid; margin-bottom: var(--s-28); }
.client-groups .eyebrow { color: var(--text-muted); font-size: 10px; margin-bottom: var(--s-8); }
.client-list li { border-bottom: 1px solid var(--border); padding: var(--s-8) 0; font-size: 14px; }
/* One dialog per country on Where we work, listing every engagement the Capacity
   Statement records there under the statement's own headings. Generated by
   build-countries.js from the parsed project list; asked for by Hamza Qattan on
   2026-09-22. Same shell as the clients dialog, a row per project. */
.project-groups > div { margin-bottom: var(--s-28); }
.project-groups .eyebrow { color: var(--text-muted); font-size: 10px; margin-bottom: var(--s-4); }
.project-list li { display: grid; gap: 3px; padding: var(--s-10) 0; border-bottom: 1px solid var(--border); }
/* Every client in the country (2026-10-07), in as many columns as the dialog's width allows. */
.dialog-clients { columns: 200px; column-gap: var(--s-28); }
.dialog-clients li { break-inside: avoid; padding: var(--s-6) 0; border-bottom: 1px solid var(--border); font-size: 14px; line-height: 1.4; }
.project-title { font-size: 14px; line-height: 1.45; text-wrap: pretty; }
.project-meta { font-family: var(--mono); font-size: 11px; letter-spacing: .02em; color: var(--text-muted); }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (min-width: 1441px) {
  .container { width: 1280px; }
  .hero-content { padding-block: var(--s-72); }
  .field-photo { height: 500px; }
}

@media (max-width: 1199px) {
  .container { width: calc(100% - 80px); }
  /* Sectors three across: four would squeeze the long names to five lines at 1024. Tall enough for
     a four-line name at 768, so the font swap moves nothing here either. */
  .sector-grid { grid-template-columns: repeat(3, 1fr); }
  .sector-tile { min-height: 214px; }
  .sector-name { font-size: 21px; }
  .desktop-nav { gap: 18px; }
  .desktop-nav a { font-size: 13px; }
  .nav-shell { gap: 24px; }
  .nav-cta { font-size: 13px; }
  .nav-download { min-width: 44px; padding: 0 var(--s-10); gap: 0; justify-content: center; }
  .nav-download-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .brand img { height: 54px; }
  h1 { font-size: 64px; }
  .hero-grid { gap: 36px; }
  .field-photo figcaption { left: 40px; right: 40px; }
  .photo-band figcaption { left: 40px; right: 40px; }
  .ground-up h2 { font-size: 48px; }
  .process { gap: 24px; }
  .project-card { padding: var(--s-24); }
  .project-card dl > div { grid-template-columns: 80px 1fr; }
  .project-card h3 { font-size: 21px; }
  .fw-row { grid-template-columns: minmax(0, 2fr) minmax(0, 5fr) minmax(0, 1.6fr) 112px; gap: 16px; }
  .contact-grid { gap: 48px; }
  .contact-grid h2 { font-size: 52px; }
  .closing h2 { font-size: 50px; }
  .team-lead { gap: 24px; }
  .portrait-initials { font-size: 92px; }
  .team-grid .portrait-initials { font-size: 60px; }
  .closing-grid { gap: 40px; }
  .footer-top { gap: var(--s-48); }
  /* Below 1200 the panel is too narrow (row 06 needs about 1070px) for the names to sit in one line, so they stack, marked
     like the panel's other lists, with the label above: no dot left hanging at a line end. */
  .ledger-also, .ledger-sectors { display: block; }
  .ledger-also > .eyebrow, .ledger-sectors > .eyebrow { display: block; margin-bottom: var(--s-6); }
  .also-items { display: block; }
  .also-items li { padding: var(--s-4) 0; }
  .also-items li:not(:last-child)::after { content: none; }
  .also-items li::before { content: ''; display: inline-block; width: 10px; height: 1px; background: var(--cyan); vertical-align: middle; margin-right: var(--s-12); }
}

@media (max-width: 1023px) {
  :root { --header-h: 84px; --header-h-compact: 72px; }
  .desktop-nav, .nav-cta, .nav-download, .nav-linkedin { display: none; }
  /* Two lines that fold into a cross. The word "Menu" stays as the button's accessible
     name and stops being drawn: at this width the mark is the convention, and the word
     was taking the room the nav needs. 44px of target either way. */
  .menu-toggle { display: flex; align-items: center; justify-content: center; background: none; border: 0; color: var(--primary); min-width: 44px; min-height: 44px; padding: var(--s-8); }
  .menu-lines { position: relative; display: block; width: 24px; height: 10px; }
  .menu-lines i { position: absolute; left: 0; width: 100%; height: 1px; background: currentColor; }
  .menu-lines i:first-child { top: 0; }
  .menu-lines i:last-child { bottom: 0; }
  /* Both lines meet in the middle and cross. The pair is 10px apart, so each travels
     half of that to the centre line before it turns. */
  .menu-toggle[aria-expanded="true"] .menu-lines i:first-child { transform: translateY(4.5px) rotate(45deg); }
  .menu-toggle[aria-expanded="true"] .menu-lines i:last-child { transform: translateY(-4.5px) rotate(-45deg); }
  /* No script, no panel. The button that opens it cannot work, so it is not offered; a
     control that does nothing is worse than no control. The same destinations are in the
     markup twice over, so the navigation that is usually kept for wide screens is laid
     out here instead, wrapped, with the contact button back beside the mark. The header
     gives up being sticky in exchange: a header carrying a wrapped nav and a logo would
     follow the reader down the page taking a third of the screen with it. */
  html:not(.js) .site-header { position: static; }
  html:not(.js) .menu-toggle { display: none; }
  html:not(.js) .nav-shell { height: auto; min-height: var(--header-h); flex-wrap: wrap; align-content: center; padding-bottom: var(--s-12); }
  html:not(.js) .nav-cta, html:not(.js) .nav-download { display: inline-flex; }
  html:not(.js) .mobile-download { display: none; }
  /* Mark, download, contact: at 390px they fit on one line with a 16px gap and not with 28. */
  html:not(.js) .nav-shell { gap: 16px; }
  html:not(.js) .desktop-nav { display: flex; flex-wrap: wrap; width: 100%; order: 1; gap: 2px 18px; margin-top: var(--s-4); }
  html:not(.js) .desktop-nav a { font-size: 14px; padding: var(--s-6) 0; }
  .mobile-nav { padding: var(--s-12) var(--s-40) var(--s-28); background: var(--background); border-top: 1px solid var(--border); box-shadow: var(--elev-2); max-height: calc(100vh - var(--header-h) - env(safe-area-inset-top, 0px)); max-height: calc(100dvh - var(--header-h) - env(safe-area-inset-top, 0px)); overflow-y: auto; overscroll-behavior: contain; }
  .mobile-nav > a { display: block; padding: var(--s-14) 0; border-bottom: 1px solid var(--border); font-size: 16px; }
  .mobile-nav > .button { display: flex; padding: var(--s-12) var(--s-16); margin-top: var(--s-20); }
  .mobile-nav > .button + .button { margin-top: var(--s-12); }
  .hero-grid { grid-template-columns: 1.55fr 1fr; gap: 28px; }
  h1 { font-size: 56px; }
  .hero-est { display: none; }
  .hero-actions .text-button { font-size: 12px; }
  .field-photo { height: 380px; }
  .logo-row { gap: 20px; }
  .logo-tile img { height: 44px; }
  .timeline { padding-top: var(--s-28); }
  .tl-event { font-size: 9px; }
  h2 { font-size: 44px; }
  .section-heading > p { max-width: 290px; font-size: 15px; }
  .ground-intro { width: 300px; }
  .process { grid-template-columns: repeat(2, 1fr); row-gap: 40px; }
  .process h3 { min-height: 0; }
  .process p { max-width: 20.81em; min-height: 50px; }
  .ground-footer > .eyebrow { font-size: 10px; }
  /* Below 1024px the list goes back under the map: a third of a tablet is too narrow for
     seven regions, and on a phone the two columns forced the page 24px wider than the
     screen. The list's top no longer has a map edge to line up with. */
  .geography { grid-template-columns: 1fr; grid-template-areas: "copy" "map"; }
  .map-canvas { grid-template-columns: 1fr; }
  .map-panel { border-left: 0; border-top: 1px solid rgba(255, 255, 255, .14); padding: var(--s-32) var(--s-40); background: rgba(14, 14, 48, .45); }
  .map-panel-hint { margin-top: var(--s-24); }
  .geography-list { margin-top: 0; }
  .list-label { margin-top: 0; }
  .country-regions { columns: 3; }
  .country-list { max-width: none; }
  .geography h2 { font-size: 38px; }
  .projects-grid { gap: 16px; }
  .project-card { padding: var(--s-18); }
  .project-card dl > div { display: block; }
  .project-card dd { margin-top: var(--s-4); }
  .project-card h3 { font-size: 20px; min-height: 106px; }
  .contact-grid { gap: 36px; }
  .contact-grid h2 { font-size: 46px; }
  .contact-email { font-size: 18px; gap: 28px; }
  .form-row { gap: 16px; }
  .closing { padding: var(--s-80) 0; }
  .team-grid { gap: 32px 16px; }
  .person :is(h3, h4) { font-size: 24px; }
  .team-grid .person :is(h3, h4) { font-size: 18px; }
  .portrait-initials { font-size: 72px; }
  .team-grid .portrait-initials { font-size: 48px; }
  .closing-grid { grid-template-columns: 1fr; gap: 40px; }
  .closing-actions { justify-self: start; }
  .closing h2 { font-size: 46px; }
  .footer-top { grid-template-columns: 1fr; gap: var(--s-48); }
  .footer-contact { justify-self: start; }
  .footer-identity { flex-direction: row; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; }
}

@media (max-width: 767px) {
  .map-panel { padding: var(--s-24) var(--s-20); }
  .map-panel-name { font-size: 34px; margin-bottom: var(--s-16); }
  .map-panel .text-button { margin-top: var(--s-16); }
  :root { --header-h: 72px; --header-h-compact: 64px; }
  .container { width: calc(100% - 40px); }
  .section { padding-block: var(--s-64); }
  .brand img { height: 42px; }
  .is-scrolled .brand img { height: 38px; }
  .mobile-nav { padding-inline: var(--s-20); }
  .hero-content { padding-top: var(--s-36); padding-bottom: var(--s-40); }
  .hero-eyebrow { font-size: 10px; line-height: 1.6; max-width: 300px; margin-bottom: var(--s-24); }
  .hero-grid { display: block; }
  h1 { font-size: 46px; line-height: 1.04; letter-spacing: -.03em; }
  .desktop-break { display: none; }
  .hero-aside { margin-top: var(--s-24); padding: 0; }
  .hero-aside p { font-size: 16px; line-height: 1.6; max-width: 27.54em; }
  .hero-actions { margin-top: var(--s-24); gap: 12px; }
  .hero-actions .primary { width: 100%; min-height: 50px; justify-content: space-between; }
  .hero-actions .text-button { font-size: 13px; min-height: 44px; align-self: center; gap: 24px; }
  .field-photo { height: 300px; }
  .field-photo img { object-position: 44% 50%; }
  .field-photo figcaption { left: 20px; right: 20px; bottom: 16px; }
  .field-photo figcaption > span:first-child { font-size: 9px; }
  .field-photo figcaption > span:last-child { display: none; }
  .photo-band { height: 32vh; min-height: 220px; }
  .photo-band figcaption { left: 20px; right: 20px; bottom: 16px; }
  .photo-band figcaption > span:first-child { font-size: 9px; }
  .photo-band figcaption > span:last-child { display: none; }
  .trust { padding-top: var(--s-24); padding-bottom: var(--s-28); }
  .trust-heading { align-items: start; gap: 16px; }
  .trust-heading .eyebrow { font-size: 9px; line-height: 1.7; max-width: 200px; }
  .trust-heading .text-button { font-size: 11px; white-space: nowrap; min-height: 36px; padding-top: 0; align-items: start; gap: 8px; }
  .logo-row { grid-template-columns: repeat(4, 1fr); gap: 24px 20px; padding-top: var(--s-24); }
  .logo-tile:nth-child(4n + 1) > .logo-tip { left: 0; right: auto; transform: none; }   /* four to a row here */
  .logo-tile:nth-child(4n) > .logo-tip { left: auto; right: 0; transform: none; }
  .logo-tile { height: 42px; }
  .logo-tile img { height: 36px; }
  /* stacked, one ruled row per figure; the tooltip spans the row */
  .stats { grid-auto-flow: row; padding-block: 0; }
  .stat { justify-content: flex-start; padding: var(--s-24) 0; border-left: 0; border-top: 1px solid var(--border); }
  .stat:first-child { border-top: 0; }
  .stat-meta { position: static; }
  .stat-value { font-size: 72px; }
  .timeline { padding-top: var(--s-28); }
  .timeline-rail { padding-left: var(--s-24); }
  .timeline-line { position: absolute; left: 0; top: 3px; width: 1px; height: calc(100% - 6px); transform-origin: center top; }
  .timeline-marks { height: auto; display: flex; flex-direction: column; gap: 16px; }
  .timeline-marks li,
  .timeline-marks li:first-child,
  .timeline-marks li:last-child {
    position: static; transform: none;
    display: grid; grid-template-columns: 9px 1fr; column-gap: 13px; row-gap: 2px;
    align-items: baseline; justify-items: start; white-space: normal;
  }
  .tl-tick { width: 9px; height: 1px; margin-left: calc(-1 * var(--s-24)); align-self: center; }
  .tl-year { grid-column: 2; }
  .tl-event { grid-column: 2; }
  .stats-source { margin-top: 0; padding: var(--s-16) 0 var(--s-24); border-top: 1px solid var(--border); }
  h2 { font-size: 40px; line-height: 1.1; }
  .section-heading { display: block; margin-bottom: var(--s-32); }
  .section-index { font-size: 10px; margin-bottom: var(--s-20); }
  .section-heading > p { font-size: 16px; max-width: 25.70em; margin-top: var(--s-24); }
  .ledger-head { grid-template-columns: 32px minmax(0, 1fr) 20px; gap: 8px 12px; padding: var(--s-20) 0; align-items: start; }
  .ledger-no { grid-row: 1 / 3; padding-top: var(--s-4); }
  .ledger-title { font-size: 22px; }
  .ledger-line { grid-column: 2; font-size: 15px; margin-top: var(--s-6); }
  .chev { grid-column: 3; grid-row: 1; align-self: start; margin-top: var(--s-8); }
  .ledger-panel-inner { grid-template-columns: 32px minmax(0, 1fr) 20px; gap: 0 12px; }
  .ledger-col, .ledger-col + .ledger-col { grid-column: 2; }
  .ledger-col { padding-bottom: var(--s-20); }
  .ledger-also, .ledger-sectors, .ledger-cta { grid-column: 2; }
  .ground-up { padding: var(--s-64) 0 var(--s-28); }
  .ground-up h2 { font-size: 40px; line-height: 1.1; }
  .ground-up .section-heading { margin-bottom: var(--s-40); }
  .ground-intro { width: 100%; margin-top: var(--s-28); }
  .ground-intro p:first-child { font-size: 19px; }
  .process { display: block; }
  .process li { display: block; padding: 0 0 var(--s-32) var(--s-72); border-top: 0; min-height: 190px; }
  .process p { margin-bottom: 0; }
  .step-output { margin-top: var(--s-28); }
  .process li::before { content: ''; position: absolute; left: 19px; top: 40px; bottom: 0; border-left: 1px solid var(--inverse-border); }
  .process li:last-child { padding-bottom: 0; }
  .process li:last-child::before { display: none; }
  .step-number { position: absolute; left: 0; top: 0; margin: 0; }
  .process h3 { font-size: 20px; line-height: 1.35; padding-top: var(--s-6); margin-bottom: var(--s-12); }
  .process p { font-size: 16px; min-height: 0; max-width: 100%; }
  .step-output { border: 0; margin-top: var(--s-16); padding: 0; font-size: 9px; }
  .ground-footer { margin-top: var(--s-32); display: block; }
  .ground-footer > .eyebrow { font-size: 9px; }
  .ground-footer > span:last-child { display: block; margin-top: var(--s-10); font-size: 13px; }
  .geography { row-gap: 28px; }
  .country-regions { columns: 2; column-gap: 24px; }
  .list-label { margin-top: 0; }
  .geography h2 { font-size: 40px; }
  .geography-copy > p:not(.eyebrow) { max-width: 100%; font-size: 16px; }
  .country-list { max-width: none; }
  .map-figure figcaption { font-size: 9px; }
  .projects-grid { grid-template-columns: 1fr; gap: 24px; }
  .project-card { padding: var(--s-24); }
  .project-card h3 { font-size: 25px; line-height: 1.3; min-height: 0; max-width: 14.28em; margin-top: var(--s-24); }
  .project-card dl > div { display: grid; grid-template-columns: 92px 1fr; gap: 12px; }
  .project-card dd { font-size: 13px; margin: 0; }
  /* On a phone the tiles become rows: number, name, arrow, one line of each. */
  .sector-grid { grid-template-columns: 1fr; gap: 8px; }
  .sector-tile { min-height: 0; flex-direction: row; align-items: center; gap: var(--s-16); padding: var(--s-14) var(--s-16); }
  .sector-top { display: contents; }
  .sector-label { flex: none; width: 24px; order: 0; }
  .sector-name { flex: 1; order: 1; font-size: 18px; line-height: 1.3; }
  .sector-arrow { flex: none; order: 2; width: 32px; height: 32px; }
  .sector-tile:is(:hover, :focus-visible) .sector-label > span { transform: none; }
  .cross-cutting { gap: 12px 24px; font-size: 13px; }
  .cross-cutting > .eyebrow { width: 100%; margin-bottom: var(--s-2); }
  /* Phones: the client and the years hold the first line, the scope and place follow. */
  .fw-row { grid-template-columns: minmax(0, 1fr) auto; gap: 4px 16px; padding: var(--s-16) 0; }
  .fw-years { grid-row: 1; grid-column: 2; }
  .fw-client { font-size: 21px; }
  .fw-scope { grid-column: 1 / -1; }
  .fw-place { grid-column: 1 / -1; }

  .contact-section { padding: var(--s-64) 0; }
  .contact-grid { grid-template-columns: 1fr; gap: 40px; }
  .contact-grid h2 { font-size: 46px; line-height: 1.08; }
  .contact-grid > div > p:not(.eyebrow) { max-width: 100%; }
  .contact-email { font-size: 20px; margin-top: var(--s-28); gap: 40px; }
  .form-row { grid-template-columns: 1fr; gap: 0; }
  .form-bottom { gap: 16px; }
  .form-bottom .button { width: 100%; min-height: 50px; }
  .closing { padding: var(--s-64) 0; }
  .team { padding: var(--s-64) 0 var(--s-72); }
  .team-group + .team-group { margin-top: var(--s-56); }
  .team-lead { grid-template-columns: 1fr; gap: 0; }
  .team-lead .person { display: grid; grid-template-columns: 116px 1fr; column-gap: 20px; padding: var(--s-16) 0; border-bottom: 1px solid var(--ink-line); }
  .team-lead .person:first-child { padding-top: 0; }
  .team-lead .portrait { grid-row: 1 / 3; }
  .team-lead .person :is(h3, h4) { align-self: end; margin: 0; font-size: 26px; }
  .team-lead .person .eyebrow { align-self: start; }
  .team-lead .portrait-initials { font-size: 44px; }
  .team-lead .portrait::before { inset: 8px; }
  .team-grid { grid-template-columns: repeat(2, 1fr); gap: 32px 16px; }
  .team-grid .person :is(h3, h4) { font-size: 20px; }
  .team-grid .portrait-initials { font-size: 56px; }
  .closing h2 { font-size: 40px; line-height: 1.1; }
  .closing p:not(.eyebrow) { font-size: 16px; }
  .closing-actions { width: 100%; }
  .closing-button { width: 100%; min-width: 0; }
  .closing-email { font-size: 22px; }
  .site-footer { padding-top: var(--s-56); }
  .footer-top { gap: var(--s-40); padding-bottom: var(--s-48); }
  .footer-identity { flex-direction: column; align-items: flex-start; gap: var(--s-32); }
  .footer-email { font-size: 24px; }
  .footer-nav a { grid-template-columns: 40px minmax(0, 1fr) 20px; padding: var(--s-14) 0; }
  .footer-name { font-size: 30px; }
  .footer-brand { width: min(100%, 260px); }
  .footer-contact { justify-self: start; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: var(--s-8); font-size: 12px; }
  .footer-meta { column-gap: var(--s-20); }
  dialog { padding: var(--s-24); }
  .dialog-top { top: -24px; margin: calc(-1 * var(--s-24)) calc(-1 * var(--s-24)) var(--s-20); padding: var(--s-24) var(--s-24) var(--s-10); }
  dialog h2 { font-size: 34px; }
  .sector-head { padding: var(--s-14) var(--s-14) var(--s-24) var(--s-24); }
  .sector-head-top { margin-bottom: var(--s-16); }
  .sector-dialog h2 { font-size: 30px; }
  .sector-body { padding: var(--s-24); }
  .sector-text { font-size: 17px; margin-bottom: var(--s-24); }
}

@media (max-width: 359px) {
  h1 { font-size: 40px; }
  .country-list { grid-template-columns: 1fr; }
  .container { width: calc(100% - 32px); }
  .hero-eyebrow { font-size: 9px; }
  .trust-heading { gap: 8px; }
  .trust-heading .eyebrow { max-width: 160px; }
  .ground-up h2 { font-size: 36px; }
  .contact-grid h2 { font-size: 40px; }
}

/* ==========================================================================
   Inner pages: Services and Where we work
   ========================================================================== */
.desktop-nav a[aria-current="page"] { color: var(--navy); }
.mobile-nav > a[aria-current="page"] { color: var(--accent); }
.section-more { margin-top: var(--s-32); display: flex; justify-content: flex-end; }

/* Page hero ---------------------------------------------------------------- */
.page-hero {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(760px 380px at 85% 40%, rgba(10, 144, 210, .22), transparent 70%),
    var(--navy);
  color: var(--inverse-text);
  padding: var(--s-80) 0 var(--s-88);
  border-bottom: 3px solid var(--cyan);
}
.page-hero::before, .qa-band::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1px);
  background-size: 26px 26px;
  opacity: .45;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, .9));
  mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, .9));
}
.page-hero > .container, .qa-band > .container { position: relative; }
.page-hero-grid { display: grid; grid-template-columns: 7fr 5fr; gap: 64px; align-items: end; }
.page-hero .section-index { color: var(--cyan-bright); }
/* The measure is held in em, not ch. A ch is the width of the font's own "0", so a
   measure set in ch changes the moment the face does: this headline's box was 677px with
   Libre Caslon and 558px with the fallback, and the headline rewrapped under it. 10.58em
   is the same 17 characters of Libre Caslon, and it keeps scaling with the font size at
   each breakpoint, but it no longer depends on which font has arrived. */
.page-hero h1 { font-size: 64px; line-height: 1.05; max-width: 10.58em; text-wrap: balance; }
.page-hero h1 em { color: var(--cyan-bright); }
.page-hero-aside p { color: var(--inverse-muted); font-size: 17px; line-height: 1.6; max-width: 24.48em; text-wrap: pretty; }
.page-hero-aside .hero-actions { margin-top: var(--s-28); }
.inverse-link { color: var(--inverse-text); }
.inverse-link:hover { color: var(--cyan-bright); text-decoration-color: var(--cyan-bright); }

.page-hero-light { background: var(--background); color: var(--text-primary); border-bottom: 0; padding: var(--s-64) 0 var(--s-40); }
.page-hero-light::before { display: none; }
.page-hero-light .section-index { color: var(--accent); }
.page-hero-light h1 em { color: var(--accent); }
.page-hero-light .page-hero-aside p { color: var(--text-muted); }

/* Chapter navigation ------------------------------------------------------- */
.chapter-nav {
  position: sticky;
  top: var(--header-h-compact);
  z-index: 40;
  background: rgba(248, 247, 243, .94);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.chapter-nav ol { display: flex; gap: 32px; overflow-x: auto; scrollbar-width: none; }
.chapter-nav ol::-webkit-scrollbar { display: none; }
.chapter-nav a {
  display: inline-flex; align-items: center; gap: 10px;
  padding: var(--s-18) 0;
  font-size: 13px; font-weight: 500; white-space: nowrap;
  color: var(--text-muted);
  transition: color .2s var(--ease-state);
}
.chapter-nav a > span:first-child { font-family: var(--mono); font-size: 11px; color: var(--accent); }
.chapter-nav a:hover, .chapter-nav a[aria-current="true"] { color: var(--navy); }

/* Service pillars ---------------------------------------------------------- */
.pillars { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--border); border-left: 1px solid var(--border); }
.pillar { padding: var(--s-28) var(--s-32) var(--s-32); border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.pillar > .eyebrow { color: var(--accent); }
.pillar h2, .pillar h3 { font-family: var(--serif); font-size: 26px; font-weight: 400; letter-spacing: -.02em; margin: var(--s-18) 0 var(--s-16); }   /* h2 in the Services hero, under the page's h1 (2026-10-14) */
.pillar ul li { border-top: 1px solid var(--border); }
.pillar ul a { display: flex; justify-content: space-between; gap: 16px; padding: var(--s-12) 0; font-size: 15px; color: var(--text-primary); transition: color .2s; }
.pillar ul a .icon { color: var(--accent); }
.pillar ul a:hover { color: var(--accent); }

/* Service detail ----------------------------------------------------------- */
/* A chapter link lands the chapter's top rule at the sheet's padding (header + 24): the 56px it once added
   on top of that was written for a strip across the page, and the strip is a rail beside the chapters from
   1024px (2026-10-14; the ledger's e11 check measured the words 243px under the header). */
.service-detail { border-top: 1px solid var(--border); padding: var(--s-88) 0; scroll-margin-top: 0; }
.service-grid { display: grid; grid-template-columns: 5fr 7fr; gap: 64px; align-items: start; }
.service-lead { position: sticky; top: calc(var(--header-h-compact) + 84px); }
.service-lead h2 { font-size: 44px; line-height: 1.08; }
.service-promise { font-family: var(--serif); font-size: 26px; line-height: 1.3; color: var(--accent); margin-top: var(--s-20); letter-spacing: -.01em; }
.service-summary { color: var(--text-muted); margin-top: var(--s-16); max-width: 24.48em; }

.service-lead .button { margin-top: var(--s-28); }
.service-block { border-top: 1px solid var(--border); padding: var(--s-28) 0 var(--s-32); }
.service-block:last-child { border-bottom: 1px solid var(--border); }
.service-block > h3.eyebrow { color: var(--accent); font-weight: 400; font-size: 12px; margin-bottom: var(--s-20); letter-spacing: .075em; }
.check-list li { position: relative; padding-left: var(--s-24); margin-bottom: var(--s-12); font-size: 17px; line-height: 1.55; }
/* a round dot, not a dash (Allam, 2026-10-14: "replace them with smooth dots"); 6px, centred on the x-height of the first line */
.check-list li::before { content: ''; position: absolute; left: 2px; top: 10px; width: 6px; height: 6px; border-radius: 50%; background: var(--cyan); }
.deliverables { display: grid; grid-template-columns: 1fr 1fr; column-gap: 32px; }
.deliverables:has(> li:only-child) { grid-template-columns: 1fr; }
.deliverables li { padding: var(--s-10) 0; border-bottom: 1px solid var(--border); font-size: 15px; }
/* One engagement from the Capacity Statement under each service, so the service is shown
   as a job that was done and not only as a list of what would be delivered. The title is
   the statement's own, set in the serif because it is the one line of prose in the block;
   the facts under it use the project cards' label-and-value pair. Built by
   tools/build-service-examples.js; asked for by Charlotta Lahnalahti on 2026-09-22. */
.service-example .example-title { font-family: var(--serif); font-size: 21px; line-height: 1.35; letter-spacing: -.01em; max-width: 30em; text-wrap: pretty; }
.service-example dl { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-top: var(--s-18); }
.service-example dl > div { display: flex; gap: 8px; align-items: baseline; }
.service-example dt { font-family: var(--mono); font-size: 10px; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.service-example dd { margin: 0; font-size: 14px; }
.service-example .source-note { margin-top: var(--s-18); }
.mini-process { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px 32px; }
.mini-process li { display: grid; grid-template-columns: 36px 1fr; gap: 14px; align-items: start; }
.mini-process li > span { font-family: var(--mono); font-size: 12px; width: 36px; height: 36px; border: 1px solid var(--border); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--accent); }
.mini-process strong { display: block; font-weight: 600; font-size: 16px; margin-bottom: var(--s-4); }
.mini-process p { font-size: 15px; color: var(--text-muted); line-height: 1.55; }

/* Methods ------------------------------------------------------------------ */
.methods-grid { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--border); border-left: 1px solid var(--border); }
.method { padding: var(--s-28) var(--s-24) var(--s-32); border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); transition: background-color .3s var(--ease-out); }
.method:hover { background: #FFFFFF; }
.method::before { content: ''; display: block; width: 10px; height: 10px; background: var(--cyan); margin-bottom: var(--s-24); }
.method h3 { font-size: 18px; margin-bottom: var(--s-10); }
.method p { font-size: 15px; color: var(--text-muted); line-height: 1.55; }

/* Quality assurance band --------------------------------------------------- */
.qa-band { background: var(--navy); color: var(--inverse-text); padding: var(--s-96) 0; position: relative; overflow: hidden; }
.qa-band .section-index { color: var(--cyan-bright); }
.qa-band h2 em { color: var(--cyan-bright); }
.qa-band .section-heading > p { color: var(--inverse-muted); }
.qa-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px 32px; }
.qa-grid li { border-top: 1px solid var(--inverse-border); padding-top: var(--s-24); }
.qa-grid h3 { font-size: 19px; margin-bottom: var(--s-10); }
.qa-grid p { color: var(--inverse-muted); font-size: 15px; line-height: 1.6; }

/* Map section on the Where we work page ------------------------------------ */
.map-section { padding: var(--s-8) 0 var(--s-16); }

/* Country profiles --------------------------------------------------------- */
.country-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.country-card {
  padding: var(--s-28);
  background: #FFFFFF;
  border: 1px solid var(--border);   /* the top edge lost its 2px navy accent with the project cards' (2026-09-25) */
  display: flex; flex-direction: column;
  scroll-margin-top: calc(var(--header-h-compact) + var(--s-24));
  transition: transform .3s var(--ease-out), box-shadow .3s var(--ease-out), border-color .3s;
}
.country-card:hover, .country-card.is-active { transform: translateY(-3px); box-shadow: var(--shadow-1); border-top-color: var(--cyan); }
.country-card > .eyebrow { color: var(--text-muted); font-size: 10px; }
.country-card h3 { font-family: var(--serif); font-size: 32px; font-weight: 400; letter-spacing: -.02em; margin: var(--s-14) 0 var(--s-12); }
.country-card > p { font-size: 15px; line-height: 1.6; color: var(--text-muted); }
.country-work { margin-top: var(--s-20); padding-top: var(--s-16); border-top: 1px solid var(--border); }
.country-work .eyebrow { font-size: 10px; color: var(--accent); }
/* Since 2026-10-14 (Allam: no "tickets") the names run as a line of text, a cyan middle dot between them, at the
   body's size; the outlined pills are gone. The "and N more" that opens the full list reads as a link in the run. */
.chips { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0; margin-top: var(--s-10); }
.chips li { font-size: 15px; line-height: 1.6; padding: 0; border: 0; color: var(--text-primary); }
.chips li:not(:last-child)::after { content: "\00B7"; margin-inline: var(--s-10); color: var(--accent); }
/* A country's clients past the first six (2026-10-07, Daniel Seckman's workbook): one link-like button at the
   end of the chips that opens the full list in the country's dialog; 44px tall like every control, so the
   row centres the chips beside it. */
.chips { align-items: center; }
.chips li:has(> .chip-more) { padding: 0; border: 0; border-radius: 0; }
/* 44px to the pointer, but its margins give back the height a chip does not have, so a row with it in is no
   taller than a row without and the cards do not move when the web font arrives; the underline is the site's own
   (.link-text), and the focus ring the site's. */
.chip-more { min-height: 44px; margin-block: calc(-1 * var(--s-10)); padding: 0; border: 0; background: none; color: var(--accent); font: 500 15px/1.6 var(--sans); cursor: pointer; }   /* 44 less 2x10 = the 24px line, so the run keeps one line height */
.country-card .text-button { margin-top: auto; padding-top: var(--s-20); font-size: 13px; }
/* Two actions at the foot of a card sit together: the first takes the slack, the second
   follows it. */
.country-card .text-button + .text-button { margin-top: 0; padding-top: var(--s-10); }
.access-band { padding-top: var(--s-96); }

/* Inner pages: responsive -------------------------------------------------- */
@media (max-width: 1199px) {
  .page-hero h1 { font-size: 54px; }
  .service-lead h2 { font-size: 38px; }
  .service-grid { gap: 40px; }
  .methods-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1023px) {
  .page-hero-grid { grid-template-columns: 1fr; gap: 32px; }
  .page-hero h1 { font-size: 48px; }
  .pillars { grid-template-columns: 1fr; border-left: 0; }
  .pillar { border-right: 0; padding: var(--s-24) 0; }
  .service-grid { grid-template-columns: 1fr; gap: 32px; }
  .service-lead { position: static; }
  .country-grid { grid-template-columns: 1fr 1fr; }
  .qa-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 767px) {
  .page-hero { padding: var(--s-56) 0 var(--s-64); }
  .page-hero h1 { font-size: 40px; }
  .page-hero-light { padding: var(--s-40) 0 var(--s-24); }
  .chapter-nav .container { width: 100%; padding-inline: var(--s-20); }
  .chapter-nav ol { gap: 24px; }
  .service-detail { padding: var(--s-56) 0; }
  .service-lead h2 { font-size: 32px; }
  .service-promise { font-size: 22px; }
  .deliverables { grid-template-columns: 1fr; }
  .mini-process { grid-template-columns: 1fr; }
  .methods-grid { grid-template-columns: 1fr; border-left: 0; }
  .method { border-right: 0; padding: var(--s-24) 0; }
  .qa-band { padding: var(--s-64) 0; }
  .qa-grid { grid-template-columns: 1fr; gap: 28px; }
  .country-grid { grid-template-columns: 1fr; }
  .country-card { padding: var(--s-24); }
  .section-more { justify-content: flex-start; }
}

/* Coarse pointers: marker hit areas would be ~10px and four Levant markers fall inside one
   fingertip, so tapping cannot be made reliable. The country list (homepage) and the country
   cards (Where we work) carry the same information and are full-size targets. */
@media (hover: none) {
  .map-dot { pointer-events: none; }
}
@media (max-width: 767px) {
  .map-labels { display: none; }
}

/* Type floors (Part I, I2) ------------------------------------------------
   Nothing on the site is set below 11px, no mono label below 12px, and every mono label
   is tracked at least .08em. Around forty text elements had drifted under that, most of
   them 10px labels in the muted grey that read as fine print. These rules are written
   last and at the specificity of the rules they correct, so they win by order; a rule
   that raised a size above the floor is left as it was. Body copy floors at 16px are set
   in the rules themselves, below in this block. */
.eyebrow, .stat .eyebrow, .trust-heading > .eyebrow, .trust-heading .eyebrow, .hero-eyebrow, .section-index, .list-label, .region-label,
.person .eyebrow, .team-grid .person .eyebrow, .country-work .eyebrow, .client-groups .eyebrow, .project-groups .eyebrow, .published-heading .eyebrow,
.step-output, .tag, .tl-year, .tl-event, .stats-source, .source-note, .published-meta, .project-meta, .contact-promise, .fw-current, .fw-place, .fw-years,
.team-label, .project-card dt, .service-example dt, .map-tooltip span, .map-figure figcaption, .dialog-top .eyebrow,
.photo-band figcaption > span:first-child, .field-photo figcaption > span:first-child, .ground-footer > span:first-child, .country-card > .eyebrow,
.project-meta .eyebrow, .example-meta dt, .fw-ledger .eyebrow, .cover-client, .cover-meta, .map-region {
  font-size: 13px;   /* the floor rose from 12px in the readability pass of 2026-09-30 */
  letter-spacing: .08em;
}
/* the same floors where an earlier rule spoke with more specificity */
.service-block > h3.eyebrow, .project-result > .eyebrow, .project-top .eyebrow, footer .eyebrow, .footer-top .eyebrow,
.fw-ledger .ledger-no, .ledger .ledger-no, .ledger-sectors .eyebrow, .ledger-also .eyebrow,
.sector-label, .cross-cutting > .eyebrow, .section-heading .eyebrow, .ground-footer > .eyebrow, .chapter-nav a > span:first-child,
.trust-heading > .eyebrow { font-size: 13px; letter-spacing: .08em; }
.ledger-no { font-size: 13px; letter-spacing: .08em; }
.closing-signal { letter-spacing: .08em; }
/* the map's region names are user units, drawn smaller than stated as the map shrinks;
   11px is the brief's own floor for them */
.map-labels span { font-size: 12px; }
/* body copy: 17px is the floor for prose and for lists that are read as prose (16px until 2026-09-30) */
.published-heading > p:not(.eyebrow), .deliverables li, .country-list li, .client-list li, .small, .project-title, .country-card > p:not(.eyebrow), .example-title ~ dl dd { font-size: 17px; }



/* Interaction states (Part J, J1) ---------------------------------------------------------
   Every control has the same six states: rest, hover, focus-visible, active, disabled and
   loading. Hover lives with each component and focus-visible under "One focus ring"; the
   other three are here, once, so no control can drift. One radius for every control, held in
   a token: the site is square-cornered (DESIGN.md, "Hairline grid"), and the 2px on the focus
   ring belongs to the ring. */
:root { --radius-control: 0px; }
.button, .text-button, .nav-cta, .icon-link, .icon-button, .ledger-head, .menu-toggle, .close-dialog, .stat-why, .form-field :is(input, textarea) { border-radius: var(--radius-control); }

/* Pressed: down a pixel, and a filled face 6% darker than whatever it was showing, in 80ms
   (--dur-press, motion.css) so the click feels physical. Cards that lift on hover settle
   part of the way instead of dropping through their rest position. */
:is(a, button):active:not(:disabled, [aria-disabled="true"], .is-loading, .is-sending) { transform: translateY(1px); transition-duration: var(--dur-press, 80ms); }
.button:active:not(:disabled, [aria-disabled="true"], .is-loading, .is-sending) { background: color-mix(in srgb, var(--btn-bg) 94%, #000); }
:is(.project-card, .country-card):active { transform: translateY(-2px); transition-duration: var(--dur-press, 80ms); }

/* Disabled: 40% and the not-allowed cursor, and no answer to the pointer. The sent enquiry
   button keeps full strength on purpose (see the form: the enquiry is in, not unavailable). */
:is(button, a, .button, .text-button)[aria-disabled="true"], button:disabled { opacity: .4; cursor: not-allowed; }
:is(button:disabled, [aria-disabled="true"]):is(:hover, :active) { transform: none; }
.primary:is(:disabled, [aria-disabled="true"]):hover { --btn-bg: var(--primary); }
.secondary:is(:disabled, [aria-disabled="true"]):hover { --btn-bg: transparent; --btn-fg: var(--primary); }
.inverse:is(:disabled, [aria-disabled="true"]):hover { --btn-bg: var(--background); }
.text-button:is(:disabled, [aria-disabled="true"]):hover { color: var(--text-primary); }
a[aria-disabled="true"] .link-text { background-size: 0 1px, 0 1px; }

/* Loading: the enquiry button's three breathing dots (E8), on any button. Add .is-loading
   (app.js setLoading() does, with aria-busy and the dots' markup): the label and its icon
   step out, the dots stand where the label was, and the button stops answering until it is
   done. The form keeps its own .is-sending name for its sending and sent sequence. */
.button.is-loading { position: relative; color: transparent; pointer-events: none; transition: none; }
.button.is-loading > *:not(.button-dots) { opacity: 0; }
.button-dots { position: absolute; inset: 0; margin: auto; display: flex; align-items: center; justify-content: center; gap: 5px; height: 4px; width: 26px; opacity: 0; }
.button-dots i { width: 4px; height: 4px; border-radius: 50%; background: var(--btn-fg, var(--inverse-text)); }
.button.is-loading .button-dots { opacity: 1; }

/* The header's logo link and the footer's plain address links are 44px targets too: the
   image and the line keep their size, the box around them grows. */
.brand { display: inline-flex; align-items: center; min-height: 44px; }
.footer-brand { align-items: flex-start; }   /* the footer's logo keeps its line; the box grows downward */
.closing-email, .contact-email { min-height: 44px; }


/* Typographic finish (Part J, J9) ------------------------------------------------------------
   Headlines balance so none ends on a single word; paragraphs take the prettier breaks.
   Hanging punctuation on the large serif lines is honoured where the engine has it (Safari)
   and ignored elsewhere. */
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
h1, .page-hero h1 { hanging-punctuation: first; }

/* Sourced statistics (Part J, J5) ------------------------------------------------------------
   Each figure in the strip can say how it was counted and where it comes from. The label
   under the figure is a button carrying a resting hairline; hovering the cell, focusing the
   button or tapping it opens one line in the map tooltip's dress (navy, small, above the
   page), Escape closes it and leaves the focus where it was. The line is real text in the
   page, described to the button, so a screen reader hears it with the figure. */
.stat { position: relative; }
.stat-why { display: inline-flex; align-items: flex-end; width: min-content; margin: 0 0 .06em; padding: 0 0 var(--s-6); min-height: 44px; border: 0;   /* a 44px target, its words stacked, seated on the numeral's baseline (2026-09-27) */ background: none; font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--text-muted); text-align: left; cursor: help; background-image: linear-gradient(var(--border), var(--border)); background-repeat: no-repeat; background-size: 100% 1px; background-position: left bottom; }
.stat:hover .stat-why, .stat-why:focus-visible, .stat.is-open .stat-why { background-image: linear-gradient(var(--accent), var(--accent)); color: var(--text-primary); }
.stat-tip { position: absolute; left: 0; top: calc(100% + var(--s-8)); z-index: 30; margin: 0; width: max-content; max-width: min(38ch, calc(100vw - 40px)); padding: var(--s-10) var(--s-12); background: var(--navy); color: var(--inverse-text); font-family: var(--sans); font-size: 13px; line-height: 1.45; letter-spacing: 0; text-transform: none; text-wrap: pretty; box-shadow: var(--elev-2, 0 8px 24px rgba(28, 28, 62, .18)); opacity: 0; visibility: hidden; transition: opacity var(--dur-1, 160ms) var(--ease-state), visibility 0s linear var(--dur-1, 160ms); }
/* the tooltip hangs from its label; from 768px the last cell's opens leftwards so it stays on the page */
@media (min-width: 768px) { .stat:last-child .stat-tip { left: auto; right: 0; } .stat:last-child .stat-tip::before { left: auto; right: var(--s-16); } }
.stat-tip::before { content: ''; position: absolute; left: var(--s-16); top: -5px; width: 10px; height: 10px; background: var(--navy); transform: rotate(45deg); }
.stat:hover:not(.is-dismissed) .stat-tip, .stat.is-open .stat-tip, .stat:focus-within:not(.is-dismissed) .stat-tip { opacity: 1; visibility: visible; transition-delay: 0s; }
@media (hover: none) { .stat-why { cursor: default; } }

/* Not found ---------------------------------------------------------------------------------- */
.not-found-links { list-style: none; margin: 0; padding: 0; max-width: 640px; border-top: 1px solid var(--border); }
.not-found-links li { border-bottom: 1px solid var(--border); }
.not-found-links .text-button { width: 100%; padding-block: var(--s-16); }

/* Print (Part J, J7) ---------------------------------------------------------------------------
   Hairlines kept, photographs at 60% of the measure, the header and footer collapsed to the
   identity, every link off the site followed by its address. The dark bands print on white:
   their inverse tokens are remapped to the page's own, since a printer paints no navy and
   light text on white is no text at all. Everything the motion layer holds back until it is
   in view is shown, and the folds are open (see "Framework agreements"). */
@media print {
  :root { --inverse-text: var(--text-primary); --inverse-muted: var(--text-muted); --inverse-border: var(--border); --ink-text: var(--text-primary); --ink-label: var(--text-muted); --ink-muted: var(--text-muted); --ink-line: var(--border); --ink-mark: var(--text-muted); --ink-initial: var(--text-muted); }
  @page { margin: 16mm; }
  html, body { background: #fff; }
  .ground-up, .team, .page-hero, .qa-band, .closing, footer, .contact-section, .hero { background: none !important; color: var(--text-primary); }
  .desktop-nav, .mobile-nav, .nav-cta, .nav-download, .menu-toggle, .chapter-nav, .scroll-progress, .skip-link, .section-pin, .cursor-label, .map-tooltip, .stat-tip, .hero-actions, .section-more, .fw-more, .list-more, .footer-links, .footer-nav, .footer-up, .brand-marks, .footer-bottom .icon-link, .form-submit, dialog { display: none !important; }
  .site-header { position: static; border-bottom: 1px solid var(--border); }
  footer .footer-top { display: block; padding: var(--s-24) 0; border-top: 1px solid var(--border); }
  footer .footer-top > div { margin-top: var(--s-16); }
  .field-photo, .photo-band { width: 60%; height: auto; aspect-ratio: 3 / 2; max-height: none; }
  :is(.field-photo, .photo-band) picture { position: static; height: auto; top: auto; }
  :is(.field-photo, .photo-band) img { transform: none !important; animation: none !important; filter: none !important; }
  main a[href^="http"]::after { content: " (" attr(href) ")"; font-family: var(--mono); font-size: 10px; letter-spacing: 0; color: var(--text-muted); word-break: break-all; }
  html.js [data-draw] { border-color: var(--border) !important; }
  html.js [data-draw]::after { content: none !important; }
  [data-reveal], .country-group > .country-row, .fw-row, .published-list li, .hero-frame:first-child, .field-photo, .photo-band { opacity: 1 !important; transform: none !important; visibility: visible !important; }
  :is(.field-photo, .photo-band)::before { content: none !important; }
  .ledger-panel { height: auto !important; overflow: visible !important; display: block !important; }
  .ledger-row .ledger-head::after { content: none; }
  h2, h3 { break-after: avoid; }
  .project-card, .fw-row, .country-card, .person, .service-example, .published-list li, .stat { break-inside: avoid; }
  .stats-source, .source-note { color: var(--text-muted); }
}

/* Touch heights last of all (Part J, J1): these outrank every component's own height above,
   so they sit after the last of them. */
@media (pointer: coarse) {
  .button, .text-button, .hero-actions .text-button, .trust-heading .text-button, .nav-cta, .nav-download, .icon-link, .icon-button, .ledger-head, .menu-toggle, .close-dialog, .stat-why, .chapter-nav a, .footer-links a, .mobile-nav a, .published-title, .brand-link, .brand, footer .footer-top a:not(.icon-link):not(.brand-link):not(.brand):not(.footer-links a), .closing-email, .contact-email { min-height: 48px; }
}

/* On a coarse pointer the markers are inert (see "Hover affordances on the map"), so the caption
   does not ask for a marker to be selected there; the list and the cards carry the facts. */
/* Touch: the logo tiles are buttons since 2026-09-25 and take the 48px coarse-pointer height like every control */
@media (pointer: coarse) { button.logo-tile { min-height: 48px; } }
/* Touch: the region chips take the 48px coarse-pointer height like every control (2026-09-29) */
@media (pointer: coarse) { .map-region { min-height: 48px; } }

/* Services page as chapters (2026-10-14) ------------------------------------------------
   The three pillars sit in the hero, so the page opens once instead of twice. The chapter
   list is a rail beside the chapters, sticky under the header; under 1024px there is no
   room for it and the strip it replaces comes back. "How we do it" and the example
   engagement fold behind a disclosure, so a chapter is read in one screen and opened when
   needed. The chapter number is set large in the paper grey as a mark; the mono label
   still carries the number for a reader who cannot see it. */
/* No table in the hero (Allam, 2026-10-14): the three groups are columns of text with one faint
   rule above them, not a grid of cells. The links sit on the navy in cream, the arrow beside the
   words rather than at the far edge of a cell. */
.pillars-hero { margin-top: var(--s-56); padding-top: var(--s-32); border: 0; border-top: 1px solid rgba(244, 243, 251, .18); gap: 0 var(--s-56); }
.pillars-hero .pillar { border: 0; padding: 0; }
.pillars-hero .pillar > .eyebrow { color: var(--cyan-bright); }
.pillars-hero .pillar h2 { color: var(--inverse-text); margin: var(--s-14) 0 var(--s-12); }   /* h2: the page's h1 is right above them (partj J8, 2026-10-14) */
.pillars-hero .pillar ul li { border: 0; }
.pillars-hero .pillar ul a { justify-content: flex-start; gap: 10px; min-height: 44px; padding: var(--s-10) 0; font-size: 16px; color: rgba(244, 243, 251, .9); }   /* 44px to the finger (mobile pass) */
.pillars-hero .pillar ul a .icon { color: var(--cyan-bright); }
.pillars-hero .pillar ul a:hover { color: var(--cyan-bright); }
.services-intro { padding-bottom: var(--s-24); }
.services-intro .section-heading { margin-bottom: 0; }

.chapter-rail > .eyebrow { display: none; }
.chapter-no { display: block; font-family: var(--serif); font-size: 88px; line-height: .9; letter-spacing: -.03em; color: var(--border); margin-bottom: var(--s-14); font-variant-numeric: tabular-nums; }

.service-fold { border-top: 1px solid var(--border); }
.service-fold:last-child { border-bottom: 1px solid var(--border); }
.service-fold > summary { display: flex; align-items: center; gap: 16px; min-height: 56px; padding: var(--s-12) 0; cursor: pointer; list-style: none; color: var(--text-primary); transition: color .2s var(--ease-state); }
.service-fold > summary::-webkit-details-marker { display: none; }
.service-fold > summary:hover { color: var(--accent); }
.service-fold > summary > h3.eyebrow { margin: 0; color: var(--accent); font-weight: 400; }
.service-fold .fold-hint { margin-left: auto; font-size: 13px; color: var(--text-muted); text-align: right; }
.service-fold .chev { color: var(--accent); transition: transform var(--dur-1) var(--ease-state); }
.service-fold[open] .chev { transform: rotate(180deg); }
.service-fold > .service-block { border-top: 0; padding-top: var(--s-8); }
.service-fold > .service-block:last-child { border-bottom: 0; }
.service-fold > summary:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }   /* the site's ring (e10) */
@media (prefers-reduced-motion: reduce) { .service-fold .chev { transition: none; } }

@media (min-width: 1024px) {
  .chapters { display: grid; grid-template-columns: 200px minmax(0, 1fr); column-gap: 64px; align-items: start; }
  .chapters-body { min-width: 0; }
  /* with the rail beside the text there is nothing over the page but the header: the chapter's top rule tucks
     under its edge and the chapter number stands 88px below it (the words 181px down, within e11's 200) */
  .service-detail { scroll-margin-top: calc(-1 * var(--s-24)); }
  /* pinned so its first line sits where the chapter's sticky lead sits (header + 84px): the
     rail's own 88px of top padding, less 4px */
  .chapter-rail { position: sticky; top: calc(var(--header-h-compact) - var(--s-4)); z-index: 1; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; border-bottom: 0; padding-top: var(--s-88); }
  .chapter-rail > .eyebrow { display: block; color: var(--text-muted); margin-bottom: var(--s-10); }
  .chapter-rail ol { display: block; overflow: visible; border-bottom: 1px solid var(--border); }
  .chapter-rail a { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 12px; align-items: baseline; padding: var(--s-10) 0; white-space: normal; border-top: 1px solid var(--border); font-size: 14px; line-height: 1.4; }
  .chapter-rail a[aria-current="true"] { color: var(--navy); font-weight: 600; }
  .chapter-rail a > span:first-child { color: var(--text-muted); }
  .chapter-rail a[aria-current="true"] > span:first-child { color: var(--accent); }
  .chapters .service-grid { gap: 48px; }
  .chapters .service-lead h2 { font-size: 40px; }
}
@media (max-width: 1023px) {
  .chapter-no { font-size: 64px; }
}

/* Where we work as a ledger (2026-10-14) -------------------------------------------------
   Forty-one cards of one shape became rows grouped by region: one line each (number, name,
   how many clients are on record), opening on click to the context line, the work delivered,
   the clients and the two actions. One row open at a time, as the services ledger on the
   homepage, with the same height animation. The three place photographs sit after the rows
   of the region they were taken in. The region tabs above the list and the chips on the map
   share one state (app.js). The card rules above stay for the print sheet and the old suites. */
.ledger-regions { display: flex; flex-wrap: wrap; gap: 0 var(--s-28); border-bottom: 1px solid var(--border); margin-bottom: var(--s-32); }
.ledger-region { min-height: 44px; padding: var(--s-10) 0; margin-bottom: calc(-1 * var(--hairline)); background: none; border: 0; border-bottom: 1px solid transparent; font-family: var(--mono); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); cursor: pointer; transition: color var(--dur-1) var(--ease-state), border-color var(--dur-1) var(--ease-state); }
.ledger-region:hover { color: var(--text-primary); }
.ledger-region[aria-pressed="true"] { color: var(--text-primary); border-bottom-color: var(--cyan); }
.country-group + .country-group, .country-notes + .country-group, .country-group + .country-notes { margin-top: var(--s-48); }
.group-head { display: flex; justify-content: space-between; align-items: baseline; gap: 24px; padding-bottom: var(--s-10); }
.group-head .region-label { margin: 0; color: var(--text-muted); }
.group-count { color: var(--accent); }   /* the one lit figure in each heading (2026-10-14) */
.country-row { border-top: 1px solid var(--border); scroll-margin-top: 0; }   /* the sheet's scroll-padding already clears the header by 24px; a margin on top of it landed the row 200px down (2026-10-14) */
/* Scrolling to a row from outside its section: the section's label pins under the header as the row arrives, and the
   sheet's padding only grows by that band once it has; the row clears the band from the start instead of landing under it. */
section:not(.is-pinned) .country-row { scroll-margin-top: var(--pin-h); }
.country-group > .country-row:last-child { border-bottom: 1px solid var(--border); }
.row-head { margin: 0; font: inherit; letter-spacing: normal; }
/* A row since 2026-10-14 (Allam: "more lovely and easy on the eye and catchy", keeping the list): number, the
   country's own shape from the map, the name a size up, the client count, the chevron. In hand the whole row
   surfaces 16px past its rules, the shape and the name turn cyan, and the rule under it inks in from the left. */
.row-toggle { position: relative; display: grid; grid-template-columns: 44px 56px minmax(0, 1fr) auto 40px; align-items: center; gap: 20px; width: calc(100% + 2 * var(--s-16)); margin-inline: calc(-1 * var(--s-16)); min-height: 56px; padding: var(--s-14) var(--s-16); background: none; border: 0; border-radius: 3px; text-align: left; color: var(--text-primary); cursor: pointer; transition: background-color var(--dur-1) var(--ease-state); }
.row-no { font-family: var(--mono); font-size: 13px; letter-spacing: .08em; color: var(--text-muted); }
.row-shape { display: block; width: 56px; height: 36px; overflow: visible; }
.row-shape :is(path, circle) { fill: var(--navy); opacity: .3; transition: fill var(--dur-1) var(--ease-state), opacity var(--dur-1) var(--ease-state); }
.row-name { font-family: var(--serif); font-size: 30px; line-height: 1.2; letter-spacing: -.02em; transition: color var(--dur-1) var(--ease-state); }
.row-meta { font-family: var(--mono); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); text-align: right; white-space: nowrap; }
@media (hover: hover) {
  .row-toggle:hover { background: var(--surface); }
  .row-toggle:hover .row-name, .row-toggle:hover .chev { color: var(--accent); }
  .row-toggle:hover .row-shape :is(path, circle) { fill: var(--accent); opacity: 1; }
}
.country-row.is-open .row-toggle { background: var(--surface); }
.country-row.is-open .row-name, .country-row.is-active .row-name { color: var(--accent); }
.country-row.is-open .row-shape :is(path, circle), .country-row.is-active .row-shape :is(path, circle) { fill: var(--accent); opacity: 1; }
.country-row.is-open .chev { color: var(--accent); transform: rotate(-180deg); }
.country-row { position: relative; }
.country-row::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: var(--accent); transform: scaleX(0); transform-origin: 0 50%; transition: transform var(--dur-2) var(--ease-state); pointer-events: none; z-index: 1; }
.country-row:hover::after, .country-row.is-open::after { transform: scaleX(1); }
.row-panel { overflow: hidden; }
html.js .row-panel { height: 0; visibility: hidden; transition: visibility 0s linear var(--dur-2); }
html.js .country-row.is-open .row-panel { height: auto; visibility: visible; transition-delay: 0s; }
@supports (interpolate-size: allow-keywords) {
  html.js .row-panel { interpolate-size: allow-keywords; transition: height var(--dur-2) var(--ease-state), visibility 0s linear var(--dur-2); }
  html.js .country-row.is-open .row-panel { transition: height var(--dur-2) var(--ease-state), visibility 0s; }
}
.row-panel-inner { display: grid; grid-template-columns: 120px minmax(0, 1fr) minmax(0, 1fr) 40px; gap: 0 20px; padding: var(--s-4) 0 var(--s-32); }   /* 120 = number 44 + gap 20 + shape 56: the panel's text starts under the name */
.row-col { grid-column: 2; display: flex; flex-direction: column; gap: var(--s-16); min-width: 0; }
.row-col + .row-col { grid-column: 3; }
.row-col > p { margin: 0; font-size: 17px; line-height: 1.6; color: var(--text-muted); max-width: 30em; }
.row-col .country-work { margin-top: 0; padding-top: 0; border-top: 0; }
.row-col .country-work + .country-work { margin-top: var(--s-4); padding-top: var(--s-16); border-top: 1px solid var(--border); }
.row-actions { display: flex; flex-wrap: wrap; gap: 0 var(--s-32); margin-top: auto; }
.row-actions .text-button { font-size: 14px; }
.country-notes > .eyebrow { color: var(--text-muted); padding-bottom: var(--s-12); }
@media (max-width: 767px) {
  .ledger-regions { gap: 0 var(--s-20); }
  .row-toggle { grid-template-columns: 32px 40px minmax(0, 1fr) 24px; gap: 12px; }
  .row-shape { width: 40px; height: 28px; grid-column: 2; grid-row: 1; }
  .row-name { grid-column: 3; grid-row: 1; font-size: 24px; }
  .row-meta { grid-column: 3; grid-row: 2; text-align: left; }
  .row-toggle .chev { grid-column: 4; grid-row: 1; align-self: center; margin-top: 0; }   /* the homepage ledger's phone rule puts every .chev in column 3; here that is the name's column */
  .row-panel-inner { grid-template-columns: 1fr; gap: var(--s-16); padding-bottom: var(--s-24); }
  .row-col, .row-col + .row-col { grid-column: 1; }
}

/* Contact as one conversation (2026-10-14) -----------------------------------------------
   One heading (the hero's), and under it only what its sentence promises: the three steps as
   a short list, and where, what you need and by when as fields of their own. The ways to
   reach SREO stay as they were; the capacity statement is one plain line and a link. */
.contact-grid .contact-aside .contact-lead { font-family: var(--serif); font-size: 26px; line-height: 1.3; letter-spacing: -.01em; color: var(--text-primary); max-width: 14em; }
.next-steps { margin-top: var(--s-32); max-width: 520px; }
.next-steps > .eyebrow { color: var(--accent); }
.next-steps ol { margin-top: var(--s-10); border-top: 1px solid var(--border); }
.next-steps li { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: 14px; align-items: baseline; padding: var(--s-12) 0; border-bottom: 1px solid var(--border); font-size: 16px; line-height: 1.5; color: var(--text-primary); }
.step-no { font-family: var(--serif); font-size: 22px; line-height: 1; color: var(--navy); font-variant-numeric: tabular-nums; }
.next-steps li:last-child { border-bottom: 0; }   /* the ways list below draws the next rule; two rules 40px apart read as an empty row */
.contact-way-text { font-size: 15px; color: var(--text-primary); }
/* The row of three optional fields (where, what you need, by when) lived here from 2026-10-14 to 2026-10-21, when
   Daniel Seckman chose the simpler form: name, organization, email, message. */
