/* ============================================================================
   From Seed to Shelter — stylesheet
   Plain CSS. No build step. Single file by design: with no bundler, every
   extra stylesheet is another blocking request on a poor connection.

   Order: fonts · tokens · reset · base · layout · components · utilities
   ========================================================================== */


/* ----------------------------------------------------------------------------
   1. Fonts

   Self-hosted, latin subset, from Google Fonts. Both faces are SIL Open Font
   License, so redistribution here is permitted. Total 93KB for all four files,
   cached once across all five pages.

     jost-var.woff2            27KB   variable, covers 300–400 in one file
     spectral-300.woff2        22KB
     spectral-400.woff2        21KB
     spectral-400-italic.woff2 22KB

   font-display: swap — text is readable in the fallback face immediately
   rather than invisible while the font downloads. That matters more than
   the reflow on a slow connection.
---------------------------------------------------------------------------- */

/* Jost is a variable font. One file serves both weights; the range below lets
   the browser interpolate 300 and 400 rather than synthesising either. */
@font-face {
  font-family: "Jost";
  src: url("../fonts/jost-var.woff2") format("woff2-variations"),
       url("../fonts/jost-var.woff2") format("woff2");
  font-weight: 300 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Spectral";
  src: url("../fonts/spectral-300.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Spectral";
  src: url("../fonts/spectral-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Real italic, so the serif is never synthetically obliqued. */
@font-face {
  font-family: "Spectral";
  src: url("../fonts/spectral-400-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}


/* ----------------------------------------------------------------------------
   2. Tokens
---------------------------------------------------------------------------- */

:root {

  /* --- Colour: brand ---------------------------------------------------- */
  --slate:        #324353;   /* primary brand; dark panels, headings on light */
  --deep-slate:   #1F2B36;   /* footer, occasional full-bleed panel           */
  --stone:        #5A6875;   /* secondary text, hairlines, captions           */
  --mist:         #F2F5F6;   /* page background — cool, never cream           */
  --paper:        #FFFFFF;   /* cards, content surfaces                       */
  --radish:       #B04A63;   /* accent — links, eyebrows, one figure/section  */

  /* Dark-background tints. NOT in the original palette — added because
     Radish on Slate measures 1.94:1 and Stone on Slate 1.78:1, both far
     below WCAG AA. Same hues, lifted to a usable lightness:
       Radish light on Slate      4.91:1
       Radish light on Deep slate 6.95:1
       Stone light  on Slate      4.83:1                                     */
  --radish-light: #E9A0B0;
  --stone-light:  #A9B4BE;

  /* --- Colour: semantic (light sections; overridden on dark) ------------- */
  --bg:           var(--mist);
  --bg-raised:    var(--paper);
  --ink:          var(--slate);     /* body copy — 9.29:1 on Mist            */
  --ink-muted:    var(--stone);     /* captions, meta — 5.22:1 on Mist       */
  --accent:       var(--radish);    /* 4.79:1 on Mist, 5.24:1 on Paper       */
  --rule:         rgba(50, 67, 83, 0.16);
  --rule-strong:  rgba(50, 67, 83, 0.32);

  /* --- Type ------------------------------------------------------------- */
  --font-display: "Jost", "Century Gothic", "Futura", "Avenir Next",
                  system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body:    "Spectral", "Iowan Old Style", "Palatino Linotype",
                  Palatino, Georgia, "Times New Roman", serif;

  /* 1.25 scale, hand-tuned. Body is 19px. */
  --t--1:   0.8125rem;  /* 13px — eyebrows, captions, meta                   */
  --t-0:    1.1875rem;  /* 19px — body                                       */
  --t-1:    1.4375rem;  /* 23px — lead paragraph, card headings              */
  --t-2:    1.625rem;   /* 26px — section headings                           */
  --t-3:    2rem;       /* 32px — page headings                              */
  --t-4:    clamp(2.125rem, 1.5rem + 2.6vw, 2.625rem); /* 34 → 42px — hero   */
  --t-fig:  clamp(2.125rem, 1.5rem + 2.6vw, 3rem);    /* 34 → 48px — figures */
  /* The written-out address is one long unbreakable-ish string. It has to fit
     at 360px without wrapping mid-token, so it scales rather than sitting at
     a fixed --t-1. 17 → 23px. */
  --t-addr: clamp(1.0625rem, 3.4vw, 1.4375rem);

  --lh-tight: 1.15;   /* display                                             */
  --lh-snug:  1.3;    /* headings                                            */
  --lh-body:  1.75;   /* body — brief specifies 1.7–1.8                      */
  --tracking-label: 0.14em;

  /* --- Space: single 4px root, no arbitrary values anywhere -------------- */
  --sp-1:   0.25rem;   /*   4 */
  --sp-2:   0.5rem;    /*   8 */
  --sp-3:   0.75rem;   /*  12 */
  --sp-4:   1rem;      /*  16 */
  --sp-5:   1.5rem;    /*  24 */
  --sp-6:   2rem;      /*  32 */
  --sp-7:   3rem;      /*  48 */
  --sp-8:   4rem;      /*  64 */
  --sp-9:   6rem;      /*  96 */
  --sp-10:  8rem;      /* 128 */

  /* Vertical rhythm scales as one system rather than per-breakpoint. Two
     adjacent sections stack their padding, so this reads at roughly double
     between bands — which is why it sits lower than it looks like it should. */
  --section-y: clamp(2.25rem, 4.5vw, 3.75rem);

  /* For sections whose content needs less room around it than the default
     rhythm gives — see .section--tight. */
  --section-y-tight: clamp(1.75rem, 3vw, 2.5rem);

  /* --- Layout ----------------------------------------------------------- */
  /* In rem, not px, so the columns follow the root scale. If the type shrank
     and these did not, the same 720px would hold ~73 characters and break the
     measure below. 45rem/70rem are 720px/1120px at the base scale. */
  --container: 45rem;   /* the column everything aligns to                   */
  --container-wide: 70rem;    /* cards, two-up grids                         */
  --measure: 66ch;      /* prose measure — the brief's 65–70 characters      */
  --gutter: clamp(1.25rem, 5vw, 2.5rem);

  --radius: 4px;        /* 4px or square. Nothing larger.                    */
}

/* Dark sections re-point the semantic tokens; components stay unchanged. */
.section--slate,
.section--deep {
  --bg:          var(--slate);
  --bg-raised:   var(--slate);
  --ink:         var(--paper);
  --ink-muted:   var(--stone-light);
  --accent:      var(--radish-light);
  --rule:        rgba(255, 255, 255, 0.18);
  --rule-strong: rgba(255, 255, 255, 0.34);
}

.section--deep {
  --bg:        var(--deep-slate);
  --bg-raised: var(--deep-slate);
}

/* Light variant. Lets sections alternate Mist / Paper without a colour jump,
   and keeps Paper cards legible by never sitting them on a Paper section. */
.section--paper {
  --bg: var(--paper);
}


/* ----------------------------------------------------------------------------
   3. Reset
---------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The header is sticky, so an in-page anchor would otherwise land underneath
     it. Roughly one header height. */
  scroll-padding-top: 6rem;
}

body,
h1, h2, h3, h4,
p, figure, blockquote, ul, ol, dl, dd {
  margin: 0;
}

ul[class],
ol[class] {
  padding: 0;
  list-style: none;
}

img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

/* No motion is shipped, but guard anything added later. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ----------------------------------------------------------------------------
   4. Base type
---------------------------------------------------------------------------- */

/* The global scale. Every type and space token is expressed in rem, so this is
   the single lever that moves the whole system: type, spacing and the two
   containers come down together and every relationship between them survives.
   Percent, not px, so a reader who has raised their browser font size keeps
   that increase — this reduces their size by a tenth, it does not override it.

   Desktop only. At 90% the --t--1 tier lands at 11.7px, and the small end of
   this design is tuned for a phone read in daylight (see the body weight note
   below), so the reduction would cost more there than it gains. 760px is where
   the team, resource and project grids already go multi-column, so the change
   in scale lands inside a reflow that was happening anyway rather than on its
   own. Every breakpoint in this file is in px and so none of them shift. */
@media (min-width: 760px) {
  html { font-size: 90%; }

  /* The rem ends of these two follow the root, but the vw term in the middle
     does not, and it is what governs section padding from 760px to about
     1200px — leaving it would shrink the type there and not the rhythm around
     it. Same coefficients, less a tenth. The other vw clamps (--gutter, --t-4,
     --t-fig, --t-addr) sit pinned to their rem maximum at every width this
     query covers, so they already come down with the root and need nothing. */
  :root {
    --section-y:       clamp(2.25rem, 4.05vw, 3.75rem);
    --section-y-tight: clamp(1.75rem, 2.7vw, 2.5rem);
  }
}

body {
  background: var(--mist);
  color: var(--slate);
  font-family: var(--font-body);
  font-weight: 400;          /* not 300 — 300 at 19px is thin on a phone in
                                daylight, which is the stated use case.
                                300 is reserved for large lead paragraphs.  */
  font-size: var(--t-0);
  line-height: var(--lh-body);
  font-kerning: normal;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: var(--lh-snug);
  letter-spacing: -0.005em;
  text-wrap: balance;
  /* Sentence case throughout. No Title Case anywhere on this site. */
}

h1 {
  font-size: var(--t-4);
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
}

h2 { font-size: var(--t-2); }
h3 { font-size: var(--t-1); }

p + p { margin-top: var(--sp-5); }

strong { font-weight: 400; color: var(--ink); }

a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}

a:hover {
  text-decoration-color: currentColor;
}

/* Visible keyboard focus everywhere. Never removed. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

.section--slate :focus-visible,
.section--deep :focus-visible {
  outline-color: var(--radish-light);
}


/* ----------------------------------------------------------------------------
   5. Layout
---------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.wrap--wide { max-width: var(--container-wide); }

/* Inner pages — work, resources, about, contact — run in one wide column, so
   the page title and the leftmost card share a left edge. The home page and
   404.html keep the 720px column. */
.page--wide .wrap { max-width: var(--container-wide); }

/* work.html runs as one centred reading column instead. Re-pointing --measure
   at the body is what makes it hold together: .prose, .figure, .status, .facts
   and .panel > p all read that token, so they land on the column's edges
   rather than each capping somewhere shorter. Scoped to main so the header and
   footer stay on the wide grid. */
.page--read { --measure: 45rem; }

.page--read main .wrap {
  max-width: calc(var(--measure) + 2 * var(--gutter));
}

/* Text blocks keep a readable width inside it: a .panel stretched to 1120px is
   mostly empty box. Both panels are capped identically — the results block and
   the limitations block must stay the same size as each other. */
.page--wide .panel { max-width: var(--container); }

/* Prose sits slightly narrower than the container so the 66ch measure holds.
   The brief's "~720px column" and "65–70 character measure" conflict at 19px;
   this resolves it — 720px container, 66ch prose inside it. */
.prose > * { max-width: var(--measure); }

.section {
  background: var(--bg);
  color: var(--ink);
  padding-block: var(--section-y);
}

/* Not a per-breakpoint override — it swaps one token for another on the same
   scale, for bands that read as over-padded at the default rhythm. Must sit
   after .section: same specificity, so source order decides. */
.section--tight { padding-block: var(--section-y-tight); }

/* Default rhythm between blocks in a section. Deliberately kept at one-class
   specificity — it must beat the `p { margin: 0 }` reset, but lose to any
   component that sets its own margin further down this file. */
:where(.section) > .wrap > * + * { margin-top: var(--sp-5); }

/* Skip link — first thing in the tab order. */
.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-4);
  z-index: 100;
  transform: translateY(-200%);
  background: var(--slate);
  color: var(--paper);
  font-family: var(--font-display);
  font-size: var(--t--1);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  text-decoration: none;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
}

.skip-link:focus {
  transform: translateY(0);
}


/* ----------------------------------------------------------------------------
   6. Header and navigation

   No hamburger. Four links plus the wordmark wrap onto a second row below
   480px. Deliberate: the site must work fully with JS off.

   The bar is solid Slate on every page and every scroll position, so it
   re-points the semantic tokens the same way a dark section does. Components
   inside it read --ink / --accent / --rule and need no dark variant.

   Sticky is pure CSS, so with JS off this is simply a bar that stays at the
   top. js/nav.js adds the one class that slides it away on scroll down.
---------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;

  --ink:         var(--paper);
  --ink-muted:   var(--stone-light);
  --accent:      var(--radish-light);
  --rule:        rgba(255, 255, 255, 0.18);
  --rule-strong: rgba(255, 255, 255, 0.34);

  background: var(--slate);
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  transition: transform 220ms ease;
}

/* Added by js/nav.js on scroll down, removed on scroll up and whenever the
   page is near the top. Never applied with JS off. */
.site-header--hidden { transform: translateY(-100%); }

/* "No transition, just stays put." nav.js also opts out entirely when this
   query matches; this is the belt to that pair of braces. */
@media (prefers-reduced-motion: reduce) {
  .site-header--hidden { transform: none; }
}

.site-header :focus-visible { outline-color: var(--radish-light); }

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3) var(--sp-5);
  padding-block: var(--sp-4);
}

/* Brand lockup: supplied mark + wordmark set in Jost.

   The PNGs are 924x924 with the artwork inset — bbox is 85% of the width and
   71% of the height, sitting 3.7% below centre. So the box is sized larger
   than the optical mark and pulled up to compensate; otherwise the mark reads
   small and low against the wordmark. If we move to logo-mark.svg the viewBox
   can be tightened and these corrections drop out. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
}

/* Sized in rem so the mark tracks the wordmark beside it under the root scale;
   in px it would stay put while the wordmark shrank and the lockup would come
   apart. The corrections are proportions of the mark, so they are in rem too.
   2.875rem/0.5rem/0.25rem/0.125rem are 46/8/4/2px at the base scale. */
.brand__mark {
  width: 2.875rem;
  height: 2.875rem;
  flex: none;
  margin-block: -0.5rem;
  margin-inline-start: -0.25rem;   /* artwork is inset 7.7% on the left */
  position: relative;
  top: -0.125rem;
}

.brand__name {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1.25rem;
  line-height: 1;
  letter-spacing: 0.01em;
  /* Semantic token, not --slate: the header and footer both re-point --ink,
     so the wordmark inverts on its own. */
  color: var(--ink);
  white-space: nowrap;
}

.brand:hover .brand__name { color: var(--accent); }

@media (max-width: 480px) {
  .brand__mark { width: 2.5rem; height: 2.5rem; }   /* 40px */
  .brand__name { font-size: 1.125rem; }
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1rem;
  color: var(--ink);
  text-decoration: none;
  padding-block: var(--sp-1);
  border-bottom: 1px solid transparent;
}

.site-nav a:hover {
  border-bottom-color: var(--accent);
}

.site-nav a[aria-current="page"] {
  border-bottom-color: var(--ink);
}

@media (max-width: 480px) {
  .site-header__inner { gap: var(--sp-4); }
  .site-nav { width: 100%; }
  .site-nav a { font-size: 0.9375rem; }
}


/* ----------------------------------------------------------------------------
   7. Eyebrow / small label
---------------------------------------------------------------------------- */

.eyebrow {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t--1);
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--accent);
  margin-bottom: var(--sp-4);
}

.eyebrow--stone { color: var(--ink-muted); }


/* ----------------------------------------------------------------------------
   8. Hero

   Text and photograph side by side on desktop, photograph below the text on
   narrow screens. Text is never set over the photograph: it cannot be
   guaranteed AA without a scrim heavy enough to flatten the picture.

   The text column is sized so the headline sets on two lines, breaking at the
   comma. If --t-4 or the column ratio changes, check that break again.

   .hero__inner sits inside .wrap, not on it, so the section flow rule does not
   land a stray top margin on the photograph as a grid item.
---------------------------------------------------------------------------- */

.hero {
  padding-block: clamp(1.75rem, 3.5vw, 3rem);
}

.hero__inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  align-items: center;
}

@media (min-width: 860px) {
  .hero__inner {
    grid-template-columns: 1.2fr 1fr;
    gap: var(--sp-6);
  }
}

.hero__lead {
  margin-top: var(--sp-5);
  font-weight: 300;
  font-size: var(--t-1);
  line-height: 1.6;
  color: var(--ink);
}

.hero__photo {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
}

/* Shared caption style: in-column figures on work.html and the hero photo. */
.figure figcaption,
.caption {
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  line-height: 1.5;
  color: var(--ink-muted);
}


/* ----------------------------------------------------------------------------
   9. Beats — "what we do". Short headings, hairline rules between.
      Not numbered: this is a posture, not a sequence.
---------------------------------------------------------------------------- */

.beats {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule);
}

.beats > li {
  border-bottom: 1px solid var(--rule);
  padding-block: var(--sp-6);
  max-width: var(--measure);
}

.beats h3 {
  font-size: var(--t-1);
  margin-bottom: var(--sp-3);
}


/* ----------------------------------------------------------------------------
   10. Project cards — "where we're working"
---------------------------------------------------------------------------- */

.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 800px) {
  .cards { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
}

.card {
  display: flex;
  flex-direction: column;
  /* --bg-raised, not --paper: the card then inverts correctly if it is ever
     placed inside a slate or deep-slate section. */
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.card__media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--sp-6);
}

.card__place {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t-1);
  line-height: var(--lh-snug);
  margin-bottom: var(--sp-3);
}

.card__neglect {
  font-style: italic;
  color: var(--stone);
  margin-bottom: var(--sp-5);
}

.card__foot {
  margin-top: auto;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--rule);
}


/* ----------------------------------------------------------------------------
   11. Figures and data — Jost 400, tabular
---------------------------------------------------------------------------- */

.stat {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-fig);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--ink);
}

/* One highlighted figure per section — no more. */
.stat--accent { color: var(--accent); }

.stat__label {
  display: block;
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

/* The qualifier that keeps a headline figure honest on the page it appears
   on, rather than one click away. */
.stat__caveat {
  display: block;
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  line-height: 1.5;
  color: var(--stone);
}


/* ----------------------------------------------------------------------------
   12. Source notes — for figures a funder will check
---------------------------------------------------------------------------- */

.source-note {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
  max-width: var(--measure);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  line-height: 1.6;
  color: var(--ink-muted);
}

/* The written-out address under the doors. Same rule and spacing as a source
   note, but it is the route off the page, so it is set to be read. */
.source-note--address {
  font-size: var(--t-1);
  line-height: 1.5;
  color: var(--ink);
}

/* The address itself in Radish, so it reads as the route out of the sentence
   around it. --accent, not --radish: correct on a dark section too. */
.source-note--address .address { color: var(--accent); }


/* ----------------------------------------------------------------------------
   13. Doors — "the ask" / contact routes
---------------------------------------------------------------------------- */

.doors {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* auto-fit, not repeat(3, 1fr): the home page has two doors and the contact
   and 404 pages have three. A fixed three-column track would leave the home
   page with an empty third column. */
@media (min-width: 760px) {
  .doors {
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--sp-6);
  }
}

.door {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--rule-strong);
  padding-top: var(--sp-5);
}

/* contact.html only. The rules and their run-up are dropped so the three
   headings sit directly under the page title. index.html keeps them — the two
   are otherwise the same component with the same copy. */
.doors--plain > .door {
  border-top: 0;
  padding-top: 0;
}

.door h3 {
  font-size: var(--t-1);
  margin-bottom: var(--sp-3);
}

.door p {
  font-size: 1.0625rem;
  margin-bottom: var(--sp-5);
}

/* Doors no longer carry a button — there is no mailto: anywhere on the site,
   so the copy is the last thing in the box and must not leave a gap under it. */
.door p:last-child { margin-bottom: 0; }

.door .btn { margin-top: auto; }

.btn {
  display: inline-block;
  align-self: flex-start;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t--1);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  text-decoration: none;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-5);
}

.btn:hover {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}

/* Text link with an arrow — for in-flow "read more" routes. */
.link-arrow {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t--1);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  text-decoration: none;
  color: var(--accent);
  border-bottom: 1px solid currentColor;
  padding-bottom: 2px;
}

.link-arrow:hover { color: var(--ink); }


/* ----------------------------------------------------------------------------
   13b. Page header — inner pages
---------------------------------------------------------------------------- */

.page-header { padding-block: clamp(1.25rem, 2.5vw, 2rem) 0; }

/* The page title is an .eyebrow on the four inner pages, and its label margin
   is the wrong gap when it is a page title rather than a label. */
.page-header .eyebrow { margin-bottom: 0; }

/* Where the title is the only thing in the header, the first block of content
   follows it closely — the default section rhythm reads as a hole there.
   Beats .section--tight on specificity, which is intended. */
.page-header--bare + .section { padding-top: var(--sp-5); }

/* about.html, where an intro paragraph does need room under it before the
   next section starts. */
.page-header--lead { padding-bottom: var(--sp-6); }

/* The four inner pages carry their page name as `<h1 class="eyebrow">` and must
   keep the eyebrow's size — hence :not(). 404.html still has a display h1. */
.page-header h1:not(.eyebrow) {
  font-size: var(--t-3);
  max-width: 20ch;
}

.page-header .lead { margin-top: var(--sp-5); max-width: var(--measure); }


/* ----------------------------------------------------------------------------
   13c. Project section — one per project on work.html
---------------------------------------------------------------------------- */

.project {
  scroll-margin-top: var(--sp-6);
  border-top: 1px solid var(--rule);
  padding-top: var(--sp-7);
}

.project + .project { margin-top: var(--sp-8); }

.project h2 { font-size: var(--t-2); margin-bottom: var(--sp-5); }

/* Flow inside a project. Components defined further down (.panel, .status)
   set their own top margin and override this on source order. */
.project > * + * { margin-top: var(--sp-5); }


/* In-column figure. The full-bleed variant is for hero photography only;
   inside a 720px column a photo should stay in the column.

   Deliberately no `margin: 0` reset here — the global `figure { margin: 0 }`
   already handles that at element specificity, so .project's flow rule can
   still give this a top margin. A class-level reset would silently win and
   jam the photo against the paragraph above it. */
/* Capped at the body measure and centred, so the photo's edges line up with
   the paragraphs above and below it rather than running wider than the text. */
.figure {
  max-width: var(--measure);
  margin-inline: auto;
}

/* For a photograph that wants to sit in a little from the column edges. */
.figure--narrow { max-width: 40rem; }

.figure img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--radius);
}

/* .figure figcaption is styled with .caption up in the hero block. */


/* ----------------------------------------------------------------------------
   13d. Panel — the results block AND the "what we got wrong" block

   THE SIGNATURE ELEMENT. These two are deliberately the same component with
   the same type size, padding and border. The honesty block is not smaller,
   not muted, not tucked at the end — it sits directly beneath the results and
   carries identical weight. Do not "tidy" this by de-emphasising it; that
   would remove the one thing that makes this site memorable.
---------------------------------------------------------------------------- */

.panel {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  margin-top: var(--sp-6);
}

.panel + .panel { margin-top: var(--sp-5); }

/* Results and Limitations side by side on desktop, stacked on mobile with
   results first (source order). Equal tracks and `stretch` are load-bearing:
   the two must be the same width and the same height. Never give the second
   one a narrower track. */
.panels {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  align-items: stretch;
}

@media (min-width: 900px) {
  .panels { grid-template-columns: 1fr 1fr; gap: var(--sp-5); }
}

/* Grid gap does the spacing, and the pair spans the full column rather than
   the single-panel cap. Contextual overrides, not a component reset. */
.panels > .panel {
  margin-top: 0;
  max-width: none;
}

.panel__label {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t--1);
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-muted);
  margin-bottom: var(--sp-5);
}

.panel > p { max-width: var(--measure); }
.panel > p + p { margin-top: var(--sp-5); }

/* Result lines: Jost figure, Spectral explanation, hairline between. */
.facts { margin: 0; padding: 0; list-style: none; }

.facts > li {
  padding-block: var(--sp-4);
  border-top: 1px solid var(--rule);
  max-width: var(--measure);
}

.facts > li:first-child { border-top: 0; padding-top: 0; }
.facts > li:last-child { padding-bottom: 0; }

.fig {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.375rem;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  color: var(--ink);
  margin-right: 0.15em;
}

/* One highlighted figure per section — no more. */
.fig--accent { color: var(--accent); }

/* The qualifier on a single fact, kept next to the figure it qualifies rather
   than moved into the limitations block. */
.footnote {
  display: block;
  margin-top: var(--sp-2);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  line-height: 1.5;
  color: var(--ink-muted);
}


/* ----------------------------------------------------------------------------
   13e. Status line — "where it is now", "forthcoming"
---------------------------------------------------------------------------- */

.status {
  margin-top: var(--sp-6);
  padding-left: var(--sp-5);
  border-left: 2px solid var(--accent);
  max-width: var(--measure);
}

.status h3 {
  font-size: var(--t-1);
  margin-bottom: var(--sp-3);
}


/* ----------------------------------------------------------------------------
   13f. Resource cards
---------------------------------------------------------------------------- */

.resources {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 760px) {
  .resources { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
}

.resource {
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--sp-6);
}

.resource h3 { font-size: var(--t-1); margin-bottom: var(--sp-3); }
.resource p  { font-size: 1.0625rem; }

.resource__meta {
  margin-top: var(--sp-4);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.resource__foot { margin-top: auto; padding-top: var(--sp-5); }

.resource--pending { border-style: dashed; }


/* ----------------------------------------------------------------------------
   13g. People and partners — about.html
---------------------------------------------------------------------------- */

.people {
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
}

@media (min-width: 640px) {
  .people { grid-template-columns: 1fr 1fr; gap: var(--sp-4) var(--sp-6); }
}

.people > li {
  border-top: 1px solid var(--rule);
  padding-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1.0625rem;
}

.people .where {
  display: block;
  font-size: var(--t--1);
  color: var(--ink-muted);
  margin-top: 2px;
}

/* A labelled group below the partners list, in the same section. One step up
   from the default block rhythm so the label reads as starting a new group
   rather than trailing the list above it. Beats `.wrap > * + *` on source
   order, not specificity — both are one class. */
.people-sub { margin-top: var(--sp-6); }


/* ----------------------------------------------------------------------------
   13h. Team — about.html. Three across on desktop, stacked below.
        Names, roles and contact only: no bios, no photographs.
---------------------------------------------------------------------------- */

.team {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  margin: var(--sp-5) 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 760px) {
  .team { grid-template-columns: repeat(3, 1fr); gap: var(--sp-6); }
}

.team > li {
  border-top: 1px solid var(--rule-strong);
  padding-top: var(--sp-5);
}

.team h3 { font-size: var(--t-1); }

.team__role {
  margin-top: var(--sp-1);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-muted);
}

.team__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  margin: var(--sp-3) 0 0;
  padding: 0;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1rem;
}


/* ----------------------------------------------------------------------------
   13i. Address — written-out, never a mailto:

   There are no mailto: links anywhere on this site. The address is set out in
   the "[at] / [dot]" form so an address scraper reading href values finds
   nothing, and so nobody lands on a dead mail client. See CLAUDE.md.
---------------------------------------------------------------------------- */

.address {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-addr);
  line-height: 1.4;
  color: var(--ink);
  overflow-wrap: break-word;
}

.address--inline {
  font-size: inherit;
  font-family: inherit;
  font-weight: inherit;
}

.footer__address {
  margin-top: var(--sp-3);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--stone-light);
  overflow-wrap: break-word;
}


/* ----------------------------------------------------------------------------
   13j. Link list — outbound routes that are not downloads

   Used for the instructional videos. They link out rather than embed: a
   third-party player would pull trackers onto the page and break the
   no-cookie-banner claim.
---------------------------------------------------------------------------- */

.links {
  margin: 0;
  padding: 0;
  list-style: none;
  max-width: var(--measure);
  border-top: 1px solid var(--rule);
}

.links > li { border-bottom: 1px solid var(--rule); }

.links > li > a {
  display: block;
  padding-block: var(--sp-4);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t-1);
  line-height: var(--lh-snug);
  text-decoration: none;
  color: var(--ink);
}

.links > li > a:hover { color: var(--accent); }


/* ----------------------------------------------------------------------------
   14. Footer
---------------------------------------------------------------------------- */

.site-footer {
  background: var(--deep-slate);
  color: var(--paper);
  /* Hairline against the deep-slate "ask" section directly above it. */
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  padding-block: var(--sp-8) var(--sp-6);
  --ink: var(--paper);
  --ink-muted: var(--stone-light);
  --accent: var(--radish-light);
  --rule: rgba(255, 255, 255, 0.18);
}

/* .brand__name and its hover read --ink / --accent, which the block above has
   already re-pointed. No footer-specific brand rules are needed. */

.site-footer a { color: var(--radish-light); }

.footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  align-items: start;
}

@media (min-width: 720px) {
  .footer__grid { grid-template-columns: 1.4fr 1fr; gap: var(--sp-8); }
}

.footer__blurb {
  margin-top: var(--sp-4);
  max-width: 40ch;
  font-size: 1.0625rem;
  color: var(--stone-light);
}

.footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: 1rem;
}

.footer__legal {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--t--1);
  line-height: 1.6;
  color: var(--stone-light);
}


/* ----------------------------------------------------------------------------
   15. Utilities
---------------------------------------------------------------------------- */

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

.lead {
  font-weight: 300;
  font-size: var(--t-1);
  line-height: 1.65;
}

/* One step down the scale. Weight goes back to 400 with it — 300 at 19px is
   too thin, which is why 300 is reserved for the larger sizes. */
.lead--sm {
  font-weight: 400;
  font-size: var(--t-0);
}

.flow > * + * { margin-top: var(--sp-5); }

/* Marks every unresolved placeholder in the build. Search for "TODO" in the
   HTML, or load the page and look for the dotted outline. Delete this rule
   once the site goes live. */
[data-placeholder] {
  outline: 1px dashed var(--radish);
  outline-offset: 3px;
}
