/* ==========================================================================
   Friends of Nunhead Reservoir
   Design system. Brand colours are sampled from the campaign logo:
   sky #009FDF, green #1EA513, white.

   Two things about the palette are deliberate and easy to "tidy" wrongly:

   1. The brand green and the brand sky BOTH fail WCAG AA as text colours
      (white on #1EA513 is 3.26:1; #009FDF on paper is 2.99:1). They are kept
      for GRAPHICS ONLY — the logo, rules, fills, the fence motif. Text and
      buttons use the darkened siblings --green-ink / --green-btn / --sky-ink,
      which are the same hues at AA contrast. Swapping a button back to
      --green because it "looks more on brand" silently breaks the contrast
      floor on the primary call to action.

   2. The surface is warm paper, not white. The campaign's argument is about
      common land and enclosure, and a warm ground carries that better than
      a clinical white. It also stops the sky photography from glaring.
   ========================================================================== */

@font-face {
  font-family: 'Archivo Black';
  src: url('/fonts/archivo-black.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- brand, graphics only --- */
  --sky: #009FDF;
  --green: #1EA513;

  /* --- accessible siblings, for text and controls --- */
  --green-ink: #0C5A08;   /* 8.0:1 on paper  */
  --green-btn: #12800D;   /* 5.1:1 with white */
  --sky-ink: #0A6E9E;     /* 5.3:1 on paper  */
  --ember: #B8451F;       /* urgency, text-safe */
  --ember-bright: #E2703A;/* urgency, graphics */

  /* --- surfaces --- */
  --ink: #0B1A14;
  --ink-soft: #354A40;
  --ink-faint: #5F6F66;   /* 5.05:1 on paper, 4.59:1 on paper-2 */
  --paper: #FBF9F4;
  --paper-2: #F2EEE4;
  --paper-3: #E7E1D3;
  --line: #DAD3C3;
  --forest: #0C2A1D;

  --radius: 14px;
  --radius-sm: 9px;
  --maxw: 1180px;
  --measure: 68ch;

  --sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto,
          'Helvetica Neue', Arial, sans-serif;
  --display: 'Archivo Black', var(--sans);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shadow: 0 1px 2px rgba(11, 26, 20, .05), 0 8px 24px -12px rgba(11, 26, 20, .18);
  --shadow-lift: 0 2px 4px rgba(11, 26, 20, .06), 0 18px 44px -18px rgba(11, 26, 20, .28);
}

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

/* [hidden] must WIN. Any class that sets `display` — .countline is flex, .btn
   is inline-flex, .sigwall is flex — beats the user-agent's `[hidden]{display:
   none}` on specificity, so `el.hidden = true` silently does nothing and the
   element stays on screen. That is how a "0 people have signed" line, or a
   toggle for a list with nothing in it, ships to a live page while the code
   that hid it reads as correct. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img, svg, video, canvas { max-width: 100%; display: block; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--sky-ink); text-underline-offset: .18em; text-decoration-thickness: .07em; }
a:hover { color: var(--green-ink); }
h1, h2, h3, h4 { line-height: 1.08; margin: 0; letter-spacing: -.018em; }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }
hr { border: 0; border-top: 1px solid var(--line); margin: 3rem 0; }
:target { scroll-margin-top: 6rem; }

::selection { background: var(--green); color: #fff; }

/* Visible focus everywhere. :focus-visible only, so a mouse click on a
   button does not leave a ring behind it. */
:focus-visible {
  outline: 3px solid var(--sky-ink);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--ink); color: #fff; padding: .8rem 1.2rem;
  border-radius: 0 0 var(--radius-sm) 0; font-weight: 700;
}
.skip:focus { left: 0; color: #fff; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------------- layout --- */
.wrap { width: min(100% - 2.5rem, var(--maxw)); margin-inline: auto; }
.wrap-narrow { width: min(100% - 2.5rem, 780px); margin-inline: auto; }
.section { padding: clamp(3.5rem, 8vw, 7rem) 0; }
.section--tight { padding: clamp(2.5rem, 5vw, 4rem) 0; }
.section--paper2 { background: var(--paper-2); }
.section--forest { background: var(--forest); color: #EAF3EE; }
.section--forest h2, .section--forest h3 { color: #fff; }
/* :not(.btn) is load-bearing. `.section--forest a` is specificity (0,1,1) and
   `.btn--primary` is (0,1,0), so without it this rule REPAINTS the primary
   call to action inside every dark section — pale blue on green, measured at
   3.26:1 against a 4.5 floor, on the most important control on the page. The
   button keeps its own colours; only real links are restyled. */
.section--forest a:not(.btn) { color: #8FD8FF; }

.stack > * + * { margin-top: 1.1rem; }
.measure { max-width: var(--measure); }

/* --------------------------------------------------------------- type --- */
.display {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: .96;
  text-wrap: balance;
}
.h-hero  { font-size: clamp(2.6rem, 8.2vw, 5.6rem); }
.h-1     { font-size: clamp(2.05rem, 5.2vw, 3.4rem); }
.h-2     { font-size: clamp(1.5rem, 3.2vw, 2.15rem); }
.h-3     { font-size: clamp(1.15rem, 2.1vw, 1.4rem); letter-spacing: -.01em; }

.eyebrow {
  font-size: .78rem; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--green-ink);
  display: flex; align-items: center; gap: .6rem; margin: 0 0 1rem;
}
.eyebrow::before {
  content: ''; width: 26px; height: 3px; background: var(--green);
  border-radius: 2px; flex: none;
}
.section--forest .eyebrow { color: #7FE06F; }
.section--forest .eyebrow::before { background: #7FE06F; }

.lede { font-size: clamp(1.12rem, 2.1vw, 1.32rem); line-height: 1.55; color: var(--ink-soft); }
.section--forest .lede { color: #C6DCD0; }
.small { font-size: .9rem; color: var(--ink-faint); }
.tiny  { font-size: .8rem; color: var(--ink-faint); line-height: 1.5; }

/* ------------------------------------------------------------ buttons --- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .55rem;
  padding: .85rem 1.5rem;
  border-radius: 999px;
  border: 2px solid transparent;
  font-weight: 750;
  font-size: 1rem;
  letter-spacing: -.005em;
  text-decoration: none;
  cursor: pointer;
  transition: transform .16s ease, background-color .16s ease,
              border-color .16s ease, box-shadow .16s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary { background: var(--green-btn); color: #fff; box-shadow: var(--shadow); }
.btn--primary:hover { background: #0E6B0A; color: #fff; box-shadow: var(--shadow-lift); }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { background: var(--paper-2); border-color: var(--ink-faint); color: var(--ink); }

.btn--onphoto {
  background: rgba(255,255,255,.14); color: #fff; border-color: rgba(255,255,255,.6);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.btn--onphoto:hover { background: rgba(255,255,255,.26); color: #fff; }

.btn--sm { padding: .55rem 1rem; font-size: .9rem; }
.btn--full { width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }

/* --------------------------------------------------------------- hero --- */
.hero {
  position: relative;
  min-height: min(94svh, 900px);
  display: grid;
  align-items: end;
  isolation: isolate;
  background: #0d1b26;
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
/* The <img> is the truth and always renders. The canvas draws the same
   photograph with a parallax displacement and fades in ONLY once WebGL has
   compiled and the textures are decoded, so a failure at any point leaves
   the photograph in place rather than a black box. */
.hero__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%;
}
.hero__canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0;
  transition: opacity .9s ease;
}
.hero__canvas.is-live { opacity: 1; }
.hero__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(to top, rgba(6,16,12,.90) 0%, rgba(6,16,12,.62) 28%,
                    rgba(6,16,12,.18) 58%, rgba(6,16,12,.34) 100%);
}
.hero__inner {
  position: relative; z-index: 2;
  /* The block is bottom-aligned, so the BOTTOM padding is what lifts it. On a
     short laptop viewport (1918x865 was the case that showed it) the buttons
     ended up almost against the fold with the whole block sitting low. Using
     vh rather than a fixed value means the lift scales with how short the
     window is, instead of being tuned for one screen. */
  padding: 7rem 0 clamp(4rem, 11vh, 8rem);
  color: #fff;
}
.hero h1 { color: #fff; max-width: 16ch; text-shadow: 0 2px 30px rgba(0,0,0,.4); }
.hero__lede {
  color: #E4EDE8; font-size: clamp(1.08rem, 2.2vw, 1.35rem);
  max-width: 52ch; margin-top: 1.4rem; line-height: 1.55;
  text-shadow: 0 1px 16px rgba(0,0,0,.5);
}
.hero__actions { margin-top: 2rem; }
.hero__credit {
  position: absolute; right: 1rem; bottom: .55rem; z-index: 2;
  font-size: .68rem; color: rgba(255,255,255,.62); letter-spacing: .02em;
}
.hero__credit a { color: rgba(255,255,255,.82); }

.hero__tag {
  display: inline-flex; align-items: center; gap: .5rem;
  /* Opaque, and the DARKER ember. The bright one at 92% let the sky through
     and measured 3.03:1 against white — fine for a graphic, not for a 12px
     bold label, which needs 4.5. This is 5.37:1. */
  background: var(--ember); color: #fff;
  font-size: .76rem; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; padding: .38rem .8rem; border-radius: 999px;
  margin-bottom: 1.4rem;
}

/* Signature counter under the hero actions */
.countline {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  margin-top: 1.7rem; color: #D8E5DE; font-size: .96rem;
}
.countline b {
  font-family: var(--display); font-size: 1.6rem; color: #fff;
  letter-spacing: -.02em;
}

/* ---------------------------------------------------------- fence rule -- */
/* The campaign's own object, used as the section divider. */
.fence {
  height: 30px;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'>\
<path d='M0 30L30 0M0 0l30 30' stroke='%231EA513' stroke-width='1.4' fill='none' opacity='.45'/>\
</svg>");
  background-size: 22px 22px;
  border-top: 2px solid var(--green);
  border-bottom: 2px solid var(--green);
  opacity: .55;
}

/* ---------------------------------------------------------------- grid -- */
.grid { display: grid; gap: 1.5rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.split {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1.05fr .95fr; }
  .split--reverse > :first-child { order: 2; }
}

/* --------------------------------------------------------------- card --- */
.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem;
  box-shadow: var(--shadow);
}
.card--flat { box-shadow: none; background: var(--paper); }
.card__num {
  font-family: var(--display); font-size: 2.4rem; color: var(--green);
  line-height: 1; margin-bottom: .6rem; letter-spacing: -.03em;
}
.card h3 { margin-bottom: .5rem; }
.card p { color: var(--ink-soft); font-size: .98rem; }

.figure { margin: 0; border-radius: var(--radius); overflow: hidden; }
.figure img { width: 100%; }
.figure figcaption {
  font-size: .82rem; color: var(--ink-faint); padding: .6rem .2rem 0; line-height: 1.45;
}
.figure--frame { border: 1px solid var(--line); box-shadow: var(--shadow); }

/* ---------------------------------------------------------- comparison -- */
/* Honor Oak vs Nunhead. The point of the whole campaign in one block. */
.versus {
  display: grid; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
@media (min-width: 760px) { .versus { grid-template-columns: 1fr 1fr; } }
.versus__side { background: #fff; padding: clamp(1.5rem, 3vw, 2.2rem); }
.versus__side--locked { background: #FFF8F5; }
.versus__label {
  font-size: .74rem; font-weight: 800; letter-spacing: .13em;
  text-transform: uppercase; margin-bottom: .7rem;
}
.versus__side--open  .versus__label { color: var(--green-ink); }
.versus__side--locked .versus__label { color: var(--ember); }
.versus__side h3 { font-family: var(--display); font-size: 1.45rem; margin-bottom: .8rem; }
.versus__list { list-style: none; margin: 0; padding: 0; font-size: .96rem; }
.versus__list li {
  padding: .5rem 0 .5rem 1.7rem; position: relative;
  border-top: 1px dashed var(--line); color: var(--ink-soft);
}
.versus__list li:first-child { border-top: 0; }
.versus__list li::before {
  position: absolute; left: 0; top: .5rem; font-weight: 700;
}
.versus__side--open .versus__list li::before  { content: '✓'; color: var(--green-ink); }
.versus__side--locked .versus__list li::before { content: '✕'; color: var(--ember); }

/* ----------------------------------------------------------- timeline --- */
.timeline { list-style: none; margin: 0; padding: 0; position: relative; }
.timeline::before {
  content: ''; position: absolute; left: 7px; top: .5rem; bottom: .5rem;
  width: 2px; background: linear-gradient(var(--green), var(--sky));
  opacity: .35;
}
.timeline li { position: relative; padding: 0 0 1.9rem 2.4rem; }
.timeline li::before {
  content: ''; position: absolute; left: 0; top: .42rem;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--paper); border: 3px solid var(--green);
}
.timeline li.is-open::before { border-color: var(--ember-bright); }
.timeline__year {
  font-family: var(--display); font-size: .95rem; color: var(--green-ink);
  letter-spacing: .01em; display: block; margin-bottom: .15rem;
}
.timeline li.is-open .timeline__year { color: var(--ember); }
.timeline h3 { font-size: 1.08rem; margin-bottom: .3rem; }
.timeline p { font-size: .97rem; color: var(--ink-soft); }

/* --------------------------------------------------------------- form --- */
.form { display: grid; gap: 1.05rem; }
.field { display: grid; gap: .38rem; }
.field label { font-weight: 700; font-size: .93rem; }
.field .hint { font-size: .83rem; color: var(--ink-faint); font-weight: 400; }
.field input[type=text],
.field input[type=email],
.field textarea,
.field select {
  width: 100%;
  padding: .78rem .9rem;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--green-btn);
  box-shadow: 0 0 0 3px rgba(18,128,13,.14);
  outline: none;
}
.field textarea { min-height: 100px; resize: vertical; }

.choice { display: flex; gap: .6rem; align-items: flex-start; font-size: .95rem; }
.choice input { margin-top: .28rem; flex: none; width: 1.05rem; height: 1.05rem; accent-color: var(--green-btn); }

.radio-row { display: flex; gap: 1.4rem; flex-wrap: wrap; }

/* Honeypot. Never display:none — some bots skip hidden fields; this keeps it
   in the layout but out of sight and out of the tab order. */
.gotcha {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; opacity: 0;
}

.form-note {
  border-left: 3px solid var(--green); padding: .2rem 0 .2rem .9rem;
  font-size: .87rem; color: var(--ink-soft);
}

.form-status { font-weight: 700; font-size: .95rem; }
.form-status[data-state="error"] { color: var(--ember); }
.form-status[data-state="ok"] { color: var(--green-ink); }

/* --------------------------------------------------- signature wall ----- */
.sigwall {
  display: flex; flex-wrap: wrap; gap: .45rem;
  max-height: 260px; overflow: hidden; position: relative;
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
.sigwall.is-open { max-height: none; mask-image: none; -webkit-mask-image: none; }
.sig {
  background: #fff; border: 1px solid var(--line); border-radius: 999px;
  padding: .32rem .8rem; font-size: .87rem; color: var(--ink-soft);
}
.sig b { color: var(--ink); font-weight: 700; }
.sig--anon { color: var(--ink-faint); font-style: italic; }

/* ------------------------------------------------------ copy blocks ----- */
.copyblock {
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.15rem 1.15rem 1rem; display: grid; gap: .85rem;
  align-content: start;
}
.copyblock__text { font-size: .97rem; line-height: 1.6; white-space: pre-wrap; margin: 0; }
.copyblock__bar { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
.copyblock__count { font-size: .78rem; color: var(--ink-faint); margin-left: auto; font-variant-numeric: tabular-nums; }
.copyblock__count.is-over { color: var(--ember); font-weight: 700; }

.copybtn { position: relative; }
.copybtn[data-copied="1"]::after {
  content: 'Copied'; position: absolute; inset: 0;
  display: grid; place-items: center;
  background: var(--green-btn); color: #fff; border-radius: 999px;
  font-size: .9rem; font-weight: 750;
}

/* --------------------------------------------------------- templates ---- */
.tpl { border: 1px solid var(--line); border-radius: var(--radius); background: #fff; overflow: hidden; }
.tpl + .tpl { margin-top: 1rem; }
.tpl > summary {
  cursor: pointer; padding: 1.05rem 1.2rem; font-weight: 750;
  display: flex; gap: .8rem; align-items: center; list-style: none;
  background: var(--paper-2);
}
.tpl > summary::-webkit-details-marker { display: none; }
.tpl > summary::after {
  content: '+'; margin-left: auto; font-family: var(--mono); font-size: 1.3rem;
  color: var(--green-ink); line-height: 1;
}
.tpl[open] > summary::after { content: '–'; }
.tpl > summary:hover { background: var(--paper-3); }
.tpl__body { padding: 1.2rem; border-top: 1px solid var(--line); }

.kv { display: grid; gap: .3rem; font-size: .92rem; margin-bottom: 1rem; }
.kv div { display: flex; gap: .55rem; flex-wrap: wrap; }
.kv dt { font-weight: 750; color: var(--ink-faint); min-width: 4.5rem; }
.kv dd { margin: 0; }

/* --------------------------------------------------------- galleries ---- */
.gallery { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.gallery--portrait { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
.shot {
  position: relative; display: block; border-radius: var(--radius-sm);
  overflow: hidden; border: 1px solid var(--line); background: var(--paper-2);
  text-decoration: none;
}
.shot img { width: 100%; aspect-ratio: 16/9; object-fit: cover; transition: transform .35s ease; }
.gallery--square .shot img { aspect-ratio: 1; }
.gallery--portrait .shot img { aspect-ratio: 9/16; }
.shot:hover img { transform: scale(1.035); }
.shot__dl {
  position: absolute; inset: auto 0 0 0;
  background: linear-gradient(transparent, rgba(6,16,12,.85));
  color: #fff; font-size: .8rem; font-weight: 700;
  padding: 1.6rem .7rem .55rem; display: flex; align-items: center; gap: .35rem;
  opacity: 0; transition: opacity .2s ease;
}
.shot:hover .shot__dl, .shot:focus-visible .shot__dl { opacity: 1; }

/* -------------------------------------------------------------- header -- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(251,249,244,.86);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease, background-color .2s ease;
}
.site-header.is-stuck { border-bottom-color: var(--line); }
.site-header__inner {
  display: flex; align-items: center; gap: 1rem;
  padding: .6rem 0; min-height: 62px;
}
.brand { display: flex; align-items: center; gap: .65rem; text-decoration: none; color: var(--ink); flex: none; }
.brand img { width: 40px; height: 40px; }
.brand__text { display: flex; flex-direction: column; line-height: 1.06; }
.brand__text span:first-child { font-size: .64rem; letter-spacing: .13em; text-transform: uppercase; color: var(--ink-faint); font-weight: 700; }
.brand__text span:last-child { font-family: var(--display); font-size: 1.02rem; letter-spacing: -.02em; }

.nav { display: none; margin-left: auto; align-items: center; gap: .3rem; }
@media (min-width: 940px) { .nav { display: flex; } }
.nav a {
  padding: .5rem .78rem; border-radius: 999px; text-decoration: none;
  color: var(--ink); font-weight: 650; font-size: .95rem;
}
.nav a:hover { background: var(--paper-2); color: var(--ink); }
.nav a[aria-current="page"] { background: var(--paper-3); color: var(--ink); }
.header-cta { margin-left: .5rem; }
@media (max-width: 939px) { .header-cta { margin-left: auto; } }

.navtoggle {
  display: grid; place-items: center; margin-left: .35rem;
  width: 44px; height: 44px; border-radius: 10px;
  background: transparent; border: 1px solid var(--line); cursor: pointer;
}
@media (min-width: 940px) { .navtoggle { display: none; } }
.mobilenav { display: none; border-top: 1px solid var(--line); background: var(--paper); }
.mobilenav.is-open { display: block; }
.mobilenav ul { list-style: none; margin: 0; padding: .5rem 0 1rem; }
.mobilenav a {
  display: block; padding: .8rem 0; text-decoration: none;
  color: var(--ink); font-weight: 650; border-bottom: 1px solid var(--line);
}

/* -------------------------------------------------------------- footer -- */
.site-footer { background: var(--forest); color: #C6DCD0; padding: 3.5rem 0 2rem; }
.site-footer h2, .site-footer h3 { color: #fff; }
.site-footer a:not(.btn) { color: #9FE3FF; }   /* same trap as above */
.footer-grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.footer-grid h3 { font-size: .78rem; letter-spacing: .13em; text-transform: uppercase; margin-bottom: .8rem; color: #7FE06F; }
.footer-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; font-size: .95rem; }
.footer-bottom {
  margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid rgba(255,255,255,.14);
  display: flex; gap: 1rem; flex-wrap: wrap; justify-content: space-between;
  font-size: .84rem; color: #90A99D;
}

/* --------------------------------------------------------- utilities ---- */
.pill {
  display: inline-flex; align-items: center; gap: .4rem;
  background: var(--paper-2); border: 1px solid var(--line);
  border-radius: 999px; padding: .3rem .75rem; font-size: .82rem;
  font-weight: 650; color: var(--ink-soft);
}
.callout {
  border: 1px solid var(--line); border-left: 4px solid var(--sky);
  background: #fff; border-radius: var(--radius-sm);
  padding: 1.1rem 1.25rem; font-size: .97rem;
}
.callout--warn { border-left-color: var(--ember-bright); background: #FFF8F5; }
.callout h3 { font-size: 1.02rem; margin-bottom: .45rem; }

.quote {
  font-family: var(--display); font-size: clamp(1.4rem, 3.4vw, 2.1rem);
  line-height: 1.18; letter-spacing: -.02em; margin: 0;
}
.quote cite { display: block; font: 400 .92rem/1.5 var(--sans); color: var(--ink-faint); margin-top: 1rem; font-style: normal; }
.section--forest .quote cite { color: #90A99D; }

/* ------------------------------------------------------ reduced motion -- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .hero__canvas { display: none; }
  .shot:hover img { transform: none; }
}

/* Reveal-on-scroll. Starts visible so that with JS off, or before the
   observer runs, nothing is hidden — the class is added by script only. */
.reveal { opacity: 1; }
.js .reveal { opacity: 0; transform: translateY(14px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; }
}

/* A verse sets its own line breaks, so the display class's text-wrap: balance
   fights it — the container is narrower than the longest line and balance
   then re-wraps "…the man or / woman" into a ragged second line. Each line is
   its own block, sized to fit rather than to fill, and a long line wraps with
   a hanging indent instead of pretending it did not. */
.verse { display: grid; gap: .1em; }
.verse span {
  display: block;
  text-wrap: initial;
  /* Sized so the longest line — "who steals the goose from off the common," —
     sits on ONE line in the widened column. It was breaking mid-line, and a
     hanging indent then threw the remainder to the right, which reads as a
     layout fault rather than as verse. */
  font-size: clamp(1.02rem, 2.1vw, 1.55rem);
  line-height: 1.32;
  padding-left: .9em;
  text-indent: -.9em;
}

/* The verse is the argument; the photograph beside it is support. The default
   near-even split left the text column too narrow for a single line of it. */
@media (min-width: 900px) {
  .split--verse { grid-template-columns: 1.42fr .58fr; }
}

/* -------------------------------------------------- YouTube facade ------ */
/* A still image and a button, not an iframe. YouTube's embed sets cookies and
   contacts Google the moment it renders, whether or not anyone presses play,
   so a page carrying two of them tracks every visitor on behalf of a third
   party for a video most of them will never watch. The iframe is created on
   click, against youtube-nocookie.com, with autoplay so the press that
   loaded it also starts it. */
.ytfacade { position: relative; cursor: pointer; background: #000; }
.ytfacade img { width: 100%; aspect-ratio: 16/9; object-fit: cover; display: block; }
.ytfacade__btn {
  position: absolute; inset: 0; width: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .7rem; border: 0; cursor: pointer; color: #fff;
  font-weight: 750; font-size: .95rem; text-shadow: 0 1px 12px rgba(0,0,0,.7);
  background: linear-gradient(rgba(6,16,12,.15), rgba(6,16,12,.5));
  transition: background-color .2s ease;
}
.ytfacade__btn:hover { background: linear-gradient(rgba(6,16,12,.05), rgba(6,16,12,.4)); }
.ytfacade__play {
  width: 62px; height: 62px; border-radius: 50%;
  background: var(--green-btn); display: grid; place-items: center;
  box-shadow: 0 6px 24px rgba(0,0,0,.4);
}
.ytfacade__play::after {
  content: ''; border-style: solid; border-width: 11px 0 11px 18px;
  border-color: transparent transparent transparent #fff; margin-left: 4px;
}
.ytfacade iframe { width: 100%; aspect-ratio: 16/9; border: 0; display: block; }

/* Match the canvas's focal bias (see hero.js focusX): as the viewport narrows
   the cover crop discards more width, and a centred crop loses the two people
   on the bank — the whole reason this photograph is the hero. */
@media (max-width: 700px) {
  .hero__img { object-position: 34% 45%; }
}

/* WCAG 2.2 SC 2.5.8 (Target Size, 24x24). The inline exception covers a link
   inside a sentence, but a footer NAV LIST is not a sentence — each item is a
   standalone target, and at the body line-height they came out 20px tall. The
   padding takes them over 24 without changing how the column looks, because
   the negative margin pulls the row back to its original rhythm. */
.footer-grid ul { gap: .15rem; }
.footer-grid ul a {
  display: inline-block;
  padding-block: .18rem;
  min-height: 24px;
}

/* ------------------------------------------------- narrow header --------- */
/* At 320px — which is 400% zoom on a 1280px screen, the WCAG 1.4.10 reflow
   case — the brand, the call to action and the menu button could not fit on
   one row. `.btn` sets white-space: nowrap and the header row does not wrap,
   so instead of reflowing they pushed the document to 438px and EVERY page
   scrolled sideways. body{overflow-x:hidden} hid the scrollbar and made it
   look fine, which is why it survived the first mobile screenshots: the
   full-page capture came out 438px wide for a 390px viewport and nothing
   said so.
   The fix keeps all three controls — identity, primary action, navigation —
   and shrinks them, rather than dropping one. */
@media (max-width: 430px) {
  .wrap { width: min(100% - 1.6rem, var(--maxw)); }
  .site-header__inner { gap: .5rem; }
  .brand { min-width: 0; }
  .brand img { width: 34px; height: 34px; }
  .brand__text { min-width: 0; }
  .brand__text span:first-child { font-size: .58rem; letter-spacing: .1em; }
  .brand__text span:last-child { font-size: .84rem; }
  .header-cta { padding: .5rem .8rem; font-size: .85rem; }
  .navtoggle { width: 38px; height: 38px; }
}
/* Below ~360px the wordmark is what has to give: the logo already carries the
   identity and the two controls have to stay reachable. */
@media (max-width: 359px) {
  .brand__text { display: none; }
}
.cta-short { display: none; }
@media (max-width: 430px) {
  .cta-long { display: none; }
  .cta-short { display: inline; }
}

/* Grid and flex children default to min-width:auto, which means they refuse to
   shrink below their min-content size. A grid item containing a 1400px-wide
   <img> therefore holds the track open at well over the viewport, and the
   whole document scrolls sideways — the `.split` figure/timeline row did
   exactly this and was the last horizontal overflow at 320px. min-width:0 lets
   them shrink; max-width:100% on the image then does the rest. */
.split > *, .grid > *, .versus > *, .site-header__inner > *, .footer-grid > * {
  min-width: 0;
}

/* .btn is nowrap so short labels never break into a ragged two-line pill. A
   long label then cannot fit a narrow screen — "The full story, and what
   Thames Water has said" held the home page at 372px against a 305px body.
   Below the phone breakpoint, let them wrap: short labels still fit on one
   line at that width, so only the long ones are affected. */
@media (max-width: 430px) {
  .btn { white-space: normal; text-align: center; }
}

/* ------------------------------------------------ hero composition ------ */
/* The two people sit about a third of the way across the photograph, and the
   headline was landing straight on top of them. The alternative was to flip
   the image, which is the wrong fix here: this is a real photograph of a
   specific view, and mirroring it puts the Eye, the Shard and the City
   cluster on the wrong side of London. Anyone who knows the view would see
   it immediately, on a campaign whose whole claim is about that view.
   So the text moves instead. On a wide screen it sits in the right-hand
   column, which also puts it in the direction the two of them are looking —
   the subject reads into the words rather than being covered by them. */
@media (min-width: 900px) {
  /* Darken only the side the words are on. Measured against the rendered
     backdrop with the text hidden, the headline sat at 2.21:1 over the gold
     sky — a text-shadow would have hidden that from the eye without fixing
     it, since a shadow does not count toward a contrast ratio. */
  .hero__scrim {
    background:
      linear-gradient(to left, rgba(6,16,12,.66) 0%, rgba(6,16,12,.52) 34%,
                      rgba(6,16,12,.12) 62%, rgba(6,16,12,0) 78%),
      linear-gradient(to top, rgba(6,16,12,.90) 0%, rgba(6,16,12,.62) 28%,
                      rgba(6,16,12,.18) 58%, rgba(6,16,12,.34) 100%);
  }
  .hero__content { margin-left: auto; width: min(58ch, 63%); }
  /* Sized to the narrower column, not to the viewport. At the full 5.6rem the
     headline broke into five lines and pushed the primary call to action onto
     the fold on a 900px-tall screen — the one thing in the hero that must
     stay visible. */
  .hero h1 { max-width: 100%; font-size: clamp(2.6rem, 5vw, 4.4rem); }
}

/* ------------------------------------------------------- MP picker ------ */
.mp-picker { border: 0; padding: 0; margin: 1.1rem 0 0; display: grid; gap: .55rem; }
.mp-opt {
  display: flex; gap: .7rem; align-items: flex-start;
  padding: .8rem .9rem;
  border: 1.5px solid var(--line); border-radius: var(--radius-sm);
  background: var(--paper); cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
.mp-opt:hover { border-color: var(--ink-faint); }
.mp-opt input { margin-top: .25rem; flex: none; width: 1.1rem; height: 1.1rem; accent-color: var(--green-btn); }
/* :has is what lets the whole row show selection without a class from script.
   Where it is unsupported the radio itself still shows the state, so nothing
   is lost — the row simply does not tint. */
.mp-picker:has(input:checked) .mp-opt:has(input:checked) {
  border-color: var(--green-btn);
  background: #fff;
  box-shadow: 0 0 0 3px rgba(18,128,13,.10);
}
.mp-opt > span { display: block; font-size: .95rem; }
.mp-note { display: block; font-size: .85rem; color: var(--ink-faint); margin-top: .2rem; line-height: 1.45; }
/* Every address is printed, not only the selected one. With JavaScript off
   the radios do nothing, and without this the two addresses the script would
   have written are nowhere on the page at all — a supporter in Lewisham would
   have no way to reach their own MP. */
.mp-mail {
  font-family: var(--mono); font-size: .8rem; color: var(--ink-soft);
  display: inline-block; margin-top: .3rem; word-break: break-all;
}
/* Shown only when a switch was declined because the reader had edited the
   letter. It offers the swap; it never performs it. */
.tpl-note {
  margin: 0 0 .9rem; padding: .7rem .9rem;
  border-left: 3px solid var(--ember-bright); background: #FFF8F5;
  border-radius: var(--radius-sm); font-size: .89rem; color: var(--ink-soft);
}

/* ------------------------------------------------------- pull quote ----- */
/* Used for the golf club's own description of its course. It is set as a
   quotation and attributed with a link, because its whole force comes from
   being their words rather than the campaign's — the moment it reads as our
   paraphrase it is worth nothing. */
.pullquote {
  margin: 2.4rem 0 0;
  padding: clamp(1.4rem, 3vw, 2.2rem);
  background: #fff;
  border: 1px solid var(--line);
  border-left: 5px solid var(--green);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-width: 68ch;
}
.pullquote blockquote { margin: 0; }
.pullquote blockquote p {
  font-size: clamp(1.05rem, 2.1vw, 1.28rem);
  line-height: 1.5;
  color: var(--ink);
  margin: 0 0 1rem;
}
.pullquote blockquote p:last-child { margin-bottom: 0; }
.pullquote blockquote b { color: var(--ember); font-weight: 750; }
.pullquote figcaption {
  margin-top: 1.2rem; padding-top: .9rem;
  border-top: 1px solid var(--line);
  font-size: .87rem; color: var(--ink-faint);
}

/* On a short window the hero's own content is taller than the viewport, so
   the top padding — which exists only to clear the 62px sticky header — is
   what pushes the call to action towards the fold. Give it back on short
   screens; 5rem still clears the header comfortably. */
@media (max-height: 780px) {
  .hero__inner { padding-top: 5rem; }
}

/* --------------------------------------------------------- table -------- */
/* The cookie table is the one table on the site. It scrolls inside its own
   container rather than widening the page — a three-column table cannot fit
   320px and must not be what reintroduces horizontal scroll. */
.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; }
.ctable { width: 100%; border-collapse: collapse; font-size: .93rem; min-width: 34rem; }
.ctable th, .ctable td { text-align: left; padding: .8rem .9rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.ctable thead th { background: var(--paper-2); font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }
.ctable tbody tr:last-child td { border-bottom: 0; }
.ctable td:first-child { white-space: nowrap; }
.ctable code { font-family: var(--mono); font-size: .85rem; color: var(--green-ink); }

/* ------------------------------------------------------ footer signup --- */
/* Sits above the footer columns on every page. A campaign that only collects
   addresses on its home page collects far fewer of them. */
.signup {
  display: grid; gap: 1.2rem;
  padding-bottom: 2.2rem; margin-bottom: 2.2rem;
  border-bottom: 1px solid rgba(255,255,255,.14);
}
@media (min-width: 820px) {
  .signup { grid-template-columns: 1fr auto; align-items: center; gap: 2rem; }
}
.signup__form { display: flex; gap: .6rem; flex-wrap: wrap; align-items: flex-start; }
.signup__form input[type=email] {
  flex: 1 1 16rem; min-width: 0;
  padding: .8rem .9rem;
  border: 1.5px solid rgba(255,255,255,.28);
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,.06); color: #fff;
}
.signup__form input[type=email]::placeholder { color: #8FA79B; }
.signup__form input[type=email]:focus {
  outline: none; border-color: #7FE06F;
  box-shadow: 0 0 0 3px rgba(127,224,111,.18);
  background: rgba(255,255,255,.10);
}
.signup__status { flex: 1 0 100%; margin: 0; font-size: .88rem; color: #C6DCD0; }
.signup__status[data-state="error"] { color: #FFB59B; }
.signup__status[data-state="ok"] { color: #9BE88C; }
