/*
 * CONTENT HASH IN EVERY FONT URL. Do not remove it, and change it whenever a
 * .woff2 in ../fonts/ changes.
 *
 * Point 583bcd02, "titles that aren't italic are too bold, the correct setting
 * is Exposure 20", is a re-report of a defect we already fixed. Our CSS was and
 * is correct: font-weight 400 with "EXPO" 20. The client was looking at a
 * CACHED FONT FILE.
 *
 * Until 2026-09-02 exposure-roman-latin.woff2 was a STATIC instance with the
 * fvar table stripped and the axis baked at 0. The variable cut replaced it at
 * the SAME URL, these files carried no version query, and Vercel serves
 * /wp-content/themes/.../fonts/*.woff2 with
 * "cache-control: public, max-age=31536000, immutable". So any browser that
 * opened the preview before that export keeps the axis-free file for a year,
 * font-variation-settings does nothing, and every roman headline renders at
 * EXPO 0. Measured at 80px on "Our expertise", alpha weighted ink: the static
 * cut gives 30784.6 at both EXPO 0 and EXPO 20 (it cannot move), the live
 * variable cut gives 26413.5 at EXPO 20. That is 16.6 percent more ink, which
 * is exactly "too bold" on a build whose CSS is already right.
 *
 * main.css itself is cache busted by filemtime in functions.php, so shipping
 * the hash here is enough to retire every stale font in one reload.
 */
@font-face { font-family: "Geist Local"; src: url("../fonts/geist-latin-var.woff2?h=be0ba286") format("woff2"); font-style: normal; font-weight: 100 900; font-display: swap; } /* impeccable-disable-line overused-font: client-approved Figma brand typeface */
/*
 * Exposure is a variable typeface with a single axis, EXPO, running -100 to 100.
 * It is an exposure axis, not a weight axis: the widths and the vertical metrics
 * never change, only how much ink each stroke carries. Negative is underexposed
 * and heavy, positive is overexposed and light.
 *
 * Point 2c39ebac: "All headline fonts in Exposure are too bold, the setting is:
 * Exposure 20". The two subsets behind these file names used to be static
 * instances with the fvar table stripped and the axis baked at 0, so no CSS
 * could have moved the weight. They are now the same French and English
 * subsets recut from the licensed variable source with the axis kept live and
 * clamped to 0..40 around a 20 default, which holds them near the old file size
 * while making the setting real and editable. The names did not change, so
 * editor.css picks the new cut up too and the block editor matches the front
 * end. The previous static pair is in _archive/2026-09-02-exposure-static-expo0
 * with its restore commands. The axis value itself is applied once, on the html
 * element, further down.
 */
@font-face { font-family: "Exposure Local"; src: url("../fonts/exposure-roman-latin.woff2?h=6a92751a") format("woff2"); font-style: normal; font-weight: 400; font-display: swap; }
@font-face { font-family: "Exposure Local"; src: url("../fonts/exposure-italic-latin.woff2?h=964c8b80") format("woff2"); font-style: italic; font-weight: 400; font-display: swap; }

:root {
  /*
   * Header height, point tracked three rounds running. Figma Header/Light is
   * 95px tall on every frame drawn, the newest two included (56:1892, 56:1746,
   * 56:1275): the logo is 130 by 31 sitting 32px down from the top, so
   * 32 + 31 + 32 = 95. Live was 82 everywhere, a 13px drift. Everything that
   * has to clear the fixed header reads this one token, so moving it moves the
   * header, the content offset under it and the mobile menu padding together.
   */
  --site-header-height: 95px;
  --site-header-gutter: clamp(54px, 3.75vw, 72px);
  --site-logo-width: 130px;
  --color-ink: #080c0a;
  --color-forest: #253432;
  --color-cream: #fffaf3;
  --color-blue: #aabbff;
  --color-blue-light: #d4daf9;
  --color-yellow: #ffcd5c;
  --radius-media: 10px;
  --page-gutter: max(55px, calc((100vw - 1332px) / 2));
  --ease-fluid: cubic-bezier(.16, 1, .3, 1);
  /*
   * Exposure axis value. Yael's setting, points 2c39ebac and 583bcd02. Change it
   * here only. It stays at 20 on purpose: solving Figma's own rendered ink back
   * to an axis value puts the homepage H1 (56:510) near EXPO -3 and the H2
   * (56:576) near EXPO -5, so 20 already renders LIGHTER than the comp. Nothing
   * in the design asks us to go lighter again; the "too bold" report was a stale
   * cached font, handled at the @font-face rules above.
   */
  --exposure-axis: 20;
  /*
   * Geist emphasis weight, point d1d1ced2 "ALMOST EVERYWHERE Geist is a little
   * bit too bold, should be regular instead of medium". This is a design change,
   * not a build defect: the node he pinned (About 56:276) is Geist Medium 500 in
   * Figma and we render 500. Every Geist rule that used a hardcoded 500 for
   * emphasis reads this token instead, so the whole site moves from one place
   * and can move back if he changes his mind on seeing it.
   */
  --weight-emphasis: 400;
  --weight-strong: 600;
  /*
   * Footer column headings. Figma 56:749 is Geist SemiBold 600; a bare <strong>
   * inherits the user agent's 700, which is the one place in the footer that is
   * measurably bolder than the comp rather than merely a taste call.
   */
  --weight-footer-heading: 600;
  /* One pill padding, point 54d70977. Change it here only. */
  --tag-pill-padding: 24px;
}

* { box-sizing: border-box; }
/*
 * font-variation-settings inherits, so setting the Exposure axis once on the
 * root reaches every heading, every em, the mobile menu and the footer tagline
 * without listing them. Geist carries a wght axis and no EXPO axis, so it
 * simply ignores this and keeps taking its weight from font-weight.
 */
html { scroll-behavior: smooth; background: var(--color-forest); overscroll-behavior-y: none; font-variation-settings: "EXPO" var(--exposure-axis); }
html.is-at-page-end { background: var(--color-blue); }
body { margin: 0; background: var(--color-cream); color: var(--color-ink); font-family: "Geist Local", Geist, Inter, Arial, sans-serif; font-size: 16px; line-height: 1.35; } /* impeccable-disable-line overused-font: client-approved Figma brand typeface */
body.menu-open { overflow: hidden; }
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; }
h1, h2, h3, p { margin-top: 0; }
/*
 * Point 05aca2fc, "some headlines look too tight". Every Exposure text node on
 * Figma page 56:232 carries letterSpacing 0, with no exceptions across Homepage
 * 56:503, About 56:234, Insights 56:1264 and the new blog frame 56:1882, and
 * the standard heading line height is 104 percent. This base rule was setting
 * -0.035em and a 0.94 line, and most pages then overrode the line height back
 * to 1.04 while inheriting the tracking, which is why the site was internally
 * inconsistent: the homepage headings already computed letter-spacing normal
 * and the Archives, Contact, Insights, Our Partners, Publications and Services
 * H1s did not. Nothing measured on the live build computes a 0.94 line, so the
 * line-height here is corrected to the Figma 1.04 as the safe default for
 * anything that lands on it later rather than to change a rendered page.
 */
h1, h2 { font-family: "Exposure Local", Exposure, "Iowan Old Style", "Times New Roman", serif; font-weight: 400; letter-spacing: normal; line-height: 1.04; }
/*
 * Orphan control on every heading, in both languages.
 *
 * French runs 15 to 25 percent longer than English, so the same heading that
 * sits on two clean lines in English strands a two letter word on a line of
 * its own in French: "us", "on", "en", "ou", "la". A QA sweep of every route
 * at 1440, 1280, 1024, 768 and 390 found it on the homepage, Insights,
 * Publications, About and Our partners.
 *
 * "pretty" is deliberate and "balance" is deliberately NOT used here. balance
 * re-flows every line to even out their lengths, which would move the line
 * breaks this design was measured against in Figma. pretty only looks at the
 * last line and pulls a word down to keep it company, so a heading that
 * already breaks the way the design intends keeps breaking that way. Browsers
 * without support simply ignore it and nothing shifts.
 */
h1, h2, h3, h4, .section-heading h2, .button-label { text-wrap: pretty; }
em { font-style: italic; }
/*
 * Geist regular, not medium. Client pin d1d1ced2: "ALMOST EVERYWHERE Geist is
 * a little bit too bold. Should be regular instead of medium." Every hardcoded
 * 500 in the fidelity sheets now reads --weight-emphasis, so this is the one
 * place it changes. Worth saying out loud: Figma node 56:276 really is Geist
 * Medium 500, so this is a deliberate divergence from the comp on his request.
 *
 * A bare <strong> was inheriting the browser's 700, which is not a taste call:
 * the homepage frame uses Geist at 400, 500 and 600 and never 700, so the
 * testimonial attributions and the citation author names were heavier than
 * anything the design contains.
 */
strong, b { font-weight: var(--weight-strong); }
.screen-reader-text { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.skip-link { position: fixed; top: -90px; left: 8px; z-index: 100; background: #ffcd5c; padding: 10px 16px; border-radius: 999px; }
.skip-link:focus { top: -90px; }
.using-keyboard .skip-link:focus { top: 8px; }
:focus-visible { outline: 3px solid #ffcd5c; outline-offset: 4px; }

/*
 * The header is three columns, 1fr auto 1fr, and the third one cannot shrink
 * below the call to action plus the language link. Below roughly 1240 the free
 * space runs out, so the nav column was landing flush against that third
 * column: in French the pill's rounded edge sat on the last glyph of "Nos
 * partenaires" with a measured 0.0px gap from 1101 to 1240. The 28px column gap
 * is the same distance the call to action already keeps from the language link.
 * Where the two 1fr columns are equal, which is every width from 1280 up, the
 * gap comes out of both sides equally and the nav stays exactly where it was.
 * The compact header below 1100 is a different three column layout, so it puts
 * the gap back to 0 and nothing about the mobile header moves.
 */
.site-header { position: fixed; inset: 0 0 auto; z-index: 20; width: 100%; height: var(--site-header-height); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; column-gap: 28px; padding: 0 var(--site-header-gutter); color: #fffaf3; background: transparent; animation: site-nav-enter 520ms 120ms var(--ease-fluid) backwards; }
.site-header::before { content: ""; position: absolute; inset: 0; z-index: -1; background: #253432; opacity: 0; transition: opacity 240ms cubic-bezier(.16,1,.3,1); pointer-events: none; }
.ebiom-site:not(.ebiom-home):not(.ebiom-figma-page):not(.ebiom-content-page) .site-header::before,
.site-header.is-scrolled::before { opacity: 1; }
.admin-bar .site-header { top: 32px; }
.brand { grid-column: 1; justify-self: start; }
.brand img { width: var(--site-logo-width); height: auto; }
.primary-nav { grid-column: 2; display: flex; align-items: center; justify-content: center; gap: 22px; font-size: 16px; line-height: 1; }
.primary-nav-list { display: flex; align-items: center; justify-content: center; gap: 22px; margin: 0; padding: 0; list-style: none; }
.primary-nav a { flex: 0 0 auto; text-align: center; }
.primary-nav a, .language-link { position: relative; }
.primary-nav a::after, .language-link::after { content: ""; position: absolute; left: 0; right: 100%; bottom: -5px; height: 1px; background: currentColor; transition: right 300ms ease; }
.primary-nav a:hover::after, .primary-nav a:focus-visible::after, .language-link:hover::after { right: 0; }
/*
 * The underline stays put on the page you are actually on. Yael, WhatsApp
 * 3 September: "surprisingly I'm always wonder which page I am on x') and that
 * indicator could help greatly ... you have underline on hover, but can it stay
 * once we're on the page".
 *
 * It reuses the hover underline rather than drawing a second indicator, so the
 * two can never disagree, and it is the same 1px rule in currentColor over the
 * photograph or the forest bar alike. aria-current is included so the state is
 * announced and not only drawn; ebiom_nav_mark_insight_ancestor() adds these
 * classes on single articles, where WordPress marks nothing by itself.
 */
.primary-nav .current-menu-item > a::after,
.primary-nav .current_page_item > a::after,
.primary-nav .current-menu-ancestor > a::after,
.primary-nav .current-menu-parent > a::after,
.primary-nav a[aria-current="page"]::after { right: 0; }
.header-actions { grid-column: 3; width: max-content; display: flex; align-items: center; justify-content: flex-end; gap: 28px; justify-self: end; }
/*
 * Language switch, point 48b45aa3. The chevron drawn in Figma (56:1774) always
 * promised a menu, and the control underneath it was a bare anchor that jumped
 * straight to the other language. It is a disclosure button and a two item list
 * now, and every measurement of the closed control is unchanged: the button
 * keeps the .language-link rule, so it is still 43.418 by 16 with the 25px
 * label box and the 7.724px gap Figma draws, and the wrapper is the same size
 * as the button, so the header grid does not move at any width.
 *
 * The open panel borrows the header's own vocabulary rather than inventing a
 * component: forest ground like the mobile drawer, cream type at the same 16px
 * Geist, and the current language marked with the same 1px currentColor
 * underline the navigation uses for the current page. It is offset left by its
 * own padding and hairline so the two codes sit exactly under the label above
 * them.
 */
.language-link { display: inline-flex; align-items: center; gap: 7.724px; width: 43.418px; font-size: 16px; line-height: 1; }
.language-label { flex: 0 0 25px; width: 25px; text-align: center; }
.language-arrow { position: relative; display: block; width: 10.694px; height: 6.483px; }
.language-arrow img { position: absolute; top: 50%; left: 50%; width: 6.483px; height: 10.694px; max-width: none; transform: translate(-50%, -50%) rotate(-90deg); transition: transform 260ms var(--ease-fluid); }
.language-switch { position: relative; display: inline-flex; }
button.language-link { margin: 0; padding: 0; border: 0; background: transparent; color: inherit; text-align: left; cursor: pointer; }
.language-switch.is-open .language-link::after { right: 0; }
.language-switch.is-open .language-arrow img { transform: translate(-50%, -50%) rotate(90deg); }
.language-switch-menu { position: absolute; top: calc(100% + 18px); left: -9px; z-index: 5; min-width: calc(100% + 18px); margin: 0; padding: 10px 8px; list-style: none; border: 1px solid rgba(255, 250, 243, .18); border-radius: 8px; background: var(--color-forest); color: var(--color-cream); box-shadow: 0 18px 40px rgba(8, 12, 10, .28); font-size: 16px; line-height: 1; opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(-6px); transition: opacity 240ms var(--ease-fluid), transform 240ms var(--ease-fluid), visibility 0s linear 240ms; } /* impeccable-disable-line layout-transition: the panel is out of flow, nothing around it reflows */
/*
 * visibility is what stops a closed panel taking clicks and keyboard focus, and
 * it has to flip the instant the panel opens: transitioned as a duration it
 * flips halfway through instead, and ArrowDown then focuses nothing because the
 * option it aims at is still hidden. So it opens with no delay and closes on a
 * delay equal to the fade.
 */
.language-switch.is-open .language-switch-menu { opacity: 1; visibility: visible; pointer-events: auto; transform: none; transition: opacity 240ms var(--ease-fluid), transform 240ms var(--ease-fluid), visibility 0s; }
/*
 * Without JavaScript the button cannot open anything, and a bilingual site that
 * loses its second language is worse than one with no animation. Until the
 * script marks the control as enhanced, focus alone opens the panel, so a click
 * or a Tab still reaches both languages. The rule stops applying the moment
 * main.js takes over, so it can never fight the toggle.
 */
.language-switch:not(.is-enhanced):focus-within .language-switch-menu { opacity: 1; visibility: visible; pointer-events: auto; transform: none; transition: opacity 240ms var(--ease-fluid), transform 240ms var(--ease-fluid), visibility 0s; }
.language-option { position: relative; display: flex; align-items: center; padding: 6px 0; }
.language-option-code { position: relative; flex: 0 0 25px; width: 25px; text-align: center; }
.language-option-code::after { content: ""; position: absolute; left: 0; right: 100%; bottom: -3px; height: 1px; background: currentColor; transition: right 300ms ease; }
.language-option:hover .language-option-code::after,
.language-option:focus-visible .language-option-code::after,
.language-option[aria-current="true"] .language-option-code::after { right: 0; }
.nav-toggle { display: none; border: 0; background: transparent; padding: 10px; }
.nav-toggle span:not(.screen-reader-text) { display: block; width: 24px; height: 2px; margin: 5px 0; background: currentColor; }
/*
 * Points 6a24439b and 9eb2c209 asked for one button. The pill still came in two
 * sizes: 18px type in a 64px pill on the homepage, 19px type in a 69px pill on
 * every other page, because only .ebiom-figma-page and the two inner call to
 * action blocks ever raised it. Figma draws the button label at 19px/21.28 Geist
 * on every frame, so the larger pair is the correct one and it belongs on the
 * base component rather than on the pages that happen to override it.
 */
.button { --button-fill: var(--color-forest); --button-orb-size: 69px; --button-orb-width: var(--button-orb-size); --button-split-gap: 7px; --button-body-padding: 52px; position: relative; isolation: isolate; display: inline-flex; width: 242px; min-height: 69px; align-items: center; overflow: visible; border-radius: 999px; padding: 0; background: var(--button-fill); font-size: 19px; font-weight: 400; line-height: 1.12; transition: color 360ms var(--ease-fluid); }
.button-label,
.button-arrow { position: absolute; z-index: 1; top: 50%; transform: translateY(-50%); transition: left 360ms var(--ease-fluid); }
.button-label { left: calc(var(--button-body-padding) * .5); white-space: nowrap; }
.button-arrow { left: calc(100% - var(--button-orb-width) * .5 - 9px); width: 18px; height: 15px; object-fit: fill; }
.button-liquid { position: absolute; inset: 0; z-index: -1; overflow: visible; pointer-events: none; }
.button-liquid::before,
.button-liquid::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; border-radius: 999px; background: var(--button-fill); transition: width 360ms var(--ease-fluid), left 360ms var(--ease-fluid); } /* impeccable-disable-line layout-transition: isolated pseudo-element preserves exact approved pill geometry */
.button-liquid::before { width: 100%; }
.button-liquid::after { left: calc(100% - var(--button-orb-width)); width: var(--button-orb-width); }
.button-liquid-neck { position: absolute; top: 50%; left: calc(100% - var(--button-orb-width) - var(--button-split-gap)); z-index: 1; width: calc(var(--button-orb-width) + var(--button-split-gap)); height: 16px; border-radius: 999px; background: var(--button-fill); opacity: 0; transform: translateY(-50%) scaleX(.12) scaleY(.08); transform-origin: left center; }
.button-liquid-ready { background: transparent; }
/*
 * Points 6a24439b, 707de6dc and 9eb2c209: one button animation everywhere, and
 * the arrow must not move.
 *
 * The About page "Work with us" button is the one Yael calls correct. On hover
 * its pill body shrinks back from the round orb, the orb holds its position and
 * the arrow inside it sits perfectly still. It only behaved that way by
 * accident. main.js measures the label, writes the result into
 * --button-body-hover, then sizes the pill to max(authored width, label +
 * padding + orb + gap). Where the label is long those two are equal, the orb has
 * no slack to slide into, and nothing translates. Where the label is short the
 * authored width wins and the orb slides left by the slack, dragging the arrow
 * with it: measured 25px on the home hero button and 7px on the home closing
 * call to action, 0 on the About button and on the header call to action.
 *
 * The three rules below take the hover geometry off the measured label and put
 * it back on the pill itself. The split is now always exactly orb + gap, so the
 * orb and the arrow land on their rest position on every button, in both
 * languages, whatever the label says. Nothing here touches --button-fill, so
 * each button keeps its own colour and the header call to action stays yellow.
 *
 * Since those three rules landed, --button-body-hover is written by main.js on
 * every button and read by nothing at all. Its fallback declaration has been
 * taken off .button above. The two writes in assets/js/main.js lines 151 and
 * 162 are the remaining dead code, in a file this lane does not own.
 */
.button:hover .button-liquid::before,
.button:focus-visible .button-liquid::before { width: calc(100% - var(--button-orb-width) - var(--button-split-gap)); }
.button:hover .button-liquid::after,
.button:focus-visible .button-liquid::after { left: calc(100% - var(--button-orb-width)); }
.button:hover .button-arrow,
.button:focus-visible .button-arrow { left: calc(100% - var(--button-orb-width) * .5 - 9px); }
.button-yellow { --button-fill: var(--color-yellow); color: var(--color-ink); }
.button-blue { --button-fill: var(--color-blue); color: var(--color-ink); }
.button-dark { --button-fill: var(--color-forest); color: var(--color-cream); }
/*
 * Point 7c787af5: on the header call to action the detached arrow must sit in a
 * circle, like every other button. It did not. .header-cta was the only rule on
 * the site that broke --button-orb-width away from --button-orb-size: the orb
 * kept the 69px width of the full size button while the header pill is 51px
 * tall, and .button-liquid::after is top:0;bottom:0, so the orb rendered 69 by
 * 51. Measured hovered on staging, every other button on the homepage renders
 * 69 by 69 and only this one rendered 69 by 51, which is exactly the pill next
 * to a pill in the client's screenshot.
 *
 * Dropping the override lets the base rule resolve --button-orb-width to
 * --button-orb-size, so the orb is 51 by 51 at the header's own height. Nothing
 * else is touched: --button-fill still comes from .button-yellow, the label is
 * untouched, and the rest and hover positions of .button-arrow are both
 * calc(100% - var(--button-orb-width) * .5 - 9px), so the arrow still does not
 * move on hover. main.js sizes the pill to label + padding + orb + gap, so the
 * body pill keeps its exact width and the whole component gets 18px narrower,
 * which the width constrained header and the longer French label both want.
 */
.header-cta { --button-orb-size: 51px; --button-split-gap: 8px; --button-body-padding: 54px; width: 208px; min-width: 208px; min-height: 51px; height: 51px; font-size: 18px; font-weight: 400; }
.primary-nav-cta { display: none; }
.button-hero { --button-orb-size: 69px; --button-body-padding: 68px; width: 261px; min-height: 69px; }

.home-hero { position: relative; min-height: 913px; display: flex; align-items: flex-end; padding: 130px 55px 150px; background-position: center 48%; background-size: cover; color: #fffaf3; }
.home-hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 48%; }
.home-hero-shade { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(8,12,10,.72) 0%, rgba(8,12,10,.27) 49%, rgba(8,12,10,.08) 100%); }
.home-hero-copy { position: relative; max-width: 920px; }
.home-hero h1 { max-width: 890px; margin-bottom: 38px; font-size: clamp(66px, 7vw, 104px); }
.home-hero p { max-width: 650px; margin-bottom: 45px; font-size: 17px; }

.innovation-panel { display: grid; grid-template-columns: .92fr 1.08fr; gap: 58px; min-height: 1090px; padding: 70px 55px; background: #aabbff; }
.innovation-image img { width: 100%; height: 950px; object-fit: cover; border-radius: 10px; }
.innovation-copy { padding-top: 58px; }
.innovation-copy h2 { max-width: 640px; margin-bottom: 36px; font-size: clamp(55px, 5.2vw, 82px); }
.innovation-copy > p { max-width: 630px; margin-bottom: 49px; font-size: 17px; }
.service-accordion { margin-bottom: 42px; border-top: 1px solid rgba(8,12,10,.5); }
.service-row { width: 100%; min-height: 75px; display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 0; border: 0; border-bottom: 1px solid rgba(8,12,10,.5); background: transparent; color: #080c0a; text-align: left; cursor: pointer; transition: background-color 300ms ease; }
.service-row span { transition: transform 300ms ease; }
.service-row:hover, .service-row[aria-expanded="true"] { background: rgba(255,250,243,.35); }
.service-row:hover span:first-child, .service-row[aria-expanded="true"] span:first-child { transform: translateX(16px); }
.service-row:hover span:last-child, .service-row[aria-expanded="true"] span:last-child { transform: translateX(-16px); }
.small-copy { max-width: 620px; font-size: 14px !important; }

.section-pad { padding: 104px 55px; }
.section-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 30px; margin-bottom: 70px; }
.section-heading h2 { margin: 0; font-size: clamp(58px, 5.5vw, 86px); }
.section-heading .button { flex: 0 0 auto; }
.expertise-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.expertise-card { position: relative; min-height: 637px; overflow: hidden; border-radius: 10px; background-position: center; background-size: cover; color: #fffaf3; }
.expertise-card::before { content: ""; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(8,12,10,.79), rgba(8,12,10,0) 58%); transition: background-color 300ms ease; }
.expertise-overlay { position: absolute; inset: auto 0 0; min-height: 310px; display: grid; grid-template-columns: 1fr auto; align-items: start; gap: 20px; padding: 28px; transform: translateY(120px); transition: transform 300ms ease, background-color 300ms ease; }
.expertise-overlay h3 { max-width: 300px; margin: 0; font-size: 34px; font-weight: var(--weight-emphasis); line-height: 1; }
.expertise-overlay > span { width: 46px; height: 46px; display: grid; place-items: center; border-radius: 50%; background: #aabbff; color: #080c0a; font-size: 20px; }
.expertise-overlay p { grid-column: 1 / -1; margin: 0; opacity: 0; visibility: hidden; transform: translateY(10px); transition: opacity 300ms ease, transform 300ms ease, visibility 300ms ease; }
.expertise-card:hover .expertise-overlay, .expertise-card:focus-visible .expertise-overlay { transform: translateY(0); background: #253432; }
.expertise-card:hover .expertise-overlay p, .expertise-card:focus-visible .expertise-overlay p { opacity: 1; visibility: visible; transform: translateY(0); }
.expertise-card:hover { background-size: 108%; }

.partners-section { padding-top: 65px; border-top: 1px solid #d8d4ce; }
.partner-row { display: grid; grid-template-columns: repeat(6, 1fr); align-items: center; gap: 54px; }
.partner-row img { width: 100%; max-height: 74px; object-fit: contain; filter: saturate(.95); }

.driving-section { position: relative; min-height: 650px; display: flex; align-items: center; padding: 90px 55px; background-position: center; background-size: cover; color: #fffaf3; }
.driving-section::before { content: ""; position: absolute; inset: 0; background: rgba(8,12,10,.24); }
.driving-grid { position: relative; z-index: 2; width: 100%; display: grid; grid-template-columns: 1fr 1.08fr; gap: 90px; }
/* z-index 2 is load bearing. The band's ::before scrim is z-index 1, and a
   positioned pseudo element with a positive z-index paints above every
   z-index:auto positioned descendant, so the forest wash was landing on top of
   the heading, the paragraphs and the button fill rather than behind them. On
   the Publications, Partners and Archives bands that rendered declared cream
   #fffaf3 as grey #7f8682, a measured 1.59:1. The homepage never showed it
   because home-fidelity.css hides that ::before entirely. */
.driving-grid h2 { margin-bottom: 48px; font-size: clamp(58px, 5.5vw, 86px); }
.driving-grid p { max-width: 580px; margin: 0 0 28px; font-size: 17px; }

.insights-section { padding-top: 110px; }
.insights-layout { display: grid; grid-template-columns: 1.15fr .85fr; gap: 33px; }
.featured-insight img { width: 100%; aspect-ratio: 1.52; object-fit: cover; border-radius: 9px; }
.tag { display: inline-flex; min-height: 26px; align-items: center; justify-content: center; margin-top: 18px; border-radius: 999px; padding: 0 var(--tag-pill-padding); background: #d4daf9; font-size: 11px; }
/*
 * Point 54d70977: "Keep same spacing from edge to text in all button tags".
 *
 * The pills used to be drawn two different ways. The insights pills were padded,
 * which gave them a steady 24px on each side. The homepage pills were given the
 * Figma frame widths instead (128, 157, 177 and 202) with zero padding and the
 * label centred, so the gap landed wherever the label happened to end: measured
 * at 6, 9.5, 19, 19.5 and 43px on the English homepage and 7.5 to 44.5px on the
 * French one, on pills that carry the same style. The Figma frames are fixed
 * width for the same reason, so they drift too.
 *
 * There is now one token, --tag-pill-padding, and the rule below is deliberately
 * stronger than the page fidelity sheets that load after this file, because a
 * single token that any page can quietly override is not a token. Width is
 * released back to the content so the padding is what actually sets the pill
 * size. Colour, height, font size and margins stay page owned.
 */
.tag,
.insight-type-filters a { padding-left: var(--tag-pill-padding) !important; padding-right: var(--tag-pill-padding) !important; width: auto !important; min-width: 0 !important; }
.featured-insight h3 { margin: 18px 0 12px; font-size: 27px; }
.featured-insight p { max-width: 690px; }
.insight-list { display: grid; align-content: start; gap: 15px; }
.insight-row { display: grid; grid-template-columns: 162px 1fr; gap: 24px; align-items: start; }
.insight-row img { width: 162px; height: 122px; object-fit: cover; border-radius: 8px; }
.insight-row h3 { margin: 4px 0 6px; font-size: 19px; font-weight: var(--weight-emphasis); }
.insight-row .tag { margin-top: 5px; }

.testimonial-section { display: grid; grid-template-columns: .58fr .95fr 1.9fr; align-items: stretch; margin: 20px 55px 105px; background: #ffcd5c; }
.testimonial-section > h2 { padding: 50px 0 45px 42px; font-size: 56px; }
.testimonial-person { min-height: 410px; display: grid; grid-template-columns: .7fr 1.3fr; align-items: end; gap: 14px; padding-top: 26px; }
.testimonial-person > div { padding: 0 0 33px 10px; }
.testimonial-person strong, .testimonial-person span { display: block; font-size: 11px; }
.testimonial-person img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 15%; }
.testimonial-section blockquote { display: flex; align-items: center; margin: 0; padding: 45px; font-size: 27px; line-height: 1.15; }

.image-cta { min-height: 660px; display: flex; flex-direction: column; align-items: center; justify-content: center; background-position: center; background-size: cover; color: #fffaf3; text-align: center; }
.image-cta h2 { margin-bottom: 48px; font-size: clamp(61px, 6.2vw, 96px); }

.site-footer { position: relative; min-height: 414px; padding: 80px var(--page-gutter) 32px; background: var(--color-blue); color: var(--color-ink); }
/*
 * Footer column tracks, read off Figma footer 56:721 rather than tuned by eye.
 * Its container 56:722 is 1328 wide at a 1440 frame and holds four columns at
 * x 975 / 1728 / 1879 / 2108, so 694, 92, 170 and 195 wide with a 59px gap
 * three times over.
 *
 * This matters for point 97c05fcc and not only for tidiness. The tagline node
 * 56:732 is a 694 wide text frame that sets one line, 493.4px of ink at
 * letterSpacing 0. Our identity column computed 479.6px at 1440, which is
 * narrower than the line Figma draws, and the only reason the sentence used to
 * fit on one line here was the -0.025em of tracking that the same pin asks us
 * to remove. Take the tracking out and leave the track at 479.6 and the tagline
 * wraps to two lines, which is a worse answer than the one he complained about.
 *
 * The tracks are those four numbers as fr shares, so at 1440 they resolve to
 * 695.2 / 92.2 / 170.3 / 195.3 and at 1512, 1728 and 1920 to 696.4 / 92.3 /
 * 170.6 / 195.7, where the page gutter has capped the container at 1332. In
 * French the second column reads 94 because "En savoir plus" is wider than
 * "Find out more".
 *
 * The max-content floor on the last three is what stops this being a trade of
 * one defect for another. Pure fr shares shrink every column together, and
 * between 1101 and 1332, the band this build has been bitten in before, that
 * pulled the address column under the width of "Parc Scientifique Créalys" and
 * the registration column under "IBAN BE83 7320 6527 7415", so both broke mid
 * line. With the floor those three hold at 90/160/180 in English and 94/164/180
 * in French all the way down and the first column absorbs the shrink instead.
 * Under 980 the media query swaps in the three column layout with the identity
 * block full width, so nothing about the narrow footer moves.
 */
.footer-grid { display: grid; grid-template-columns: minmax(360px, 694fr) minmax(max-content, 92fr) minmax(max-content, 170fr) minmax(max-content, 195fr); gap: clamp(36px, 4.1vw, 59px); min-height: 174px; }
.footer-brand img { width: 364px; max-width: none; height: 85px; }
.footer-grid strong { display: block; margin-bottom: 16px; font-size: 14px; font-weight: var(--weight-footer-heading); }
.footer-grid p { margin: 0; font-size: 14px; line-height: 22.75px; }
/*
 * Footer identity column, measured off the Figma footer frame 56:721 rather
 * than guessed. In the frame the logo is 364 by 85 at y 15924, the tagline is
 * Exposure VAR 35 on a 39.90439 line at y 16041, and the social row of three
 * 26px icons starts at y 16113. That is 32px of air under the logo and another
 * 32px under the tagline.
 *
 * Points ff8f5ed9 and 3b0eab61. The tagline is a <p> inside .footer-grid, so the
 * .footer-grid p rule above was winning on specificity and pinning it to 14px
 * on a 22.75 line with no top margin at all. That is why the sentence sat flush
 * against the logo and why it rendered at 14px where the frame says 35. Both
 * rules below carry .footer-grid so the tagline wins its own declarations back.
 */
/*
 * Point 97c05fcc, "the sentence below the logo is a bit too big and bold". The
 * size is right and stays: Figma 56:732 and 56:1857 are both Exposure VAR 35 on
 * a 39.904 line, and rendered side by side at one scale the Figma tagline is
 * 493.5px of ink against our 470.0px, so ours was already the narrower of the
 * two. What was wrong is the tracking. Figma sets letterSpacing 0 and this rule
 * carried -0.025em, which is also what pulled the line in under the logo. The
 * "too bold" half of the pin is the stale cached font, handled at @font-face.
 *
 * The size is a clamp rather than a flat 35px only so the sentence never breaks
 * where the design shows one line. It reaches the Figma 35px by about 796 wide
 * and holds it through 1440, 1512, 1728 and 1920, so every width the comp
 * actually covers gets the comp value; below that the column is the full page
 * width and 4.4vw is what keeps the longer French string, 662px of ink against
 * a 658px column at 768, on a single line. The line height is the Figma ratio,
 * 39.90439 over 35, which still computes 39.9px at 35.
 */
.footer-grid .footer-tagline { margin: 32px 0 0; font-family: "Exposure Local", Exposure, "Iowan Old Style", "Times New Roman", serif; font-size: clamp(28px, 4.4vw, 35px); line-height: 1.14; letter-spacing: normal; }
.footer-identity .social-links { margin-top: 32px; }
/*
 * Points 97c05fcc and 7ccd1982. The marks are the OUTLINE set Figma actually
 * draws in its footers, nodes 56:734 / 56:740 / 56:745 inside row 56:733, on a
 * 26x26 frame. The 27px box the solid tiles used is gone with them, so every
 * mark, anchor or pending span, is 26 like the comp. The previous solid pair is
 * in _archive/2026-09-07-social-icons-solid-back-to-outline with restore lines.
 */
.social-links { display: flex; gap: 12px; margin-top: 30px; }
.social-links a { width: 26px; height: 26px; display: grid; place-items: center; }
/* The Instagram mark ships before the account is linked. Yael, WhatsApp 3
   September: "can you add insta logo even if not connected yet in footer too",
   and on Contact "instagram (even without link to page)". It renders as a plain
   span rather than an anchor, so it is not focusable, carries no href leading
   nowhere, and is hidden from assistive tech until global.instagram_url is set,
   at which point the anchor branch takes over and it behaves like the others. */
.social-links .social-link-pending { width: 26px; height: 26px; display: grid; place-items: center; }
.social-links .social-link-pending img { width: 100%; height: 100%; }
.social-links img { width: 100%; height: 100%; }
/* Figma 56:751 sets the footer link and address text at 14px, which is what
   .footer-grid p already uses for the address column. These links were the one
   13px hold out, so the two columns did not line up on size. */
.footer-discover a { display: block; width: max-content; margin: 0 0 9px; font-size: 14px; }
.footer-discover a:hover, .footer-discover a:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.footer-base { display: flex; justify-content: space-between; gap: 20px; margin-top: 80px; padding-top: 33px; border-top: 1px solid rgba(8,12,10,.25); font-size: 10px; line-height: 15px; }

.image-refraction-canvas { position: fixed; inset: 0; z-index: 4; width: 100vw; height: 100vh; pointer-events: none; contain: strict; }
.inner-hero, .inner-cta, .image-cta { background-color: var(--color-forest); }

@keyframes site-nav-enter { from { opacity: .72; transform: translateY(-12px); } to { opacity: 1; transform: none; } }

/*
 * The header had a dead band. The desktop call to action was hidden from 1100px
 * down, but the hamburger only appeared from 980px down, so between 981 and
 * 1100 the header showed neither: no menu button, no yellow "Work with us", and
 * therefore no route to Contact from the persistent header at all. Measured on
 * / and /about-us/ in both languages: at 1024 and at 1100 both .nav-toggle and
 * .header-cta computed to display:none.
 *
 * The header now switches to its compact mode on the same 1100px line the call
 * to action leaves on, so the two boundaries meet and the band closes. Only the
 * header moves: the page layout breakpoints below stay at 980 and 620 exactly
 * where they were, so nothing else about 1024 changes.
 *
 * Putting the call to action back instead was measured and does not fit: at
 * 1100 the French header needs 130 logo + 408 nav + 305 pill + 43 language plus
 * gutters, which is wider than the viewport.
 */
@media (max-width: 1100px) {
  .header-actions { width: auto; }
  .header-cta { display: none; }
  .site-header { grid-template-columns: 1fr auto auto; column-gap: 0; }
  .nav-toggle { position: relative; z-index: 40; display: block; color: inherit; }
  .primary-nav { position: fixed; inset: 0; z-index: -1; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 20px; padding: calc(var(--site-header-height) + 28px) 28px 28px; background: #253432; color: #fffaf3; font-family: "Exposure Local", Exposure, "Times New Roman", serif; font-size: 32px; opacity: 0; visibility: hidden; overflow-y: auto; transition: opacity 300ms ease, visibility 300ms ease; }
  .primary-nav-list { flex-direction: column; align-items: flex-start; gap: 20px; }
  .primary-nav.is-open { z-index: 30; opacity: 1; visibility: visible; }
  .primary-nav.is-open ~ .primary-nav-cta { display: inline-flex; position: fixed; left: 28px; bottom: 36px; z-index: 35; width: 264px !important; min-width: 264px; }
  /*
   * The drawer's own call to action is fixed to the bottom of the viewport, and
   * the list above it is vertically centred. On a landscape phone, 844 by 390,
   * that put the last navigation item underneath the button: "Our Partners"
   * measured 302 to 340 against a button at 303 to 354, so a tap on the last
   * item hit the button and went to Contact instead. Reserving the button's
   * height plus its offset inside the drawer's own padding moves the centred
   * list up rather than overlapping it, and the drawer already scrolls if the
   * remaining height is too small.
   */
  .primary-nav.is-open { padding-bottom: 122px; }

  /*
   * The panel is fixed over the whole viewport at z-index 30, above the z-index
   * 20 header, so with the menu open a hit test on the logo and on the FR/EN
   * link both returned the panel. On a bilingual site the language switch being
   * unreachable exactly when the menu is open is the worst place to lose it, so
   * both are lifted above the panel while it is open, the same way the burger
   * already is.
   */
  body.menu-open .brand,
  body.menu-open .header-actions { position: relative; z-index: 40; }
  body.menu-open .language-link { color: #fffaf3; }
  .language-link { grid-column: 3; grid-row: 1; }
}

.page-main, .listing-main, .article-main { padding-top: var(--site-header-height); }
.page-main + .site-footer { margin-top: 0; }
.page-hero { display: grid; grid-template-columns: 1fr 1fr; min-height: 700px; background: #aabbff; }
.page-hero-copy { display: flex; flex-direction: column; justify-content: flex-end; padding: 80px 55px; }
.eyebrow { display: block; margin-bottom: 28px; font-size: 13px; letter-spacing: .12em; text-transform: uppercase; }
.page-hero h1, .listing-header h1, .article-header h1 { margin-bottom: 24px; font-size: clamp(67px, 7vw, 108px); }
.page-hero p { max-width: 570px; font-size: 19px; }
.page-hero-image img { width: 100%; height: 100%; object-fit: cover; }
.page-content, .entry-content { max-width: 920px; margin: 0 auto; padding: 110px 30px; font-size: 19px; line-height: 1.6; }
.entry-content h2 { margin-top: 1.2em; font-size: 56px; }
.entry-content h3 { margin-top: 1.5em; font-size: 28px; }
.listing-header, .article-header { padding: 120px 55px 75px; background: #aabbff; }
.listing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 36px 18px; padding: 70px 55px 110px; }
.listing-card img { width: 100%; aspect-ratio: 1.35; object-fit: cover; border-radius: 8px; }
.listing-card h2 { margin: 18px 0 12px; font-size: 35px; }
.article-header { max-width: none; }
.article-header h1 { max-width: 1040px; }
.article-image { margin: 0; }
.article-image img { width: 100%; max-height: 780px; object-fit: cover; }

.reveal-on-scroll { opacity: 1; transform: none; }
.has-reveal-motion .reveal-on-scroll { opacity: 0; transform: translateY(18px); transition: opacity 620ms cubic-bezier(.22,.61,.36,1) var(--reveal-delay, 0ms), transform 620ms cubic-bezier(.22,.61,.36,1) var(--reveal-delay, 0ms); }
.has-reveal-motion.is-preparing-reveals .reveal-on-scroll { transition: none; }
.has-reveal-motion .reveal-on-scroll.reveal-fade { transform: none; transition-duration: 720ms; }
.has-reveal-motion .reveal-on-scroll.is-visible { opacity: 1; transform: none; }

@media (max-width: 980px) {
  :root { --site-header-gutter: 24px; }
  .brand img { width: 108px; }
  .home-hero { min-height: 760px; padding: 110px 24px 80px; }
  .home-hero h1 { font-size: clamp(56px, 11vw, 78px); }
  .innovation-panel { grid-template-columns: 1fr; gap: 30px; min-height: 0; padding: 28px 24px 70px; }
  .innovation-image img { height: 610px; }
  .innovation-copy { padding-top: 15px; }
  .section-pad { padding: 75px 24px; }
  .section-heading { align-items: center; margin-bottom: 45px; }
  .expertise-grid { grid-template-columns: 1fr; }
  .expertise-card { min-height: 560px; }
  .partner-row { grid-template-columns: repeat(3, 1fr); gap: 35px; }
  .driving-section { padding: 75px 24px; }
  .driving-grid { grid-template-columns: 1fr; gap: 40px; }
  .insights-layout { grid-template-columns: 1fr; }
  .testimonial-section { grid-template-columns: .7fr 1.1fr; margin: 20px 24px 75px; }
  .testimonial-section blockquote { grid-column: 1 / -1; padding: 40px; }
  .footer-grid { grid-template-columns: 1fr 1fr 1fr; }
  .footer-grid > div:first-child { grid-column: 1 / -1; }
  .page-hero { grid-template-columns: 1fr; }
  .page-hero-image { min-height: 480px; }
  .listing-grid { grid-template-columns: repeat(2, 1fr); padding-inline: 24px; }
}

@media (max-width: 620px) {
  .site-header { padding-inline: 18px; }
  .primary-nav { padding: calc(var(--site-header-height) + 20px) 18px 24px; font-size: 26px; gap: 16px; }
  .primary-nav-list { gap: 16px; }
  .primary-nav.is-open ~ .primary-nav-cta { left: 18px; bottom: 22px; }
  .home-hero { min-height: 700px; padding: 120px 18px 60px; background-position: 54% center; }
  .home-hero-shade { background: rgba(8,12,10,.43); }
  .home-hero h1 { max-width: 360px; margin-bottom: 27px; font-size: 57px; line-height: .92; }
  .home-hero p { margin-bottom: 32px; font-size: 14px; }
  .button { min-height: 49px; padding: 0 19px; font-size: 13px; }
  .innovation-panel { padding-inline: 18px; }
  .innovation-image img { height: 470px; }
  .innovation-copy h2 { font-size: 53px; }
  .service-row { min-height: 66px; font-size: 14px; }
  .section-pad { padding: 65px 18px; }
  .section-heading { align-items: flex-start; }
  .section-heading h2 { font-size: 51px; }
  .section-heading .button { min-width: 49px; width: 49px; overflow: hidden; color: transparent; gap: 0; padding: 0; }
  .section-heading .button span { color: #fffaf3; }
  .expertise-card { min-height: 490px; }
  .expertise-overlay h3 { font-size: 29px; }
  .partner-row { display: flex; overflow-x: auto; padding-bottom: 10px; scroll-snap-type: x mandatory; }
  .partner-row img { flex: 0 0 116px; scroll-snap-align: start; }
  .driving-section { min-height: 720px; padding: 70px 18px; }
  .driving-grid h2 { font-size: 53px; }
  .insight-row { grid-template-columns: 118px 1fr; gap: 15px; }
  .insight-row img { width: 118px; height: 95px; }
  .insight-row h3 { font-size: 16px; }
  .testimonial-section { display: block; margin-inline: 18px; }
  .testimonial-section > h2 { padding: 35px 25px 20px; font-size: 50px; }
  .testimonial-person { min-height: 330px; padding-inline: 24px; }
  .testimonial-section blockquote { padding: 34px 25px 40px; font-size: 22px; }
  .image-cta { min-height: 590px; padding: 30px 18px; }
  .image-cta h2 { font-size: 58px; }
  .site-footer { padding: 58px 18px 20px; }
  .footer-grid { grid-template-columns: 1fr; }
  .footer-grid > div:first-child { grid-column: auto; }
  /* The width drops to 265 of the mark's natural 364, so the height has to come
     with it. Pinned at the desktop 85 it drew the wordmark 27 percent narrow.
     265 is a fixed number, though, and this rule runs all the way down to the
     narrowest phone: the mark holds 27 percent of its column at 1440 and 45 at
     620, then jumps to 75 percent at 390, where it swallows the footer and
     dwarfs its own tagline. Capping it against the column instead keeps the
     proportion the design has everywhere else, and 265 still wins at 620. */
  .footer-brand img { width: min(265px, 46%); height: auto; }
  /* The logo drops to 265 of its 364, so the tagline block follows it down by
     the same 0.728 rather than keeping the desktop 35 next to a smaller mark. */
  /*
   * Figma draws no narrow footer, so this size is ours. It has to stay fluid
   * now that the tracking is gone: at a flat 25px the English tagline measures
   * 356px of ink against the 354px column a 390 wide phone leaves, so it broke
   * to two lines by two pixels. 6.2vw puts it at 24.2px on a 390 and reaches the
   * 25px ceiling by about 404, which keeps one line from 360 up in English and
   * matches the Figma 114 percent line ratio at every size.
   */
  .footer-grid .footer-tagline { margin-top: 23px; font-size: clamp(21px, 6.2vw, 25px); line-height: 1.14; }
  .footer-identity .social-links { margin-top: 23px; }
  .footer-base { display: block; margin-top: 48px; }
  .footer-base span { display: block; margin-top: 8px; }
  .page-main, .listing-main, .article-main { padding-top: 82px; }
  .page-hero-copy { padding: 65px 18px 45px; }
  .page-hero h1, .listing-header h1, .article-header h1 { font-size: clamp(48px, 13vw, 62px); overflow-wrap: anywhere; }
  .page-hero-image { min-height: 390px; }
  .page-content, .entry-content { padding: 75px 18px; font-size: 17px; }
  .entry-content h2 { font-size: 45px; }
  .listing-header, .article-header { padding: 80px 18px 50px; }
  .listing-grid { grid-template-columns: 1fr; padding: 50px 18px 80px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 1ms !important; transition-duration: 1ms !important; }
  .reveal-on-scroll { opacity: 1; transform: none; }
  .has-reveal-motion .reveal-on-scroll { opacity: 1; transform: none; transition: none; }
  .button-liquid-neck { display: none; }
  html { scroll-behavior: auto; }
  .site-header { animation: none; }
}
