/* ============================================================================
   Valad — design system base
   ----------------------------------------------------------------------------
   These pages are an extension of valad.io and must read as the same site, so
   the values below are measured from valad.io rather than invented here.

   Reference readings (Chrome, 2026-08-12):

     Typeface     Saans VF-TRIAL Variable Light. The face is served from Framer
                  as a single woff2; it is vendored into assets/ so these pages
                  do not hotlink framerusercontent.com.

     Weight       An earlier reading here recorded "weight 300 everywhere,
                  valad.io has no weight variation at all". That was taken off
                  `font-weight`, which is indeed 300 on virtually every element
                  on that site — but the face is variable, and what actually
                  renders is the `wght` axis in `font-variation-settings`, which
                  valad.io sets independently and does vary. Re-measured:

                    display  24-64px   300
                    body     13-20px   400
                    nav      15px      450
                    hero CTA 15px      500
                    footer   15px      350
                    eyebrow  12px      350

                  So hierarchy is carried by weight as well as size and colour,
                  on the usual optical principle: display runs light, body sits
                  at normal, and small UI text is nudged heavier so it holds at
                  size. The vendored woff2 is the same variable file and its
                  axis works here — 'Handgloves' at 64px measures 523 / 527.7 /
                  538.9 / 551.1px at wght 300 / 400 / 500 / 700 — so plain
                  `font-weight` drives it and no font-variation-settings are
                  needed. Body sits at 400 below; display re-asserts 300.

     Tracking     letter-spacing: normal (0) on every element bar one 15px body
                  style at 0.15px. Matches the project note in CLAUDE.md.

     Breakpoints  desktop  >= 1200      gutter 80px, header 70px
                  tablet   810 - 1199   gutter 40px, header 70px
                  mobile   <= 809       gutter 20px, header 70px

     Logo         85 x 22 at every breakpoint — it does not scale.

     Type scale   12 13 14 15 16 18 20 22 24 26 28 32 36 40 64
                  Display steps run tight (1.1 - 1.4), body runs loose
                  (1.5 - 1.6), small labels sit at 1.2 - 1.3.

   This file carries the base layer only. Everything breakpoint-specific lives
   in responsive.css, which loads after it.
   ========================================================================== */

@font-face {
  font-family: 'Saans';
  src: url('assets/saans-light.woff2') format('woff2');
  /* valad.io ships one Light master. Claiming the full range means a stray
     font-weight:500 left anywhere in the markup resolves to this face instead
     of triggering the browser's synthetic bold. */
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Framer pairs the webfont with a metric-matched Arial fallback so the swap
   does not reflow. Same overrides, same numbers. */
@font-face {
  font-family: 'Saans Fallback';
  src: local('Arial');
  ascent-override: 91.69%;
  descent-override: 22.67%;
  line-gap-override: 0%;
  size-adjust: 99.25%;
}

:root {
  --valad-font: 'Saans', 'Saans Fallback', Helvetica, Arial, sans-serif;

  /* Page gutter — the single value every section's horizontal padding uses.
     Overridden per breakpoint in responsive.css. */
  --valad-gutter: 80px;
  /* valad.io does not let the content column grow without limit. Its sections
     are `padding: 0 80px` around an inner block capped at `max-width: 1600px`
     and centred, so the column is min(viewport - 160, 1600): 1265 at 1440,
     still 1600 at 2560. Measured at 1920, where the inner block reports
     max-width 1600px and sits at x=152.5 — 80 of gutter plus 72.5 of the
     centring remainder.

     Reproduced here on the gutter rather than with a wrapper element, because
     the gutter is already the one value every section, absolute inset and
     negative bleed on this site is written against. Growing it past 80 once the
     viewport clears 1760 (1600 + two gutters) caps the column at 1600 and
     centres it, with no markup change and no second concept to keep in sync.
     Below 1760 the value is untouched, so 1440/810/390 render exactly as
     before. See the min-width rule under section 1. */
  --valad-max: 1600px;
  /* Measured off valad.io: 24px of padding above and below a 46px inner row,
     for 94px total. The row is 46px because the CTA pill sets it — the 22px
     wordmark is centred inside it, not the thing that defines it. An earlier
     note here read the row as 22px and landed on 70px, which is why the bar
     sat 24px short. Mobile drops to 92px (see responsive.css section 0). */
  --valad-header-h: 94px;

  --valad-ink: #141414;
  --valad-paper: #EDECE8;

  /* Vertical rhythm ---------------------------------------------------------
     valad.io's section padding is a set of flat pixel values. It has no
     viewport-height component anywhere: a section that opens on 200px of air
     opens on 200px whether the window is 700px tall or 1400px. Read off the
     live site at 1440x900, the full vocabulary it uses is

       tops     80  120  150  160  180  200  220
       bottoms  80   90  120  130  160  200

     which is the scale below, with 150 folded into 160 and 130 into 120 — a
     10px step is not a design decision at these sizes, it is drift.

     This replaces a `clamp(min, Nvh, max)` model that had grown to 62 distinct
     hand-authored values across the site. Two things were wrong with it. The
     ad-hoc part is obvious. The vh part is subtler and mattered more: at the
     900px-tall reference viewport those clamps sat on their middle term, so a
     section authored as "up to 180px" actually rendered 108px, and the whole
     site ran roughly half of valad.io's spacing while reading, in source, as
     though it matched. Tying section rhythm to window height also means the
     composition recomposes when a browser toolbar appears, which valad.io's
     never does.

     Tablet holds the desktop values — valad.io changes only its gutter at 810,
     not its rhythm. Mobile compresses (see responsive.css section 0). */
  --v-space-xs:   80px;
  --v-space-sm:   90px;
  --v-space-md:  120px;
  --v-space-lg:  160px;
  --v-space-xl:  180px;
  --v-space-2xl: 200px;
  --v-space-3xl: 220px;

  /* One bar of the dark-to-light section wipe. Six of them stack, so the strip
     is 72px and sits well inside the 160px of top padding every light section
     opens with. Mobile takes it down in responsive.css, where that padding
     compresses to 100px. */
  --wipe-band: 12px;
}

html, body {
  font-family: var(--valad-font);
  /* Body weight, inherited by everything that does not set its own. Was 300,
     which ran the whole site a step lighter than valad.io everywhere except
     the display sizes. Display text is not affected: every heading and figure
     on these pages sets `font-weight: 300` on itself, and those win over this. */
  font-weight: 400;
  letter-spacing: 0;
}

/* Small UI text is nudged heavier, as valad.io's axis does, so it holds at
   size: navigation and the header pill at 450, footer links back down to 350.
   Kept to the two link families that valad.io measures distinctly rather than
   applied by size, since 15px means three different things on this site. */
.r-header nav a,
.r-header .r-cta { font-weight: 450; }
.r-foot-nav a,
.r-foot-nav div { font-weight: 350; }

/* Content cap ---------------------------------------------------------------
   Past 1760 the gutter absorbs everything over 1600 so the column stops
   growing and stays centred, which is what valad.io's `max-width: 1600px`
   inner block does. `100vw` counts the classic scrollbar where one is painted,
   which puts the column up to half a scrollbar off centre on those platforms;
   it is padding, not width, so it can never overflow, and on the overlay
   scrollbars this site is read on the two agree exactly. */
@media (min-width: 1761px) {
  :root { --valad-gutter: calc((100vw - var(--valad-max)) / 2); }
}

/* Header CTA ----------------------------------------------------------------
   The bar is a grid with `align-items: stretch`, and the CTA comes in two
   shapes: on the homepage and Careers `.r-cta` is a wrapper that stretches the
   full bar and centres the 44px pill inside itself, while on the other four
   pages `.r-cta` is the pill — an anchor with `height: 44px` of its own. A grid
   item whose cross size is definite cannot stretch, so those four fall back to
   start alignment and the pill hangs off the top edge of the bar with all the
   slack below it. Centring it explicitly fixes the four and changes nothing for
   the two, which already centre their pill by other means.

   Only below 810 does this stop applying — there the CTA leaves the bar for the
   overlay and is positioned, so alignment within the grid no longer means
   anything (see section 2 of responsive.css). */
.r-header .r-cta { align-self: center; }

/* Header plate --------------------------------------------------------------
   The bar carries no background while it is over a page's hero — it is a fixed
   overlay on the composition, not a band above it — and takes its plate once
   the page has scrolled past the trip point. The homepage and Careers drive
   this from their own scroll components, which write these same values inline;
   the other four pages get the class from header-scroll.js. Both routes land on
   the same appearance so the bar behaves identically across the site. */
/* Solid white, not the paper tint it used to carry at 92% over a 12px blur —
   that read as a translucent grey panel sitting on the page rather than as a
   bar. Opaque means the blur has nothing to sample and only costs a compositing
   layer and a containing block, so it goes with it; the hairline stays, since
   an opaque white bar over white content needs the edge to read. */
.r-header--solid.r-header--light {
  background: #FFFFFF;
  box-shadow: 0 1px 0 rgba(20, 20, 20, .12);
}
.r-header--solid.r-header--dark {
  background: rgba(20, 20, 20, .9);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 rgba(243, 242, 238, .12);
}

/* Two pages open on a dark full-bleed photograph while running the same light
   bar as everywhere else, so with no plate their marks would be dark ink on a
   dark image. `r-header--hero-dark` flips the wordmark, the burger and the nav
   links for exactly as long as the bar is transparent.

   The pill is a separate opt-in because it is a separate judgement: it is a
   solid shape, not ink, so it survives a mid-tone image where thin marks would
   not. The homepage's dusk skyline reads fine behind the dark pill and keeps
   it; About's near-black cave does not, and takes `r-header--hero-invert-cta`
   as well.

   Nav and pill are handled at desktop widths only — below 810 both live inside
   the overlay, which paints its own background and sets its own colours. */
/* `!important` because the mobile tier pins the light bar's wordmark to
   `filter: none !important` and an important declaration is only beaten by
   another one; this selector is the more specific of the two, so it wins where
   it applies and defers to the open-overlay rule that is more specific still. */
.r-header--hero-dark:not(.r-header--solid) .r-logo img {
  filter: invert(1) brightness(1.7) !important;
}

/* Careers runs the dark bar variant, which is dark at every scroll position —
   transparent over its dark hero, then rgba(20,20,20,.9) once plated. It ships
   the same dark wordmark file every other page does and nothing ever inverted
   it, so the mark was dark on dark and simply did not appear. The rule above
   only covers a light bar sitting temporarily over a dark hero, which is a
   different case: there the inversion has to switch off once the bar plates,
   and here it must not. Same transform, held for good.

   `[data-mega="open"]` further down turns it back off, because the panel takes
   the bar white and a white wordmark on white is the bug in reverse. */
.r-header--dark .r-logo img {
  filter: invert(1) brightness(1.7) !important;
}
/* valad.io sets the nav links to a 15px line-height, the same as their size, so
   each link box is exactly as tall as the type. The links here carry no
   line-height inline and so inherit `normal` (~18px), which pushed them off the
   optical centre of the bar. The 8px of inline vertical padding stays — it is
   hit area only and, in a centred flex row, moves nothing. */
@media (min-width: 810px) {
  .r-header .r-nav > a { line-height: 15px; }
  .r-header--hero-dark:not(.r-header--solid) .r-nav > a { color: #FFFFFF !important; }
  .r-header--hero-invert-cta:not(.r-header--solid) a.r-cta,
  .r-header--hero-invert-cta:not(.r-header--solid) .r-cta > a {
    background: #FFFFFF !important;
    color: #141414 !important;
  }
}

/* The markup carries ~370 inline font-family declarations that have been
   rewritten to lead with Saans, but headings still default to bold in the UA
   sheet. valad.io renders every heading at 300. */
h1, h2, h3, h4, h5, h6 {
  font-weight: 300;
}

/* Nothing on valad.io is bolder than Light. These are the only two elements
   that would otherwise inherit a heavier default. */
strong, b {
  font-weight: 300;
}

/* Form controls do not inherit type from `body` — the UA sheet gives them their
   own `font: 400 13.333px Arial`. Without this the portfolio and news
   pagination and the modal close buttons render in Arial Regular. */
button, input, select, textarea {
  font-family: inherit;
  font-size: inherit;
  font-weight: 300;
  letter-spacing: 0;
}

/* The portfolio and news filters are `flex:1` selects inside a fixed grid
   cell. A flex item's default `min-width:auto` refuses to shrink below its
   widest option, so at 1200 — the narrowest desktop width — the Sector select
   pushed 37px past the viewport. `min-width:0` lets them take the cell. */
select {
  min-width: 0;
  max-width: 100%;
}

/* Footer link leading ------------------------------------------------------
   valad.io sets its footer links to 15px/22.5px, which makes each link row
   exactly 23px tall. VAM's links carry no line-height and inherit `normal`
   (~18px), so the columns stacked tighter than valad.io's at the same 32px gap
   and the whole block read shorter than it should. Size and colour are already
   matched inline; this is only the leading.

   The uppercase column headers are deliberately left alone — they are VAM's
   own eyebrow treatment, and CLAUDE.md allows positive tracking on exactly
   that kind of small uppercase label. valad.io runs its headers at the same
   15px as the links instead, which is a content decision, not a metric. */
.r-foot-nav a { line-height: 22.5px; }

/* Partner logos -------------------------------------------------------------
   A note on the two fills, because they look redundant and are not: the cells
   are painted the same colour as the section they sit in, and the grid beneath
   them is painted #CCCBC8. Nothing of the grid is visible except the 1px gaps
   between cells, so the fills are what make the rules read as rules rather than
   as a grey block. valad.io does the same and its cells are #EDECE8 because its
   section is; this section is white, so these are white. Change the section
   background and these have to follow it.

   valad.io does not show its partners in brand colour. It ships white and grey
   monochrome artwork and puts `filter: invert(0.8)` on the wrapper, so white
   (255) lands at 255*0.2 = 51 — every mark renders as a flat #333333.

   The same trick cannot be applied here, because these are full-colour brand
   assets rather than white ones: inverting them would produce photo negatives.
   `brightness(0)` first crushes any colour to black while keeping the alpha
   shape, and `invert(.2)` then lifts black to 0 + 255*0.2 = 51 — the identical
   #333333, arrived at from a colour source instead of a white one. */
.r-logo-grid img { filter: brightness(0) invert(.2); }

/* Footer column rules -------------------------------------------------------
   Each footer column opens on a 1px rule. An earlier spec recorded valad.io's
   columns as having "no top border — border-top: 0px none, the separation is
   done with 24px of padding alone", and this site duly had its rules removed.
   That reading was taken off the column elements themselves; valad.io draws the
   rules on an `::after` pseudo-element instead, absolutely positioned to inset
   0 with a 1px top border, so the column's own computed border really is 0
   while a rule is plainly painted. Read off the pseudo-elements:

     link row    1px solid #FFFFFF                (full white)
     legal row   1px solid rgba(255,255,255,.12)  (a hairline)

   The two rows are deliberately different weights — the links carry a bright
   rule, the copyright and addresses beneath sit under a faint one. The 24px of
   padding the old note described is real and already inline on every column;
   it is the gap between the rule and the text, not a replacement for it. */
.r-foot-nav > div { border-top: 1px solid #FFFFFF; }
.r-foot-addr > div { border-top: 1px solid rgba(255, 255, 255, .12); }

/* Closing marquee -----------------------------------------------------------
   Every page ends on the same block: the closing line and its links, then a
   slow band of grayscale imagery, then the LinkedIn and email marks. The band
   is a single row duplicated end to end and translated -50%, so the second copy
   lands exactly where the first started and the loop has no seam — which is why
   the track must carry an even number of images, half of them repeats.

   The keyframes are the reason this lives here rather than in the markup: the
   rest of the composition is authored inline, but `@keyframes` has no inline
   form, and the homepage was the only page that had it. The remaining sizing is
   kept alongside it so the six copies of the block cannot drift apart. */
@keyframes vmarquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
.r-marquee {
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.r-marquee-track {
  display: flex;
  width: max-content;
  gap: clamp(60px, 9vw, 150px);
  animation: vmarquee 32s linear infinite;
}
.r-marquee-track img {
  height: clamp(150px, 20vh, 215px);
  width: auto;
  display: block;
  filter: grayscale(1) contrast(1.05);
}
/* Held still rather than hidden — the imagery is part of the composition, the
   travel is not. The duplicate half simply sits off the right edge. */
@media (prefers-reduced-motion: reduce) {
  .r-marquee-track { animation: none; }
}
/* The homepage's closing section carries no horizontal padding, so its band
   runs edge to edge on its own. The five interior pages close inside the page
   gutter, and the band has to step back out of it to read the same way. */
.r-marquee--bleed {
  margin-left: calc(var(--valad-gutter) * -1);
  margin-right: calc(var(--valad-gutter) * -1);
}

/* Closing CTA band ----------------------------------------------------------
   Box metrics measured off valad.io: a 632px plate, `overflow: clip`, 200px of
   vertical padding around a centred 232px stack -- 134px of headline, a 50px
   gap, a 48px button row -- with the headline at 32px/44.8px in #141414.

   An earlier note here recorded the four renders as permanently `scale(3)` and
   overlapping the copy, and concluded the arrangement could not be reproduced.
   That reading was taken mid-animation. Measured again at successive scroll
   positions the same four boxes report scale 3.0, then 1.85, then 1.0: the
   scale is driven by scroll, and at rest they settle into the margins around
   the centred copy without touching it. Their resting geometry, relative to
   the 1425x632 plate, is

     A  445x354  left 1053  top -105   (bleeds off the top and right)
     B  315x428  left   74  top  325   (bleeds off the bottom)
     C  168x208  left  976  top  353
     D  117x114  left  371  top   59

   so they are anchored here by whichever edge they sit nearest -- A and C from
   the right, B and D from the left -- and the plate clips the overflow. There
   is no scrim and no full-bleed image: the ground is flat paper, and the
   renders are four discrete objects on it.

   The drift that earlier passes left out has since been read exactly. Every
   figure is driven by one shared value -- call it v, which runs 2 at the
   approach down to 0 at rest -- and each writes it into the same two channels:

     scale        1 + v                    (so 3 on approach, 1 at rest)
     translate    a per-figure px offset x v

   sampled off the live site as

     A  translateY(-100v)
     B  translateX(-30v)  translateY(-15v)
     C  translateX( 20v)  translateY( 30v)
     D  translateY(35.5v)

   at v = 2 that reads translateY(-200px) scale(3), translateX(-60px)
   translateY(-30px) scale(3), and so on, which is what the four elements carry
   on a page that has not reached the band yet. So the boxes do not merely zoom
   out of the plate, they converge on it: A falls from above, C rises from
   below and to the left, B drifts in from the right, D from above. The
   multipliers are the whole of the second channel and they are small next to
   the zoom, which is why a two-sample reading missed them.

   valad.io writes B's offsets on top of a `translate(-50%, -50%)` because it
   anchors that figure by its centre. Ours is anchored top-left like the other
   three, so only the drift carries over; the -50% is its layout, not its
   animation.

   header-scroll.js writes v as `--cta-v` on the band. The figures compose both
   channels from it, so a band that never gets the property -- script blocked,
   JS off, reduced motion -- resolves to translate(0,0) scale(1) and renders at
   rest, which is the state the composition was measured in. */
.r-cta-band {
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
  height: 632px;
  padding: 200px var(--valad-gutter);
  /* valad.io's plate is #EDECE8. White is a deliberate departure — asked for
     directly, so the renders sit on paper rather than on the page's own grey. */
  background: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
}
.r-cta-band-fig {
  position: absolute;
  display: block;
  object-fit: cover;
  transform:
    translate(calc(var(--fig-dx, 0px) * var(--cta-v, 0)),
              calc(var(--fig-dy, 0px) * var(--cta-v, 0)))
    scale(calc(1 + var(--cta-v, 0)));
  will-change: transform;
}
.r-cta-band-fig--a { width: 445px; height: 354px; right: -73px;  top: -105px; --fig-dx:   0px; --fig-dy: -100px; }
.r-cta-band-fig--b { width: 315px; height: 428px; left:   74px; top:  325px; --fig-dx: -30px; --fig-dy:  -15px; }
.r-cta-band-fig--c { width: 168px; height: 208px; right: 281px; top:  353px; --fig-dx:  20px; --fig-dy:   30px; }
.r-cta-band-fig--d { width: 117px; height: 114px; left:  371px; top:   59px; --fig-dx:   0px; --fig-dy: 35.5px; }
/* 232px of content in a 632px band: 134 of headline, a 50px gap, a 48px row. */
.r-cta-band-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 50px;
  width: 100%;
  max-width: 1265px;
  text-align: center;
}
.r-cta-band-head {
  margin: 0;
  max-width: 404px;
  font-family: var(--valad-font);
  font-weight: 300;
  font-size: 32px;
  line-height: 44.8px;
  letter-spacing: 0;
  color: #141414;
  text-wrap: pretty;
}
.r-cta-band-actions { display: flex; justify-content: center; gap: 4px; }
.r-cta-band-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 22px;
  border: 1px solid rgba(20, 20, 20, .28);
  border-radius: 60px;
  font-family: var(--valad-font);
  font-weight: 300;
  font-size: 15px;
  line-height: 24px;
  letter-spacing: 0;
  color: #141414;
  transition: background-color 260ms ease, color 260ms ease;
}
.r-cta-band-btn:hover { background: #141414; color: #EDECE8; }

/* About mega-menu -----------------------------------------------------------
   A panel that drops from the bar when About is opened. Built once by
   header-scroll.js and inserted after the header on every page, rather than
   pasted into all twenty-one — the header is inline markup with no include, so
   duplicating a block this size would mean twenty-one copies to keep in step.

   Laid out on the same spine as the rest of the site: the gutter carries the
   inset, so the panel's content is capped at --valad-max and centred exactly as
   every section is, and the row rules are the same hairline the footer uses.

   Off below 810, where the bar hands its navigation to the burger overlay. */
.r-mega {
  position: fixed;
  top: var(--valad-header-h);
  left: 0;
  width: 100%;
  z-index: 59;
  background: #FFFFFF;
  border-top: 1px solid rgba(20, 20, 20, .13);
  box-shadow: 0 18px 40px rgba(20, 20, 20, .07);
  padding: 0 var(--valad-gutter);
  /* Collapsed rather than display:none so the open can be animated and the
     panel still measures for assistive tech when open. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 260ms cubic-bezier(.22,.61,.36,1),
              transform 260ms cubic-bezier(.22,.61,.36,1),
              visibility 0s linear 260ms;
}
.r-mega[data-open] {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
  transition-delay: 0s, 0s, 0s;
}
.r-mega-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 0 clamp(40px, 5vw, 90px);
  padding: 8px 0 44px;
}
/* Each group is a label column beside its links, ruled off from the one above
   exactly as valad.io rules its footer columns. */
.r-mega-group {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: 0 clamp(20px, 3vw, 46px);
  padding: 30px 0;
  border-bottom: 1px solid rgba(20, 20, 20, .13);
}
.r-mega-group:last-of-type { border-bottom: 0; }
.r-mega-label {
  font-family: var(--valad-font);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.4;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgba(20, 20, 20, .45);
  padding-top: 4px;
}
.r-mega-items {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 22px clamp(24px, 4vw, 64px);
}
/* Icon box, then the title with the arrow pushed to the far edge of the row,
   then the description indented to the title. */
.r-mega-item {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  gap: 0 14px;
  color: #141414;
}
.r-mega-icon {
  grid-row: 1 / 3;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(20, 20, 20, .16);
  transition: border-color 260ms ease, background-color 260ms ease;
}
/* Hover fills the icon box with the accent. Scoped to `.r-mega`, which is the
   desktop panel's own class — the burger's sub-panels render the same rows from
   the same markup but without it, and a colour that only means "your pointer is
   here" should not be reachable by a tap that sticks.

   The accent lands on the box rather than on the glyph's stroke. #BAD2FF is a
   pale blue: as an 1.3px stroke on this panel's ground it reads as the icon
   having faded out rather than as it having gone blue, while behind a #141414
   glyph it is unmistakable and leaves the mark legible. */
.r-mega .r-mega-item:hover .r-mega-icon { border-color: #BAD2FF; background: #BAD2FF; }
.r-mega-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--valad-font);
  /* 15px, the bar's own size — the panel drops out of the nav, so its items
     read as a continuation of it rather than as a heading level of their own.
     Weight stays at 400 against the bar's 450: same size, one step lighter, so
     the nav still reads as the control and these as its contents. */
  font-weight: 400;
  font-size: 15px;
  line-height: 24px;
  letter-spacing: 0;
  color: #141414;
}
/* The arrow steps forward on hover, which is the same gesture the portfolio
   and news cards use. */
.r-mega-head svg {
  flex: 0 0 auto;
  transition: transform 260ms cubic-bezier(.22,.61,.36,1);
}
.r-mega-item:hover .r-mega-head svg { transform: translate(3px, -3px); }
.r-mega-desc {
  margin: 4px 0 0;
  max-width: 260px;
  font-family: var(--valad-font);
  font-weight: 400;
  font-size: 13px;
  line-height: 20px;
  letter-spacing: 0;
  color: rgba(20, 20, 20, .55);
  text-wrap: pretty;
}
.r-mega-item:hover .r-mega-desc { color: rgba(20, 20, 20, .8); }
/* The featured rail sits in its own column and takes the full stack height. */
.r-mega-feature { padding: 30px 0 0; }
.r-mega-feature-label {
  font-family: var(--valad-font);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.4;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: rgba(20, 20, 20, .45);
}
.r-mega-feature-card { display: block; margin-top: 22px; color: #141414; }
.r-mega-feature-img {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: #EDECE8;
}
/* The arrow button sits over the image's lower-left corner, as it does on
   Augustus's featured card. */
.r-mega-feature-img::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 46px;
  height: 46px;
  background: #141414
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3 8h10M9 4l4 4-4 4' stroke='%23FFFFFF' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 16px 16px no-repeat;
}
.r-mega-feature-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 700ms cubic-bezier(.22,.61,.36,1);
}
.r-mega-feature-card:hover .r-mega-feature-img img { transform: scale(1.04); }
.r-mega-feature-head {
  display: block;
  margin: 16px 0 0;
  font-family: var(--valad-font);
  font-weight: 400;
  font-size: 15px;
  line-height: 23px;
  letter-spacing: 0;
  color: #141414;
  text-wrap: pretty;
}
/* The chevron on the bar's two menu links. Drawn as a pseudo-element keyed off
   the href rather than an SVG appended by the script, because the DC runtime
   replaces the nav anchors on re-render and takes any injected child with them.
   The href is authored markup, so it survives; the rotation reads the open flag
   from the root element for the same reason.

   Rotation is keyed to `data-mega-id` rather than to `data-mega` so only the
   caret belonging to the panel that is actually open turns — with both menus
   sharing the one open flag, the plain `[data-mega="open"]` form flipped both
   chevrons whichever of them you clicked. */
/* Direct children only. Written as a descendant selector originally, which was
   harmless while the only `about.html` and `portfolio.html` links inside
   `.r-nav` were the two bar items themselves. The mobile sub-panels changed
   that: their rows live inside `.r-nav` too, and "Overview" points at
   about.html while "Our Assets" points at portfolio.html — so both picked up a
   caret announcing a dropdown they do not have. `>` scopes this back to the
   bar's own items, which is all it ever meant. */
.r-header .r-nav > a[href="about.html"]::after,
.r-header .r-nav > a[href="portfolio.html"]::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin: -3px 0 0 8px;
  border-right: 1.4px solid currentColor;
  border-bottom: 1.4px solid currentColor;
  transform: rotate(45deg);
  transform-origin: 60% 60%;
  transition: transform 260ms cubic-bezier(.22,.61,.36,1);
  vertical-align: middle;
}
[data-mega-id="aboutMega"] .r-header .r-nav > a[href="about.html"]::after,
[data-mega-id="portfolioMenu"] .r-header .r-nav > a[href="portfolio.html"]::after {
  transform: rotate(-135deg);
}

/* Mobile/tablet sub-nav (header-scroll.js). Hidden by default: at desktop
   widths `.r-nav` is the horizontal bar and this would otherwise land as an
   extra flex item squeezed in next to "About" and "Portfolio" — the
   responsive mobile block is what turns it on, under 810px only. */
.r-nav-sub { display: none; }

/* With the panel open the bar takes its plate whatever the scroll state. Over a
   hero the bar is transparent, and a white panel hanging off a transparent bar
   reads as two disconnected layers with the photograph running between them.

   Every declaration here is `!important` because it has to beat an inline
   style, not another rule: the homepage and Careers paint their bar from their
   own scroll components, which write background, shadow and the logo's filter
   straight onto the element. An author `!important` outranks a normal inline
   declaration, which is the one case where it does.

   The bar's own hairline is dropped rather than doubled — the panel carries a
   border on its top edge, and the two would land on the same pixel. */
[data-mega="open"] .r-header {
  background: #FFFFFF !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* A hero-dark page runs its links and wordmark inverted while the bar is over
   the photograph. On white they would be white on white. */
[data-mega="open"] .r-header .r-nav a { color: #141414 !important; }
[data-mega="open"] .r-header .r-logo img { filter: none !important; }

/* The scrim closes the panel on a click anywhere off it, and dims what is
   behind so the panel reads as a layer rather than part of the page. */
.r-mega-scrim {
  position: fixed;
  inset: var(--valad-header-h) 0 0 0;
  z-index: 58;
  background: rgba(20, 20, 20, .22);
  opacity: 0;
  pointer-events: none;
  transition: opacity 260ms ease;
}
.r-mega-scrim[data-open] { opacity: 1; pointer-events: auto; }

/* How we operate — icon boxes ------------------------------------------------
   The same mark the About panel uses: a square hairline box with a thin line
   icon in it, rather than a filled rounded tile. Only the box changes; the
   glyphs and the section's own dim-when-closed behaviour are untouched.

   Written here rather than on the elements because the chain script rewrites
   `background` inline on every state change — hence `!important` on the two
   properties it sets. The border follows the panel instead: the script marks
   the open panel with `data-active`, which is light, so the border there is
   dark ink and on the closed dark panels it is light.

   Stroke drops 1.5 -> 1.3 to match the panel's icons, and the script's own
   stroke colour swap is left alone: it already flips with the panel. */
#chainRow [data-icon] {
  border-radius: 0 !important;
  background: transparent !important;
  border: 1px solid rgba(255, 255, 255, .22);
  transition: opacity 620ms ease, border-color 620ms ease;
}
#chainRow [data-panel][data-active] [data-icon] {
  border-color: rgba(20, 20, 20, .18);
}
#chainRow [data-icon] svg { stroke-width: 1.3; }

/* Dark-to-light section wipe -------------------------------------------------
   Every boundary where a dark section hands over to a light one is drawn as a
   stack of thin bars that fill in as the light section rises, rather than as a
   single hard edge. Each bar grows from its own top-left corner — wider and
   taller at once — and the bars run bottom-first, so the edge reads as a comb
   closing up from the seam rather than as a curtain.

   The strip sits inside the light section, painted in the dark section's colour
   with the light colour growing over it. That direction matters: doing it the
   other way, as an overlay hanging above the section, would put a light plate
   over the last 84px of the dark section and cover whatever it closes with.
   This way the strip only ever occupies the light section's own top padding,
   which is 160px and empty.

   Six background layers on one pseudo-element rather than six divs, because the
   DC runtime diffs the rendered tree against itself on a second pass after
   load: an extra child inserted into a section it owns puts that diff out of
   step, and it patches the section after it into the section before — which is
   how a strip built for News ended up inside Contact. A pseudo-element is not a
   node and cannot be diffed. Each layer is a flat fill of the light colour,
   sized to its bar and pinned to its band's top-left corner, so growing one is
   one background-size and no reflow.

   `--wipe-from`, `--wipe-to` and the six progress values are written by
   header-scroll.js. A strip that never gets them renders as the fully-closed
   edge, which is the state the boundary has always had, so a blocked or failed
   script costs nothing. */
.r-wipe-host {
  position: relative;
  /* Makes the section a stacking context so the strip's negative index lands
     between the section's background and its content rather than sinking
     behind the section's own paint. */
  isolation: isolate;
  /* No rule across the seam. Several sections open on a hairline top border,
     and a line drawn along the top of the strip turns the bars into the
     contents of a bordered box — it reads as a graphic that belongs to the
     section rather than as the edge between two colours. `!important` because
     those borders are authored inline. */
  border-top: 0 !important;
}

.r-wipe-host::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(var(--wipe-band) * 6);
  z-index: -1;
  pointer-events: none;
  background-color: var(--wipe-from, transparent);
  /* Flat fills, hard edges. An earlier pass feathered each bar's leading
     edge to soften how it arrived; that reads as a blurred bar rather than
     a drawn one, which is not the effect. The seams these were meant to
     help with were never an edge problem anyway — they came from the
     fractional band heights fixed below. */
  background-image:
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent)),
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent)),
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent)),
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent)),
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent)),
    linear-gradient(var(--wipe-to, transparent), var(--wipe-to, transparent));
  background-repeat: no-repeat;
  /* Both channels animate. Each bar starts as a 2px line and grows toward its
     band, so while the comb is in flight there is a sliver of the dark ground
     visible between one line and the next — that gap is the effect, not a
     defect, and it is what makes six lines read as six lines rather than as
     one block being stretched.

     The gap has to be gone once a bar arrives, or the strip keeps a ladder of
     hairlines across it long after the animation has finished, which is what
     was wrong the first time. Two things guarantee it: the eased progress
     resolves to exactly 1 at the end of the run, and the +2px carries each bar
     past its own band into the one below. Since every layer is the same flat
     colour, overshooting paints over the bar beneath in the colour already
     there — the closing shows, the overlap does not. */
  background-size:
    calc(var(--wipe-p0, 1) * 100%) calc(var(--wipe-p0, 1) * var(--wipe-band) + 2px),
    calc(var(--wipe-p1, 1) * 100%) calc(var(--wipe-p1, 1) * var(--wipe-band) + 2px),
    calc(var(--wipe-p2, 1) * 100%) calc(var(--wipe-p2, 1) * var(--wipe-band) + 2px),
    calc(var(--wipe-p3, 1) * 100%) calc(var(--wipe-p3, 1) * var(--wipe-band) + 2px),
    calc(var(--wipe-p4, 1) * 100%) calc(var(--wipe-p4, 1) * var(--wipe-band) + 2px),
    calc(var(--wipe-p5, 1) * 100%) calc(var(--wipe-p5, 1) * var(--wipe-band) + 2px);
  background-position:
    0 0,
    0 var(--wipe-band),
    0 calc(var(--wipe-band) * 2),
    0 calc(var(--wipe-band) * 3),
    0 calc(var(--wipe-band) * 4),
    0 calc(var(--wipe-band) * 5);
}

/* Type weight ---------------------------------------------------------------
   Saans ships here as a single static master (`font-weight: 100 900` on one
   file), so `font-weight` and `font-variation-settings` change nothing —
   measured on the page, one string renders at an identical width at weight
   100, 200, 300, 400 and 700. The axis note at the top of this file is stale;
   there is no lighter cut in the build to reach for.

   So the only lever on apparent weight is rasterisation. Default subpixel
   antialiasing adds weight to glyph edges; greyscale antialiasing does not,
   which is what actually thins the type rather than shrinking or greying it.

   Set globally. The effect is most pronounced on light ink over near-black,
   where the added weight blooms, and an earlier pass scoped it to dark blocks
   only for that reason — but the site then rendered its display type at two
   different apparent weights depending on which way a section was inverted,
   which is worse than either weight on its own. One rasterisation everywhere
   is the consistent result, and it is the lighter of the two. */
html, body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
