/* =========================================================
   FRONTERAS Collective — Landing
   Tokens, primitives, and section styles.
   Edit any value here to reskin the whole page.
   ========================================================= */

/* ---------------------------------------------------------
   Fonts, served from this origin.

   These came from Google Fonts until 11 August 2026, which cost a
   render-blocking round trip to two extra hosts before any text could paint,
   two exceptions in the Content-Security-Policy, and a third-party request
   that /privacy/ had to disclose. Self-hosting removed all four.

   Files are the `latin` subset only — 6 weights' worth of coverage in 59 KB.
   Every non-ASCII character the site renders today is inside Latin-1, and so
   is everything Spanish needs (ñ á é í ó ú ü ¿ ¡). Two things are NOT covered
   and fall back to a system font, which is how they already behaved:
     - the geometric marks and arrows (◆ ◇ ○ ◐ ↑ →), which Plex's latin subset
       does not carry
     - Guaraní's tilde vowels (ẽ ĩ ũ ỹ), which Google files under its
       `vietnamese` subset rather than latin-ext. If Guaraní text is ever set on
       this site, that is the subset to fetch — latin-ext would not help.

   IBM PLEX SANS IS ONE VARIABLE FONT, NOT FOUR WEIGHTS. Google's css2 endpoint
   hands back four @font-face rules for 300/400/500/600 that all point at the
   same file, because the file carries the whole weight axis. Written out as
   four rules against four copies, a browser downloads 40 KB four times. One
   rule with a weight RANGE is the correct form. Plex Mono is not variable —
   its two weights really are two files.

   To re-fetch: request the css2 URL with a modern browser User-Agent (an older
   one is served TTF instead of woff2) and keep the blocks Google labels as the
   latin subset:
   https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap

   Do not write a CSS comment marker inside this block. The first draft quoted
   Google's own subset label literally, which closed the comment early; the
   remainder became stray declarations and silently ate the Plex Sans @font-face
   below it. Nothing looked wrong locally, because this machine has Plex Sans
   installed and the page simply used that — the sort of failure that only shows
   up on someone else's computer. Caught by checking document.styleSheets for
   the rule rather than by looking at the page. --------------------------- */

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 300 600;
  font-stretch: 100%;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-sans-var.woff2') format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-400.woff2') format('woff2');
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fonts/ibm-plex-mono-500.woff2') format('woff2');
}

:root {
  /* Foundation */
  --ink:         #11151c;
  --ink-soft:    #2c313a;
  --paper:       #efece4;
  --paper-2:     #e6e3d9;
  --paper-3:     #ddd9cd;
  /* --saffron is the accent on DARK surfaces only: 8.25:1 on --ink, but 1.88:1
     on --paper, which fails WCAG AA outright. Never put it on a light surface. */
  --saffron:     #e8a017;
  /* Lifted saffron for SMALL text on --canopy, where plain --saffron lands at
     4.42:1 and misses AA for body-size type. */
  --saffron-lift: #f2b134;
  --steel:       #5a6068;
  --rule:        #cdc8b8;
  --rule-strong: #b8b29e;

  /* Accent on LIGHT surfaces: --petrol, 9.01:1 on --paper. This replaced a rust
     red (--saffron-ink, #9e3f2c, 5.56:1) in July 2026. The token was deliberately
     removed rather than repointed — a variable named "saffron" holding a blue is
     the kind of quiet lie that survives review. Use --petrol directly.

     --petrol-lift is the same blue raised for DARK surfaces, where --petrol is
     1.72:1 and unusable. At 8.30:1 on --ink it carries the same visual weight as
     --saffron does, so the two read as equal partners in categorical use. */
  --petrol:      #1e4258;
  --petrol-lift: #63b8e6;
  /* --canopy raised for dark surfaces, same relationship saffron-lift and
     petrol-lift have to their bases. Plain --canopy is 1.87:1 on --ink and
     effectively invisible there; this is 8.15:1. */
  --canopy-lift: #5fbf92;
  /* The fifth pin colour on the research atlas: collaborators' research sites,
     added 13 August 2026. It is a -lift with no base, and deliberately so —
     there is no light-surface pink on this site and this token must not become
     one. It measures 8.25:1 on --ink, which is saffron's number exactly, and
     within 0.15 of petrol-lift and canopy-lift; that is the point of the value,
     since the four have to read as equal partners in a categorical set rather
     than as a hierarchy. On --paper it is 1.88:1 and unusable, same as the
     others. */
  --rose-lift: #ec93bd;

  /* Categorical accents */
  --canopy: #0e4d3a;
  --poppy:  #b8302c;

  /* Fonts */
  --display: 'IBM Plex Sans', sans-serif;
  --body:    'IBM Plex Sans', sans-serif;
  --mono:    'IBM Plex Mono', monospace;

  /* Spacing */
  --pad-x: clamp(20px, 4vw, 64px);
  --max-w: 1320px;

  /* Radii */
  --r-card:    4px;
  --r-card-lg: 8px;
  --r-pill:    999px;
}

/* ---------- Reset ---------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* In-page anchors scroll rather than teleport, so a jump up the page reads as
   movement and the reader can see where they landed. Off under
   prefers-reduced-motion, where the instant jump is the correct behaviour. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* .site-header is sticky and 73px tall, and nothing was offsetting for it, so
   every anchor target on the site landed with its top 73px underneath the
   header — #contact, #connect, #acronym, the four /research/#line-* links from
   the homepage atlas, and the netlify.toml redirects that point at them. Set on
   the scroll container so it applies to all of them at once rather than needing
   scroll-margin-top on each target. 96px is the header plus a little air. */
html { scroll-padding-top: 96px; }
body {
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; border: 0; background: none; color: inherit; }
h1, h2, h3, h4, p { margin: 0; }

/* ---------- Typography ---------- */
.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--saffron);
}
.eyebrow.on-light { color: var(--petrol); }

.display {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.02;
  text-wrap: balance;
}

.section-title {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.05;
  font-size: clamp(34px, 3.4vw, 48px);
  text-wrap: balance;
}

.body-lg {
  font-size: 16px;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 38ch;
}
.body-lg.on-dark { color: rgba(243, 235, 221, 0.82); }

.caption {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  height: 44px; padding: 0 22px;
  border-radius: var(--r-pill);
  font-family: var(--body);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.005em;
  transition: transform .15s ease, background .2s, border-color .2s, color .2s;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn .arrow { font-size: 15px; line-height: 1; }

/* Text is --ink, not near-white. Cream on the saffron fill measured 2.09:1 and
   failed AA; --ink on the same fill is 8.25:1. The hover darkens the gold rather
   than switching hue, so the dark text stays readable through the transition. */
.btn--primary {
  background: var(--saffron);
  color: var(--ink);
  box-shadow: 0 1px 0 rgba(0,0,0,0.06), 0 8px 24px -12px rgba(201,136,12,0.55);
}
.btn--primary:hover { background: #c9880c; }

.btn--secondary-light {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--ink);
}
.btn--secondary-light:hover { background: var(--ink); color: var(--paper); }

.btn--secondary-dark {
  background: transparent;
  color: var(--paper);
  border: 1px solid rgba(239,236,228,0.78);
}
.btn--secondary-dark:hover { background: rgba(239,236,228,0.78); color: var(--ink); }

.btn--ghost-link {
  height: auto; padding: 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--petrol);
  font-weight: 500;
  border-radius: 0;
}
.btn--ghost-link:hover { color: #16334a; }

/* ---------- Tags / Pills ---------- */
.tag {
  display: inline-flex; align-items: center;
  padding: 6px 14px;
  border-radius: var(--r-pill);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
}
.tag--dark {
  border: 1px solid rgba(239,236,228,0.5);
  color: rgba(239,236,228,0.78);
  background: rgba(255,255,255,0.02);
}
.tag--light {
  border: 1px solid var(--rule-strong);
  color: var(--ink-soft);
  background: rgba(255,253,248,0.6);
}
.tag--accent { color: var(--petrol); border-color: rgba(30,66,88,0.3); }

/* ---------- Layout ---------- */
.container {
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--pad-x);
}

section { position: relative; }

.dark-section {
  background: var(--ink);
  color: var(--paper);
  position: relative;
  overflow: hidden;
}

/* Subtle topographic noise for dark sections */
.dark-section::before {
  content: '';
  position: absolute; inset: 0;
  background-image:
    radial-gradient(rgba(247,241,230,0.04) 1px, transparent 1px),
    radial-gradient(rgba(247,241,230,0.03) 1px, transparent 1px);
  background-size: 28px 28px, 60px 60px;
  background-position: 0 0, 14px 30px;
  pointer-events: none;
}
.dark-section > * { position: relative; }

/* =========================================================
   FULL-BLEED BAND
   =========================================================
   A photograph run edge to edge as a section break, with its caption held to
   the container. Four of them: two on the home page, two on the research page.
   Every source is cropped to 2400x664 at authoring time by a script in tools/,
   so the ratio below only reserves space — it never crops. */
.bleed-band {
  margin: 0;
  display: block;
}
.bleed-band img {
  width: 100%;
  /* Not optional. The width/height attributes on the tag are there to reserve
     layout space before the image loads, but `height` is also a presentational
     hint, and at width:100% it wins over aspect-ratio — the band rendered at a
     fixed 664px tall on every viewport until this line was added. */
  height: auto;
  aspect-ratio: 2400 / 664;
  object-fit: cover;
  object-position: center;
  display: block;
}
.bleed-band__cap {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.6;
  color: var(--steel);
  padding-top: 12px;
  padding-bottom: 4px;
}

/* One band does not take the standard ratio: the Radio So'o bike frame is
   resized whole rather than cropped, because the horizon and the road are the
   subject and a 3.6:1 crop takes one of them. It is the only --tall on the
   site; if a second appears, this belongs in a shared rule rather than an
   exception. */
.bleed-band--tall img { aspect-ratio: 2400 / 1350; }

/* Narrow viewports get a squarer crop of the standard bands, so a 3.6:1 strip
   does not shrink to a 100px ribbon. The tall one is exempt: it is already
   16:9, which reads fine at 375px, and cropping it here would undo the whole
   reason it has its own ratio. */
@media (max-width: 720px) {
  .bleed-band:not(.bleed-band--tall) img { aspect-ratio: 2.2; }
}


/* =========================================================
   SKIP LINK
   ========================================================= */
/* base.njk renders this as the first element in <body>. It carried no rule at
   all until August 2026, so "Skip to content" sat as visible stray text above
   the header on every page, in production. Clipped rather than
   display:none — a hidden link is not focusable, which defeats the point.
   --paper on --ink is 15.50:1; the --saffron ring is on a dark surface, the
   only place that token is allowed. */
.skip-link {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.skip-link:focus,
.skip-link:focus-visible {
  position: fixed;
  top: 12px; left: var(--pad-x);
  width: auto; height: auto;
  margin: 0;
  padding: 10px 22px;
  overflow: visible;
  clip-path: none;
  /* .site-header is z-index 50; this has to clear it. */
  z-index: 100;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  border-radius: var(--r-pill);
  box-shadow: 0 4px 18px rgba(17,21,28,0.45);
  outline: 2px solid var(--saffron);
  outline-offset: 2px;
}

/* <main> carries tabindex="-1" so the skip link can land focus on it. It is
   never tab-reached, only skipped to, so the ring would be a full-page outline
   with nothing to indicate. */
#main-content:focus { outline: none; }

/* =========================================================
   HEADER
   ========================================================= */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(31,39,35,0.92);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  color: var(--paper);
  border-bottom: 1px solid rgba(243,235,221,0.06);
}
.site-header .container {
  display: flex; align-items: center;
  gap: 40px;
  padding-top: 14px; padding-bottom: 14px;
}

/* Right-hand cluster: language switch + primary CTA. */
.site-header__actions {
  display: flex; align-items: center;
  gap: 20px;
  margin-left: auto;
  flex-shrink: 0;
}

/* Language switch. Deliberately quiet — it is wayfinding, not a call to action.
   Labelled in the language it switches TO, which is the one convention that
   reliably works for people who cannot read the current page. */
.lang-switch {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(243,235,221,0.68);
  text-decoration: none;
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 1px solid rgba(243,235,221,0.28);
  transition: color .18s ease, border-color .18s ease;
}
.lang-switch:hover,
.lang-switch:focus-visible {
  color: var(--saffron);
  border-color: var(--saffron);
}

@media (max-width: 900px) {
  .site-header__actions { gap: 14px; }
  .lang-switch { font-size: 10px; letter-spacing: 0.1em; }
}

/* The header row did not fit a phone. Brand, a 40px gap, a nowrap "Get in
   touch" pill and the menu button came to more than 390px, so the row overflowed
   — which widened the whole document and cut every section off at the right
   edge, not just the header. These three rules are what let it fit: a smaller
   gap, a pill that gives up some padding, and a wordmark allowed to shrink. */
@media (max-width: 720px) {
  .site-header .container { gap: 12px; }
  .site-header__actions { gap: 8px; }
  .site-header .btn--primary {
    height: 40px;
    padding: 0 14px;
    font-size: 13px;
  }
  .brand { min-width: 0; }
  .brand__mark { width: 38px; height: 38px; }
}
@media (max-width: 400px) {
  /* Below this the pill's arrow is the first thing that can go without
     costing meaning. */
  .site-header .btn--primary .arrow { display: none; }
  .site-header .btn--primary { padding: 0 12px; }
}

.brand {
  display: flex; align-items: center; gap: 12px;
  flex-shrink: 0;
}
/* The mark IS the circle. It was tejido.png — the full logo, wordmark and all —
   sitting at 36px inside a 44px paper disc, which put the words "FRONTERAS
   Collective" at about 6px tall next to a wordmark that already says it. It is
   now brand-mark.png, the weave alone on its canopy ground, filling the disc
   edge to edge; the green that shows around the weave is the border. Replaced
   10 August 2026. --canopy behind it so a slow image load is the right colour
   rather than a pale disc that then turns green. */
.brand__mark {
  width: 44px; height: 44px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--canopy);
  display: grid; place-items: center;
  flex-shrink: 0;
}
.brand__mark img { width: 100%; height: 100%; object-fit: cover; display: block; }
.brand__wordmark {
  font-family: var(--display);
  font-size: 17px;
  line-height: 1;
  letter-spacing: 0.06em;
}
.brand__wordmark .b1 { display: block; font-weight: 500; }
.brand__wordmark .b2 {
  display: block;
  font-style: italic;
  font-size: 13px;
  color: var(--saffron);
  margin-top: 4px;
  letter-spacing: 0.04em;
}

.nav { display: flex; gap: 32px; flex: 1; justify-content: center; }

/* Top-level items only. This was `.nav a`, which now also matches the links
   inside the Research dropdown — they are a different thing and get their own
   rules below. */
.nav > a,
.nav__item > a {
  font-size: 13.5px;
  font-weight: 500;
  color: rgba(239,236,228,0.78);
  padding: 6px 0;
  position: relative;
  transition: color .15s;
}
.nav > a:hover,
.nav__item > a:hover { color: var(--paper); }
.nav > a::after,
.nav__item > a::after {
  content: '';
  position: absolute; left: 50%; bottom: -2px;
  width: 0; height: 1px;
  background: var(--saffron);
  transition: width .2s, left .2s;
}
.nav > a:hover::after,
.nav__item > a:hover::after { width: 100%; left: 0; }

/* ---- Dropdown ------------------------------------------------------------
   Nav items that carry children. The panel is positioned against .nav__item,
   so the item is the positioning context and the hover target both. */
.nav__item--has-sub {
  position: relative;
  display: flex; align-items: center;
  gap: 5px;
}

/* The chevron. A real <button> because it is a control, not decoration — it
   carries aria-expanded and is the keyboard path into the menu. */
.nav__toggle {
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  padding: 0;
  background: none; border: 0;
  border-radius: var(--r-card);
  color: rgba(239,236,228,0.78);
  cursor: pointer;
  transition: color .15s;
}
.nav__toggle:hover { color: var(--paper); }
.nav__toggle:focus-visible { outline: 2px solid var(--saffron); outline-offset: 2px; }
.nav__chev {
  width: 6px; height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .2s;
}
.nav__item--has-sub:hover .nav__chev,
.nav__item--has-sub[data-open="true"] .nav__chev {
  transform: translateY(1px) rotate(-135deg);
}
@media (prefers-reduced-motion: reduce) {
  .nav__chev { transition: none; }
}

/* Closed state is visibility, not display, so the panel can transition — and
   visibility:hidden takes it out of the tab order and the accessibility tree,
   which display:none-by-another-name would not if we faked it with opacity
   alone. */
.nav__sub {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  transform: translateX(-50%);
  min-width: 190px;
  margin: 0; padding: 8px;
  list-style: none;
  display: flex; flex-direction: column;
  background: rgba(31,39,35,0.98);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(239,236,228,0.14);
  border-radius: var(--r-card-lg);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,0.6);
  visibility: hidden;
  opacity: 0;
  transition: opacity .16s, visibility .16s;
}

/* The gap between the item and the panel is dead space that would close the
   menu as the pointer crosses it. This bridges it without drawing anything. */
.nav__item--has-sub::after {
  content: '';
  position: absolute;
  top: 100%; left: 0; right: 0;
  height: 14px;
}

/* Hover opens it for pointer users with no script. Guarded on a real hovering
   pointer so a touch tap does not leave a menu stuck open. */
@media (hover: hover) and (pointer: fine) {
  .nav__item--has-sub:hover .nav__sub { visibility: visible; opacity: 1; }
}
/* Keyboard and touch path, set by nav-dropdown.js. */
.nav__item--has-sub[data-open="true"] .nav__sub { visibility: visible; opacity: 1; }

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

.nav__sub a {
  display: block;
  padding: 9px 12px;
  font-size: 13.5px;
  font-weight: 500;
  color: rgba(239,236,228,0.82);
  border-radius: var(--r-card);
  white-space: nowrap;
  transition: background .15s, color .15s;
}
.nav__sub a:hover { background: rgba(239,236,228,0.08); color: var(--paper); }
.nav__sub a:focus-visible { outline: 2px solid var(--saffron); outline-offset: -2px; }

/* =========================================================
   IN-PAGE SECTION NAV
   =========================================================
   A sticky map of a long page, sitting under the site header. Added for
   /research/ on 12 August 2026: that page runs to about 26 screens, so the
   sections carrying the current work and the ethics of it were past the point
   most people scroll. The header dropdown gets someone in at the right
   altitude; this is what tells them where they landed and what else is here.

   It lives here rather than in a pages/*.css because it is on two pages now —
   /research/ since 12 August and the homepage since 13 August, where "Let's
   connect" sits nine screens down on desktop and thirteen on a phone. It was
   `.r-secnav` in pages/research.css until then; the rename is what stops the
   homepage from carrying a second copy of the same bar under a research name.

   Two things each page still owns, because they depend on the page rather than
   on the component:
     - `html { scroll-padding-top }`, which has to clear the header AND this
       bar. Set to 142px in both research.css and landing.css.
     - Any other sticky element that now has a second bar above it. See
       .acronym__copy in landing.css.

   Deliberately NOT the dark of the site header. It belongs to the page, not to
   the site chrome, and a second dark bar directly under a dark header reads as
   one thick band of furniture. */
.secnav {
  position: sticky;
  top: 73px;                    /* .site-header is sticky and 73px tall */
  z-index: 40;                  /* under the header's 50, over the sections */
  background: var(--paper-2);
  border-bottom: 1px solid var(--rule);
}
.secnav__inner {
  display: flex;
  gap: 4px;
  overflow-x: auto;             /* four items do not fit a narrow phone */
  scrollbar-width: none;
  padding-top: 0; padding-bottom: 0;
}
.secnav__inner::-webkit-scrollbar { display: none; }

/* Mono, small, uppercase — the same field-notebook register the eyebrows and
   coordinates use, so this reads as an index rather than a second navbar
   competing with the real one above it. */
.secnav__link {
  position: relative;
  flex-shrink: 0;
  padding: 15px 14px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color .15s;
}

/* --- Why the links alternate, and why in THESE two colours ---------------- */
/* Joel asked on 13 August 2026 for the links to alternate so the bar separates
   itself from the page it sits on. The colours he named were --saffron and
   --petrol-lift, and both were measured before anything was written:

                        on --paper-2 (this bar)   on --ink
       --saffron                1.73:1              8.25:1
       --petrol-lift            1.72:1              8.30:1

   Both are dark-surface colours and neither is usable here — 1.7:1 is the
   failure named at the top of this file, not a near miss. They would have
   needed the bar to go dark, which was the other option on the table and was
   not the one taken. So the alternation runs in the two colours that DO carry
   on a light ground:

       --petrol                 8.28:1
       --ink                   14.25:1

   It reads as blue against near-black rather than gold against blue, and that
   is the deliberate cost of keeping the bar light. There is no light-surface
   warm accent on this site to reach for: --saffron-ink was deleted in July 2026
   and is not to be reintroduced.

   nth-child, not hand-applied classes: the three bars carry four, five and six
   links, and the alternation has to survive one being added. Odd starts on
   --petrol so the first link is the accent on every page. */
.secnav__link:nth-child(odd)  { color: var(--petrol); }
.secnav__link:nth-child(even) { color: var(--ink); }

.secnav__link:hover { background-color: rgba(17, 21, 28, 0.05); }
.secnav__link:focus-visible { outline: 2px solid var(--petrol); outline-offset: -2px; }

/* Active section. NOTE WHAT CHANGED: colour used to carry this — the bar was
   uniformly --steel and the active link went --petrol. It cannot any more,
   because colour now carries identity (which link) rather than state (which
   section you are in). So state moved onto two signals that are not colour: a
   ground wash, and the 2px rule beneath. Both are needed. The wash alone is
   weak at this type size, and the rule alone was already judged too thin to be
   the only signal back when it was merely reinforcing a colour change.

   currentColor on the rule, so it takes each link's own colour rather than
   pinning every active state to --petrol and half-undoing the alternation. */
.secnav__link.is-active { background-color: rgba(17, 21, 28, 0.075); }
.secnav__link.is-active::after {
  content: '';
  position: absolute;
  left: 14px; right: 14px; bottom: -1px;
  height: 2px;
  background: currentColor;
}

@media (max-width: 720px) {
  .secnav__link { padding: 13px 12px; font-size: 10px; }
  .secnav__link.is-active::after { left: 12px; right: 12px; }
}

/* =========================================================
   HERO
   ========================================================= */
.hero {
  background: var(--ink);
  color: var(--paper);
  padding: 56px 0 72px;
  position: relative;
  overflow: hidden;
}
.hero__grid {
  display: grid;
  grid-template-columns: minmax(420px, 1.05fr) 1.5fr;
  gap: 48px;
  align-items: start;
}
.hero__left { padding-top: 24px; max-width: 540px; }
.hero__eyebrow { margin-bottom: 22px; }
.hero__title {
  font-size: clamp(56px, 6.2vw, 84px);
  margin-bottom: 24px;
}
.hero__title em {
  font-style: italic;
  color: var(--saffron);
}
.hero__body {
  font-size: 16px;
  line-height: 1.5;
  color: rgba(243,235,221,0.82);
  max-width: 38ch;
  margin-bottom: 32px;
}
.hero__cta { display: flex; gap: 12px; margin-bottom: 36px; flex-wrap: wrap; }

.hero__stats {
  display: flex; gap: 0;
  border: 1px solid rgba(239,236,228,0.18);
  border-radius: 24px;
  padding: 16px 4px;
  background: rgba(255,255,255,0.02);
  max-width: 480px;
}
.hero__stat {
  flex: 1;
  display: flex; align-items: center; gap: 12px;
  padding: 4px 16px;
  position: relative;
}
.hero__stat + .hero__stat::before {
  content: ''; position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 1px; background: rgba(239,236,228,0.18);
}
.hero__stat__big {
  display: block;
  font-family: var(--display);
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--saffron);
}
.hero__stat:nth-child(2) .hero__stat__big,
.hero__stat:nth-child(3) .hero__stat__big { color: var(--paper); }
.hero__stat__lbl {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(243,235,221,0.55);
  margin-top: 6px;
  line-height: 1.25;
}

/* Hero video tile */
.hero__right {
  position: relative;
  min-height: 654px;
}
.hero__video {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 654px;
  border-radius: var(--r-card-lg);
  overflow: hidden;
  background: #2a3027;
  border: 1px solid rgba(243,235,221,0.08);
  box-shadow: 0 24px 60px -28px rgba(0,0,0,0.55);
}
.hero__video__media,
.hero__video__placeholder {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.hero__video__placeholder { z-index: 0; }
.hero__video__placeholder img { width:100%; height:100%; object-fit: cover; }
.hero__video__media { z-index: 1; background: transparent; }
.hero__video__media:not([src]),
.hero__video__media[src=""] { display: none; }
/* Subtle vignette so label/play sits clean over imagery */
.hero__video::after {
  content: '';
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(15,20,17,0) 50%, rgba(15,20,17,0.55) 100%),
    linear-gradient(180deg, rgba(15,20,17,0.25) 0%, rgba(15,20,17,0) 30%);
  pointer-events: none;
  z-index: 2;
}
.hero__video__play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 76px; height: 76px;
  border-radius: 50%;
  background: rgba(247,241,230,0.92);
  color: var(--ink);
  display: grid; place-items: center;
  z-index: 3;
  box-shadow: 0 12px 32px -12px rgba(0,0,0,0.45);
  transition: transform .2s ease, background .2s ease;
}
.hero__video__play:hover { transform: translate(-50%, -50%) scale(1.04); background: var(--paper); }
.hero__video__play svg { margin-left: 3px; }
.hero__video.is-playing .hero__video__play { opacity: 0; pointer-events: none; }
.hero__video__label {
  position: absolute;
  left: 24px; bottom: 22px;
  z-index: 3;
  display: flex; flex-direction: column; gap: 4px;
  color: var(--paper);
}
.hero__video__label__kind {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(243,235,221,0.78);
}
.hero__video__label__sub {
  font-family: var(--mono);
  font-size: 11px;
  color: rgba(243,235,221,0.55);
}
.hero__video__label__sub code {
  font-family: var(--mono);
  background: rgba(247,241,230,0.08);
  padding: 1px 6px;
  border-radius: 4px;
  color: var(--paper);
}

/* =========================================================
   IMPACT STATS BAR
   ========================================================= */
.impact-bar {
  background: var(--paper-2);
  padding: 36px 0;
  border-bottom: 1px solid var(--rule);
}
.impact-bar__row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}
.impact-stat {
  display: flex; align-items: center; gap: 16px;
  padding: 18px 24px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
}
.impact-stat__icon {
  width: 48px; height: 48px;
  border-radius: 50%;
  border: 1px solid var(--rule-strong);
  display: grid; place-items: center;
  color: var(--ink-soft);
  flex-shrink: 0;
}
.impact-stat__num {
  font-family: var(--display);
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--ink);
}
.impact-stat__lbl {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-top: 6px;
}
.impact-stat--copy {
  align-items: flex-start;
  padding: 22px 22px;
}
.impact-stat__copy {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
}

/* =========================================================
   FIELD ATLAS
   ========================================================= */
.atlas {
  padding: 96px 0;
  background: var(--paper);
}
.atlas__grid {
  display: grid;
  grid-template-columns: 1fr 2.2fr;
  gap: 56px;
  align-items: start;
}
.atlas__title { max-width: 340px; }
/* Framing paragraph under the section heading. Carried over from the method
   section when that was removed, so this column now does the work that section
   used to.

   --ink-soft, not --steel. This paragraph shares a section with the woven
   corner mark on the collective page, and --steel only clears AA on bare paper —
   5.38:1, with very little to give. Over a watermarked ground it dropped to
   3.30-3.86:1 and the text became hard to read, which is what Joel reported.
   --ink-soft measures 11.07:1 on bare paper and 6.53:1 over the weave, so it
   holds either way.

   These two are coupled: strengthening the mark eats this paragraph's headroom.
   Re-measure against .atlas::before in collective.css before raising any of its
   opacities.

   Only the collective page uses this class — the homepage's equivalent is
   .atlas-intro, one hyphen, a different rule. */
.atlas__intro {
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--ink-soft);
  margin-top: 18px;
}
.atlas__cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
.atlas-card {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  padding: 24px 22px;
  min-height: 230px;
  display: flex; flex-direction: column; gap: 14px;
  transition: transform .2s ease, box-shadow .2s ease;
}
.atlas-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 18px 40px -24px rgba(48,39,27,0.18);
}
.atlas-card__icon {
  width: 36px; height: 36px;
  color: var(--petrol);
  display: grid; place-items: center;
}
.atlas-card__title {
  font-family: var(--display);
  font-size: 20px;
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-wrap: balance;
}
.atlas-card__body {
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* =========================================================
   VISUAL LANGUAGE
   ========================================================= */
.visual {
  background: var(--ink);
  color: var(--paper);
  padding: 80px 0;
  overflow: hidden;
  position: relative;
}
.visual__container {
  position: relative;
  border-radius: 36px;
  overflow: hidden;
  background: linear-gradient(135deg, #232b27 0%, #161c19 100%);
  min-height: 540px;
  padding: 56px;
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 40px;
  align-items: start;
  border: 1px solid rgba(243,235,221,0.06);
}
.visual__left { max-width: 420px; }
.visual__title {
  font-family: var(--display);
  font-size: clamp(34px, 3.2vw, 44px);
  line-height: 1.05;
  letter-spacing: -0.015em;
  font-weight: 400;
  margin-bottom: 20px;
  text-wrap: balance;
}
.visual__body {
  font-size: 14px;
  line-height: 1.55;
  color: rgba(243,235,221,0.72);
  margin-bottom: 28px;
  max-width: 38ch;
}
.visual__tags { display: flex; flex-wrap: wrap; gap: 8px; }

.visual__image {
  position: absolute;
  inset: 0;
  left: 28%;
  border-radius: 0 36px 36px 0;
  overflow: hidden;
}
.visual__image::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(to right, #161c19 0%, rgba(22,28,25,0.4) 28%, transparent 52%);
}
.visual__image img { width: 100%; height: 100%; object-fit: cover; }

.visual__motif-card {
  position: absolute;
  right: 32px;
  bottom: 32px;
  width: 280px;
  padding: 22px;
  border-radius: 20px;
  background: rgba(31,39,35,0.78);
  border: 1px solid rgba(243,235,221,0.18);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  z-index: 2;
}
.visual__motif-card__head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 12px;
}
.visual__motif-card__head svg {
  width: 28px; height: 28px;
  color: var(--saffron);
}
.visual__motif-card__title {
  font-family: var(--display);
  font-style: italic;
  font-size: 16px;
  color: var(--paper);
}
.visual__motif-card__body {
  font-size: 12px;
  line-height: 1.5;
  color: rgba(243,235,221,0.72);
}

/* =========================================================
   PROJECTS
   ========================================================= */
.projects {
  padding: 96px 0;
  background: var(--paper);
}
.projects__head {
  text-align: center;
  max-width: 720px;
  margin: 0 auto 56px;
}
.projects__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
.project-card {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-card-lg);
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: transform .2s ease, box-shadow .2s ease;
}
.project-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 22px 48px -28px rgba(48,39,27,0.22);
}
.project-card__media {
  aspect-ratio: 4 / 3;
  background: #d8cfb8;
  overflow: hidden;
}
.project-card__media img,
.project-card__media .placeholder { width: 100%; height: 100%; object-fit: cover; }
.project-card__body { padding: 22px 24px 26px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.project-card__tags { display: flex; gap: 8px; flex-wrap: wrap; }
.project-card__title {
  font-family: var(--display);
  font-size: 22px;
  font-weight: 500;
  line-height: 1.18;
  letter-spacing: -0.012em;
  color: var(--ink);
}
.project-card__excerpt {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  flex: 1;
}

/* =========================================================
   PUBLICATIONS
   ========================================================= */
.publications {
  background: var(--ink);
  color: var(--paper);
  padding: 96px 0;
}
.publications__head {
  font-family: var(--display);
  font-size: clamp(28px, 2.8vw, 36px);
  line-height: 1.1;
  letter-spacing: -0.012em;
  margin-bottom: 40px;
  max-width: 460px;
}
.publications__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 24px;
  align-items: start;
}
.publications__books {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
.book-card {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(243,235,221,0.12);
  border-radius: 22px;
  padding: 16px;
  display: flex; flex-direction: column; gap: 16px;
  min-height: 290px;
  transition: background .2s ease;
}
.book-card:hover { background: rgba(255,255,255,0.07); }
.book-card__cover {
  aspect-ratio: 3 / 4;
  border-radius: 10px;
  overflow: hidden;
  background: #1c221a;
  position: relative;
}
/* These are reproductions of published covers, so they are letterboxed rather
   than cropped — the three arrive at different aspect ratios and `cover` would
   slice the titles off. */
.book-card__cover img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.book-card__meta { display: flex; flex-direction: column; gap: 8px; }
.book-card__kind {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(243,235,221,0.55);
}
.book-card__title {
  font-family: var(--display);
  font-size: 17px;
  line-height: 1.18;
  letter-spacing: -0.01em;
  color: var(--paper);
}

/* The whole card is clickable, but only the title carries the link.

   The alternative was wrapping the entire <article> in an <a>, which is what the
   Radio So'o notes do — but those have a two-line title and nothing else, while
   these carry a cover, a kind, a title, a sentence, and a year. Wrapping them
   would give a screen reader a link named by all five, read as one run-on
   string. Here the link is named by the title alone and the ::after below
   stretches it over the card, so the cover is clickable without the accessible
   name changing. .book-card:hover already existed, so the affordance was
   promised before it was real. */
.book-card { position: relative; }

.book-card__title a {
  color: inherit;
  text-decoration: none;
}

.book-card__title a::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* .book-card__cover is position: relative, so without this the cover paints
     over the overlay and the image — the thing Joel asked to be clickable —
     would not be. */
  z-index: 1;
}

.book-card__title a:hover { text-decoration: underline; }

/* Keyboard focus lands on the link, which is inline and would otherwise draw a
   ring around the words only. Drawn on the card instead, since the card is what
   the click target actually is. */
.book-card:focus-within {
  outline: 2px solid var(--saffron);
  outline-offset: 3px;
}

.book-card__title a:focus { outline: none; }
.book-card__title a:focus-visible { text-decoration: underline; }
.book-card__sub {
  font-size: 12px;
  line-height: 1.5;
  color: rgba(243,235,221,0.6);
}
.book-card__year {
  margin-top: auto;
  font-family: var(--mono);
  font-size: 10.5px;
  color: rgba(243,235,221,0.45);
  letter-spacing: 0.1em;
}

.publications__photo {
  border-radius: 22px;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background: #2a3028;
  position: relative;
}
.publications__photo img { width: 100%; height: 100%; object-fit: cover; }

.publications__cta { margin-top: 28px; }

/* =========================================================
   CREDIBILITY
   ========================================================= */
.credibility { padding: 96px 0; background: var(--paper); }
.credibility__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: start;
}
.credibility__left { max-width: 500px; }
.credibility__title { margin-top: 12px; margin-bottom: 32px; }
.award-card {
  display: flex; gap: 18px;
  padding: 22px 24px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  margin-bottom: 12px;
}
.award-card__mark {
  width: 54px; height: 54px;
  border-radius: 50%;
  border: 1px solid var(--rule-strong);
  display: grid; place-items: center;
  color: var(--ink-soft);
  font-family: var(--display);
  font-size: 22px;
  flex-shrink: 0;
  background: var(--paper-2);
}
.award-card__body { display: flex; flex-direction: column; gap: 6px; }
.award-card__title {
  font-family: var(--body);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.01em;
}
.award-card__sub {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}

.credibility__right { display: flex; gap: 24px; align-items: flex-start; }
.credibility__metric {
  display: flex; flex-direction: column; gap: 6px;
  min-width: 130px; padding-top: 4px;
}
.credibility__metric__num {
  font-family: var(--display);
  font-size: 64px;
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.credibility__metric__lbl {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  line-height: 1.45;
}
.credibility__photo {
  flex: 1;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-card-lg);
  overflow: hidden;
  background: #d8cfb8;
}
.credibility__photo img { width: 100%; height: 100%; object-fit: cover; }

/* =========================================================
   FIELD NOTES
   ========================================================= */
.fieldnotes { padding: 96px 0 80px; background: var(--paper); }
.fieldnotes__head {
  display: flex; justify-content: space-between; align-items: end;
  margin-bottom: 36px; gap: 32px;
}
.fieldnotes__grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 24px;
}
.fieldnote {
  display: flex; gap: 20px;
  padding: 18px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--r-card-lg);
}
.fieldnote__media {
  width: 180px; height: 180px;
  border-radius: 18px;
  overflow: hidden;
  background: #c8bfa5;
  flex-shrink: 0;
}
.fieldnote__media img { width: 100%; height: 100%; object-fit: cover; }
.fieldnote__body { display: flex; flex-direction: column; gap: 10px; padding: 6px 8px 6px 0; }
.fieldnote__meta {
  display: flex; gap: 12px; align-items: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--saffron);
}
.fieldnote__meta .dot { color: var(--ink-soft); }
.fieldnote__title {
  font-family: var(--display);
  font-size: 19px;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.fieldnote__excerpt {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-soft);
}
.fieldnote__link {
  margin-top: auto;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--saffron);
}

/* =========================================================
   CTA BAND
   ========================================================= */
.cta-band {
  background: var(--ink);
  color: var(--paper);
  padding: 100px 0;
  text-align: center;
}
.cta-band__title {
  font-family: var(--display);
  font-size: clamp(36px, 4vw, 56px);
  line-height: 1.05;
  letter-spacing: -0.018em;
  max-width: 18ch;
  margin: 0 auto 18px;
  font-weight: 400;
}
.cta-band__sub {
  font-size: 15px;
  color: rgba(243,235,221,0.7);
  max-width: 56ch;
  margin: 0 auto 32px;
}
.cta-band__buttons { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* --- Woven braid behind a CTA band -------------------------------------- */
/* Opt-in per page with .cta-band--weave. Every page carries it except the
   collective, which stays plain.

   Two things about this one are not interchangeable with the light-surface
   marks, and both follow from the band being --ink:

   1. The artwork is drawn in --petrol-lift. Plain --petrol measures 1.72:1 on
      --ink and would be invisible — the same trap documented at the top of this
      file. weave-band-dark.svg also draws its over-under casings in --ink
      rather than --paper, or every crossing would flash a light notch.

   2. It runs at 0.22, not the 0.30 the light-surface marks use. .cta-band__sub
      is rgba(243,235,221,0.7), already down at 7.99:1 on bare --ink before
      anything sits behind it. At 0.30 the braid drops it to 4.34:1 and it fails
      AA for body text; 0.22 holds 5.24:1. The title, being full --paper, has
      room to spare either way at 10.17:1. Do not raise this without
      re-measuring against the sub-copy, which is the binding constraint. */
.cta-band--weave::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: url('/assets/img/weave-band-dark.svg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0.22;
}

/* =========================================================
   FOOTER
   ========================================================= */
.site-footer {
  background: var(--ink);
  color: rgba(239,236,228,0.78);
  padding: 56px 0 24px;
  border-top: 1px solid rgba(243,235,221,0.08);
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.4fr repeat(3, 1fr) 1.4fr;
  gap: 32px;
  margin-bottom: 40px;
}
.footer__brand { display: flex; flex-direction: column; gap: 18px; }
.footer__brand .brand__wordmark { color: var(--paper); }
.footer__brand p {
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(243,235,221,0.55);
  max-width: 28ch;
}
.footer__col h2 {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(243,235,221,0.45);
  margin-bottom: 14px;
  font-weight: 500;
}
.footer__col ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.footer__col a {
  font-size: 13px;
  color: rgba(239,236,228,0.78);
  transition: color .15s;
}
.footer__col a:hover { color: var(--saffron); }

.footer__contact .contact-line {
  font-size: 13px;
  color: rgba(239,236,228,0.78);
  margin-bottom: 8px;
}
.footer__contact .contact-cta {
  margin-top: 10px;
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--saffron);
}

.footer__bottom {
  display: flex; justify-content: space-between; align-items: center;
  padding-top: 22px;
  border-top: 1px solid rgba(243,235,221,0.08);
  font-size: 11.5px;
  color: rgba(243,235,221,0.45);
}
/* These three links are words — "Bluesky", "LinkedIn", "Scholar" — not icons.
   They were sized as 22x22 icon boxes, so each label overflowed its box and ran
   into the next one. Let the text set its own width and give the row a gap wide
   enough to read as three separate links. */
.footer__bottom__socials { display: flex; flex-wrap: wrap; gap: 22px; }
.footer__bottom__socials a {
  color: rgba(243,235,221,0.5);
  transition: color .15s;
  white-space: nowrap;
}
.footer__bottom__socials a:hover { color: var(--saffron); }
.footer__bottom__links { display: flex; gap: 28px; }
.footer__bottom__links a { color: rgba(243,235,221,0.5); }
.footer__bottom__links a:hover { color: var(--paper); }

/* =========================================================
   Striped image placeholder (fallback for missing photos)
   ========================================================= */
.placeholder {
  width: 100%; height: 100%;
  background:
    repeating-linear-gradient(135deg,
      rgba(120, 90, 60, 0.16) 0 10px,
      rgba(120, 90, 60, 0.06) 10px 20px),
    linear-gradient(180deg, #d8c9a8 0%, #c3b187 100%);
  display: grid; place-items: center;
  position: relative;
  color: #5d4f33;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  padding: 14px;
}
.placeholder::after {
  content: attr(data-label);
  display: block;
  max-width: 80%;
}

/* =========================================================
   Responsive
   ========================================================= */
@media (max-width: 1100px) {
  .hero__grid { grid-template-columns: 1fr; }
  .hero__right { min-height: 420px; }
  .hero__video { min-height: 420px; }
  .atlas__grid { grid-template-columns: 1fr; gap: 32px; }
  .atlas__cards { grid-template-columns: repeat(2, 1fr); }
  .impact-bar__row { grid-template-columns: repeat(2, 1fr); }
  .projects__grid { grid-template-columns: 1fr 1fr; }
  .publications__grid { grid-template-columns: 1fr; }
  .publications__photo { aspect-ratio: 16 / 9; }
  .credibility__grid { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: repeat(3, 1fr); }
  .visual__container { grid-template-columns: 1fr; }
  .visual__image { display: none; }
}
/* =========================================================
   MOBILE MENU
   A <details> disclosure. Hidden above 720px, where .nav does the work; below
   it, .nav is display:none and this takes over. Only one of the two is ever
   rendered, so there is a single nav landmark at any width.
   ========================================================= */
.nav-m { display: none; position: relative; }

.nav-m__btn {
  list-style: none;                 /* no default disclosure triangle */
  width: 44px; height: 44px;        /* a real tap target, not a 24px icon */
  display: grid; place-items: center;
  cursor: pointer;
  border-radius: var(--r-card);
  border: 1px solid rgba(239,236,228,0.28);
}
.nav-m__btn::-webkit-details-marker { display: none; }
.nav-m__btn:hover { border-color: rgba(239,236,228,0.55); }
.nav-m__btn:focus-visible { outline: 2px solid var(--saffron); outline-offset: 2px; }

/* Three bars from one element: the middle is the box, the outer two are its
   before and after. */
.nav-m__bars,
.nav-m__bars::before,
.nav-m__bars::after {
  display: block;
  width: 18px; height: 2px;
  background: var(--paper);
  border-radius: 2px;
}
.nav-m__bars { position: relative; }
.nav-m__bars::before,
.nav-m__bars::after { content: ''; position: absolute; left: 0; }
.nav-m__bars::before { top: -6px; }
.nav-m__bars::after  { top: 6px; }

/* Open: the bars become a cross, so the control says how to close itself. */
.nav-m[open] .nav-m__bars { background: transparent; }
.nav-m[open] .nav-m__bars::before { top: 0; transform: rotate(45deg); }
.nav-m[open] .nav-m__bars::after  { top: 0; transform: rotate(-45deg); }
@media (prefers-reduced-motion: no-preference) {
  .nav-m__bars, .nav-m__bars::before, .nav-m__bars::after {
    transition: transform .18s ease, top .18s ease, background .18s ease;
  }
}

/* The panel hangs off the header rather than pushing it taller — the header is
   sticky, and a second permanent row would cost that height on every screen. */
.nav-m__panel {
  position: absolute;
  top: calc(100% + 14px);
  right: 0;
  min-width: 208px;
  display: flex; flex-direction: column;
  padding: 8px;
  background: rgba(31,39,35,0.98);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(239,236,228,0.14);
  border-radius: var(--r-card-lg);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,0.6);
}
.nav-m__panel a {
  padding: 12px 14px;               /* 44px-ish rows, thumb-sized */
  font-size: 15px;
  font-weight: 500;
  color: rgba(239,236,228,0.82);
  border-radius: var(--r-card);
  white-space: nowrap;
}
.nav-m__panel a:hover { background: rgba(239,236,228,0.08); color: var(--paper); }
.nav-m__panel a[aria-current="page"] { color: var(--saffron); }

/* Sub-items under a parent that has children. Indented and set in the mono at
   a smaller size so the panel still reads as a list of four destinations with
   detail under one of them, rather than eight equal ones. The rule on the left
   carries the indent visually — without it the indent alone reads as a
   mistake at this size. */
.nav-m__sub {
  list-style: none;
  margin: 2px 0 6px;
  padding: 0 0 0 14px;
  border-left: 1px solid rgba(239,236,228,0.16);
  margin-left: 14px;
}
.nav-m__sub a {
  padding: 10px 12px;               /* still thumb-sized */
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: rgba(239,236,228,0.66);
}

@media (max-width: 720px) {
  .nav { display: none; }
  .nav-m { display: block; }
  .hero__right { min-height: 320px; }
  .hero__video { min-height: 320px; }
  .atlas__cards { grid-template-columns: 1fr; }
  .projects__grid { grid-template-columns: 1fr; }
  .fieldnotes__grid { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .fieldnote { flex-direction: column; }
  .fieldnote__media { width: 100%; height: 200px; }
}


/* =========================================================
   DECORATIVE OVERLAY LAYERS — topographic lines + plant outlines
   Layered behind content in dark sections, on top of images, and
   tucked into corners of cream sections. Toggled via body classes.
   ========================================================= */

/* Hosts must clip overlays to their bounds */
.cta-band { position: relative; overflow: hidden; }
.credibility { position: relative; overflow: hidden; }
.fieldnotes { position: relative; overflow: hidden; }
.publications { position: relative; overflow: hidden; }
.atlas { position: relative; overflow: hidden; }

/* Make sure content stacks above overlays */
.hero .container,
.visual .container,
.cta-band .container,
.credibility .container,
.fieldnotes .container,
.publications .container,
.atlas .container {
  position: relative;
  z-index: 1;
}

/* --- Topographic background layer --- */
.layer-topo {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('/assets/img/overlay-topo.svg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity 280ms ease;
  z-index: 0;
  mix-blend-mode: screen; /* lifts the bone color on dark bgs */
}
/* On light/cream sections, swap blend mode and use a darker bone stroke */
.layer-topo.on-light {
  mix-blend-mode: multiply;
  filter: invert(1) sepia(0.4) saturate(0.7) brightness(0.8);
}

body.show-topo .layer-topo { opacity: calc(var(--topo-opacity, 0.42)); }
body.show-topo .layer-topo.on-light { opacity: calc(var(--topo-opacity, 0.42) * 0.45); }

/* Variant alignments — push the topo origin around so it doesn't
   repeat the same composition in every section */
.layer-topo--top-left  { background-position: 0% 0%; }
.layer-topo--top-right { background-position: 100% 0%; }
.layer-topo--bottom-l  { background-position: 0% 100%; }
.layer-topo--bottom-r  { background-position: 100% 100%; }
.layer-topo--center    { background-position: 50% 50%; }

/* --- Plant outline accents --- */
.layer-plant {
  position: absolute;
  pointer-events: none;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  opacity: 0;
  transition: opacity 320ms ease;
  z-index: 0;
}
body.show-plants .layer-plant { opacity: calc(var(--plant-opacity, 0.55)); }

.layer-plant--frond {
  background-image: url('/assets/img/overlay-frond.svg');
}
.layer-plant--monstera {
  background-image: url('/assets/img/overlay-monstera.svg');
}
.layer-plant--palm {
  background-image: url('/assets/img/overlay-palm.svg');
}

/* Plants on cream backgrounds — invert to a darker bone tone */
.layer-plant.on-light {
  filter: invert(1) sepia(0.5) saturate(0.6) brightness(0.78);
}
body.show-plants .layer-plant.on-light { opacity: calc(var(--plant-opacity, 0.55) * 0.5); }

/* Placement utilities for plants */
.layer-plant--br { right: -40px;  bottom: -30px; width: 280px; height: 380px; transform: rotate(8deg); }
.layer-plant--bl { left: -40px;   bottom: -30px; width: 280px; height: 380px; transform: scaleX(-1) rotate(8deg); }
.layer-plant--tr { right: -60px;  top: -40px;    width: 260px; height: 360px; transform: rotate(180deg); }
.layer-plant--tl { left: -60px;   top: -40px;    width: 260px; height: 360px; transform: scale(-1, -1); }

.layer-plant--palm.layer-plant--br { width: 360px; height: 380px; bottom: -120px; right: -80px; transform: rotate(-15deg); }
.layer-plant--palm.layer-plant--bl { width: 360px; height: 380px; bottom: -120px; left: -80px;  transform: scaleX(-1) rotate(-15deg); }

/* --- Topographic overlay on imagery --- */
.image-topo {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url('/assets/img/overlay-image-topo.svg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 280ms ease;
  z-index: 2;
}
body.show-image-topo .image-topo { opacity: calc(var(--topo-opacity, 0.55) * 1.3); }

/* Ensure image containers can host the overlay */
.hero__video,
.visual__image,
.project-card__media,
.fieldnote__media,
.credibility__photo {
  position: relative;
}
