# Laurence Peberdy

I lead product at Bankuet, working across research, product direction and software development.

I built our donor application, led the redesign of our food bank portal, and independently made Research Atlas, a visual tool for navigating academic research.

Canonical portfolio: https://laurence-peberdy.com

Contact: [email](mailto:laurence.peberdy@googlemail.com) · [GitHub](https://github.com/LPeberdy) · [LinkedIn](https://www.linkedin.com/in/lpeberdy/)

A reading copy of the introduction and selected work. [Portfolio JSON](https://laurence-peberdy.com/portfolio.json) and [llms.txt](https://laurence-peberdy.com/llms.txt) also include structured measurement metadata.

## Selected work

- [Continuous integration and deployment (CI/CD)](#project-reliability)
- [Donor experience rebuild](#project-donor)
- [Research Atlas](#project-atlas)
- [Food bank request portal](#project-portal)
- [Flexible funding propositions](#project-funding)

<a id="project-reliability"></a>

## Bankuet: Continuous integration and deployment (CI/CD)

> When we change Bankuet's donor app, Vercel turns the repository code into a version that could go live. I built checks around that path so broken behaviour or secrets are more likely to be caught before release.

Canonical HTML: https://laurence-peberdy.com/w/reliability

Project: [Bankuet](https://bankuet.co.uk)

Project date: 4 September 2026

### At a glance

#### My role

I designed and implemented Bankuet.co.uk's CI/CD pipeline end to end.

#### Decision

Use overlapping checks rather than trust a successful build alone.

#### Outcomes

Source and build secret scans · normal releases held until required checks pass

<a id="reliability--summary"></a>

### Context

When we change Bankuet's donor application, we change the code in GitHub. Vercel then turns that code into the version that could go live — a build.

A build can finish successfully and still be wrong. A donation page can pass the wrong fund, generated files can accidentally contain credentials or other secret values, or the deployed app can behave differently from local tests. I used a **Swiss cheese approach**: several imperfect checks overlap, so one can catch what another misses.

<a id="reliability--problem"></a>

### Problems

Every change follows the same path: we commit code to GitHub, Vercel builds a version of the app, and that version can then be promoted to the live site. Each step can succeed while the result is still broken or unsafe.

- **The app can build and still behave incorrectly.** Discovery can fail, or a food bank page can load but pass the wrong fund identifier to its donation form.
- **The files that go live are not only the files we wrote.** The build generates pages and scripts, so reviewing source code alone cannot show everything that may be published.
- **What works locally can fail after deployment.** Vercel runs the candidate in a different environment, so the exact version that could go live also needs testing.

<a id="reliability--constraints"></a>

### Constraints

The checks cannot prove safety by repeating real production activity. They have to catch problems without leaking secrets, making real donations or bookings, or taking the current site offline:

- **Our post-build secret scan cannot inspect Vercel's build.** It can inspect the controlled local/CI build; Vercel makes a separate build with its real environment.
- **Tests cannot expose the secrets they are meant to protect.** The workflow cannot put credentials, bypass secrets or donor data in URLs, permanent headers, traces or uploaded artifacts.
- **Integration checks cannot submit real donations or bookings.** They have to test the handoffs without performing the real action or making releases depend on a third party being available.
- **Testing the next version cannot take the current site offline.** The existing deployment stays public until the candidate passes both main CI and the deployment check.

<a id="reliability--hypothesis"></a>

### Working hypothesis

If we check the same change at several points — the source code, the files the build generates, browser behaviour, and the exact Vercel version that could go live — one check can catch what another misses and reduce the chance that broken software or a secret reaches production.

<a id="reliability--solution"></a>

### Decisions and delivery

The pipeline follows the path of a release. It checks the source before the build, checks what the build generates, and then tests the exact Vercel deployment before that version can replace the live site.

#### Run the same checks locally and in GitHub

One command runs both on a developer machine and in GitHub Actions. It checks environment-variable documentation, scans source for secrets, applies the dependency policy, checks TypeScript and lint warnings, and runs unit, integration, and browser tests. If any check fails, the sequence stops.

The repository therefore defines the acceptance process; nobody has to reconstruct a checklist before publishing. Known exceptions have an owner and an expiry date.

#### Inspect what the build generates

Secretlint scans the source before the build. After the controlled local/CI build, the pipeline also scans the text that build creates: server output, client assets, and selected deployment manifests. This can catch credentials or other secret-like text that was not obvious in the authored source.

A finding stops the check and reports the file and rule while masking the suspected secret. An inert test fixture verifies both detection and masking. Vercel builds separately with its real environment; this scan does not inspect Vercel's output.

#### Check dependencies and app behaviour

The dependency check combines npm's audit with first-party Next.js advisories. High or critical findings block the change unless an accepted exception covers them. If an advisory source is unavailable or unreadable, the check fails rather than silently passing.

Vitest runs unit and integration tests. Playwright then opens a fresh production build using synthetic data and local service fixtures. It checks discovery, public routes, and the fund identifier passed to donation forms. Live donation forms and analytics stay disabled.

#### Test the exact version that could go live

Vercel builds a production candidate while the previous deployment stays public. A trusted workflow confirms that the candidate belongs to the right project and commit, then tests that exact deployment. Normal promotion requires both main CI and the candidate check to pass.

Those checks cover key pages and redirects, public discovery APIs, required security headers, the Beacon donation-form and Mapbox handoffs, and Calendly discovery. Selected third-party responses are controlled so we can test Bankuet's side of each integration without submitting donations or bookings, or making releases depend on provider availability.

#### Keep test access safe and retain human control

Test access is limited to the validated production candidate. The browser gains access without putting the bypass secret in a URL or permanent request header. Protected traces are disabled; the workflow uploads no credentials or donor data as review evidence.

The release policy also includes a documented human override and rollback procedure. Automated checks support, rather than replace, operational responsibility.

<a id="reliability--pipeline"></a>

### Example pipeline run

A typical change moves through five stages. CI and production smoke do different jobs: CI checks the repository and a controlled build; production smoke checks the exact Vercel deployment that could become live.

#### Open a pull request

A proposed change is opened in GitHub. Nothing has changed in production yet; this creates the reviewable version of the work that the rest of the pipeline follows.

- Reviewable code change
- No production change

#### CI + preview

**GitHub Actions** runs the main acceptance checks against the repository and a controlled build. **Vercel** also builds a preview so the change can be inspected before merge.

- Lint + TypeScript
- Unit, integration + browser tests
- Source secret scan
- Generated-file secret scan
- Dependency security checks
- Vercel preview

#### Merge into main

Once the change is approved, it is merged into the main branch. That starts the release path, but the new version is still not public.

- Approved change
- Release workflow starts
- Current production stays live

#### Main CI + production smoke

**CI runs again** on the merged change. Vercel builds the production candidate, and **production smoke** checks that exact deployed version — not the controlled CI build — before it can replace the current site.

- CI — code + types
- CI — test suite
- CI — secret scans
- CI — dependency checks
- Smoke — key pages + redirects
- Smoke — APIs + security headers
- Smoke — Beacon, Mapbox + Calendly handoffs

#### Promote to production

Normal promotion happens only when main CI and the production smoke check have both passed. Vercel then makes the tested candidate live; the previous deployment remains public until that point.

This is why the checks overlap rather than duplicate one another: CI looks for problems in code and a controlled build; production smoke asks whether the exact version about to go live behaves as expected.

- Both checks passed
- Tested candidate becomes live
- Human override + rollback remain available

<a id="reliability--outcomes"></a>

### Outcomes

The pipeline makes acceptance checks repeatable and holds normal releases until the required checks pass.

<a id="reliability--bankuet-reliability-shared-checks"></a>

- Local development and GitHub share **one acceptance command**, including source and build-output secret scans.

<a id="reliability--bankuet-reliability-held-release"></a>

- A controlled production proof on **4 September 2026** recorded the previous deployment staying public while checks were pending. The candidate went live only after the required checks passed.

The layers reduce reliance on any single check. They do not prove the application is free of vulnerabilities or that a live payment will complete.

Published: 5 September 2026

Updated: 7 September 2026

[JSON source and measurement metadata](https://laurence-peberdy.com/projects/reliability.json)

<a id="project-donor"></a>

## Bankuet: Rebuilding the donor experience

> I led and built a three-month visual and technical rebuild of Bankuet's donor application, reducing donation from five steps to three and technical food bank setup from about 45 to 10 minutes.

Canonical HTML: https://laurence-peberdy.com/w/donor

Project: [Bankuet](https://bankuet.co.uk)

Project date: October 2025

### At a glance

#### My role

I led product direction and architecture, recommended the move from Squarespace and built the production application. I worked with our fractional UX Lead and secured CEO buy-in.

#### Decision

Build an application we could adapt, but retain Beacon's forms rather than building a bespoke payment layer.

#### Outcomes

Technical food bank setup ~45 → 10 min · donation 5 → 3 steps

![Bankuet's food bank discovery interface, with a map and a list of food banks to support.](https://laurence-peberdy.com/images/donor-service-find-a-food-bank.png)

Food bank discovery in the redesigned donor application.

<a id="donor--summary"></a>

### Context

Bankuet's donor experience lived in Squarespace. Changing donation options meant working around its page and form model; adding a food bank or fundraising campaign meant repeating setup; and the site connected awkwardly to the services behind it.

I moved the donor experience into an application we controlled, while keeping specialist services such as Beacon for payments and CRM. That made the experience easier to change, reduced setup work, and gave a small team a codebase it could keep extending.

<a id="donor--problem"></a>

### Problems

- **Changing the donation journey was hard.** Donation options and steps were constrained by the Squarespace setup, so adapting the flow as Bankuet's needs changed was cumbersome.
- **Adding food banks and campaigns repeated the same setup work.** Technical food bank setup and fundraising campaign setup both required manual configuration.
- **The website and operational systems were loosely connected.** The donor site did not integrate cleanly with Beacon CRM and the other services Bankuet relied on.

<a id="donor--constraints"></a>

### Constraints

Rebuilding the app did not mean replacing every service. Three constraints shaped the architecture:

- **The application needed to be extensible.** It had to connect cleanly to multiple data sources and services, adapt as Bankuet's needs changed, and support future initiatives without another rebuild.
- **The payment system had to stay proportionate to our scale.** We needed flexible giving and Direct Debit without taking on the implementation and operational risk of a bespoke payment stack unless that extra control justified it.
- **A small team needed to operate and adapt it.** The application had to stay straightforward to run and change without a large engineering team.

<a id="donor--hypothesis"></a>

### Working hypothesis

If we owned the application layer but reused specialist services where they were strong, we could simplify giving, remove repeated setup work, and connect the donor experience to operational data without creating a system the team could not maintain.

<a id="donor--solution"></a>

### Decisions and delivery

I recommended moving the donor experience out of Squarespace and built a Next.js app on Vercel. Rather than rebuild every capability, I connected it to existing services where that reduced risk or made the product easier to operate.

- **Beacon rather than a bespoke payment layer.** Rather than own card and Direct Debit flows, I embedded Beacon CRM donation forms. I had considered handling payments directly with Stripe and Supabase, but Beacon gave us flexible giving, Direct Debit and closer CRM integration with less implementation and operational risk.
- **Supabase and Mapbox for food bank discovery.** A Supabase Edge Function pulls food bank data from Beacon; Mapbox uses it for location search and maps on the donor site.
- **Notion rather than a custom CMS.** Colleagues already managed blog content in Notion, so I used it as the content system and retrieved posts through Notion's API. This reduced build scope and kept editorial control with non-technical colleagues.
- **Look and feel.** With our UX Lead, I implemented the visual redesign and standardised common sections in Figma and code. This made the experience more consistent and pages faster to design and build.

I also led structured pre-launch QA across pages and screen sizes.

<a id="donor--outcomes"></a>

### Outcomes

The redesigned site launched in October 2025. The clearest measured gains were in technical food bank setup and the donation flow:

<a id="donor--bankuet-donor-onboarding-time"></a>

- Technical food bank setup time fell from approximately **45 minutes to 10 minutes**.

<a id="donor--bankuet-donor-campaign-setup-time"></a>

- Fundraising campaign setup went from around **one day to effectively instant**.

<a id="donor--bankuet-donor-checkout-steps"></a>

- The donation flow was reduced from **five steps to three**.

Conversion measurement remains limited: the embedded Beacon donation form and application analytics sit across different systems. This case does not establish a conversion uplift attributable to the rebuild.

Published: 5 August 2026

Updated: 6 September 2026

[JSON source and measurement metadata](https://laurence-peberdy.com/projects/donor.json)

<a id="project-atlas"></a>

## Research Atlas: Designing a visual search tool for interdisciplinary research

> Entering a field is difficult before you know its terminology. I independently built and evaluated a visual tool for finding the topics, papers and relationships that help establish that context.

Canonical HTML: https://laurence-peberdy.com/w/atlas

Project: Master's capstone · [London Interdisciplinary School](https://www.lis.ac.uk)

Project date: 2025

### At a glance

#### My role

I independently researched, designed and built Research Atlas as my Master's capstone at the London Interdisciplinary School.

#### Decision

Represent relationships between topics and papers, rather than produce another ranked list of search results.

#### Evaluation

Users valued finding papers they would not have thought to search for.

[Open Research Atlas](https://www.researchatlas.app)

![Research Atlas displaying an interactive map of academic topics and connected works.](https://laurence-peberdy.com/images/research-atlas.png)

A map of academic topics and the works connecting them.

<a id="atlas--summary"></a>

### Context

A researcher entering an unfamiliar field often starts with only a rough question. Conventional search immediately asks for keywords, but the useful keywords, authors and concepts are often exactly what the researcher is trying to discover.

I built Research Atlas to start from that rough question, retrieve relevant papers, and show the topics and relationships between them. The point is to help a researcher learn what to search for next, not just rank a list of papers.

<a id="atlas--problem"></a>

### Problems

- **Search assumes you already know the language of the field.** Effective queries depend on terms users may still be trying to discover.
- **A ranked list hides the shape of the literature.** Long result lists make it hard to see clusters, relationships or how papers connect.
- **Useful work can sit outside the discipline where the search starts.** Relevant papers may use different terminology or belong to another field.
- **The reason a result appears is hard to see.** Conventional ranking gives users little visibility into the relationships behind a result.
- **Understanding builds across sessions.** Researchers need to keep useful papers, topics and maps rather than restart each time.

<a id="atlas--constraints"></a>

### Constraints

The product also has three hard limits:

- **It was a solo capstone.** The scope had to fit research, product design, engineering and evaluation into one project.
- **Research Atlas depends on OpenAlex.** It can organise and relate the works and topics OpenAlex exposes, but cannot control the source's coverage.
- **The graph has to stay readable.** Adding works can reveal useful connections and add noise at the same time, so broad maps are capped at about 200 works and Analysis at about 100 per topic.

<a id="atlas--hypothesis"></a>

### Working hypothesis

If the tool turns a rough question into a visible map of papers and topics, reveals relationships progressively, and saves useful connections, users should find unfamiliar fields easier to navigate and discover work they would not have thought to search for.

<a id="atlas--solution"></a>

### Decisions and delivery

I tested the relevance of OpenAlex search results using 30 queries across different subject areas. Those checks helped me decide how much material the maps could show before useful connections were lost in noise: about 200 works for broad exploration and 100 per topic in the more focused Analysis flow.

I built the application with Next.js. Neo4j stores the network of papers and topics; Supabase handles accounts and saved content; D3 draws the maps; and Vercel hosts the application.

<a id="atlas--walkthrough"></a>

### Reading the product

The product has three workflows:

#### 01 — Search from a question

Start with a keyword or phrase. The Search flow retrieves relevant works and maps the topics and relationships within them, providing a view beyond a ranked list.

#### 02 — Analyse a paper

Start with a paper's title and abstract. The Analysis flow identifies its core topics and surfaces related research around each one.

#### 03 — Keep useful connections

The Archive saves works, topics and maps for later sessions.

<a id="atlas--outcomes"></a>

### Evaluation

I evaluated the tool with an academically oriented group and recorded usage and performance over a 30-day period.

<a id="atlas--research-atlas-user-discovery"></a>

- Structured feedback repeatedly identified **unexpected discovery** as the strongest source of value.

<a id="atlas--research-atlas-visits"></a>

- The application recorded **115 visits**.

<a id="atlas--research-atlas-real-experience-score"></a>

- Vercel Speed Insights reported a **96/100 Real Experience Score**.

Users described finding connected research areas and papers they would not have thought to explore.

The evaluation also highlighted areas to improve: map readability, relevance controls, source coverage, exports and the explanation of the tool's exploratory purpose.

I did not test willingness to pay or institutional demand, so commercial viability remains an open question.

Published: 5 August 2026

Updated: 6 September 2026

[JSON source and measurement metadata](https://laurence-peberdy.com/projects/atlas.json)

<a id="project-portal"></a>

## Bankuet: Giving food banks control over supply requests

> Food banks could set a budget but not choose exact quantities. I led the shift to visible prices and case quantities, reducing weekly request processing from about four hours to one.

Canonical HTML: https://laurence-peberdy.com/w/portal

Project: [Bankuet](https://bankuet.co.uk)

Project date: 9 July 2024

### At a glance

#### My role

I set direction, priorities and scope, and worked with our in-house UX Lead in Figma. I directed an external 2–4-person team across business analysis, engineering and QA, bringing in architecture support as needed.

#### Decision

Stop optimising an allocation algorithm and let food banks choose exact quantities against visible prices.

#### Outcomes

Weekly processing ~4h → 1h · average underspend ~7% → 0%

![Bankuet's request interface, showing products that food banks can request by case quantity.](https://laurence-peberdy.com/images/foodbank-service-request-a-delivery.png)

Choosing products by case quantity in the redesigned request interface.

<a id="portal--summary"></a>

### Context

Before v3, a food bank did not tell Bankuet exactly how many cases of each product it wanted. It set a total budget and marked products high, medium or low priority. An algorithm chose the quantities, and our team often had to interpret or adjust the result.

I replaced that model with visible prices and exact case quantities. Food banks could see the cost of their choices and submit the request they actually wanted; Bankuet spent less time fixing the output.

<a id="portal--problem"></a>

### Problems

- **Food banks could express priorities, not quantities.** They could say what mattered most, but not how many cases they wanted.
- **They could not see the cost of each choice.** Without unit and case prices, users could not tell how a product affected their budget.
- **Bankuet had to repair the algorithm's output.** Allocations could underspend the budget or need manual adjustment before ordering.
- **Precise requests needed better financial context.** Food banks had limited information about previous requests, spending and their available balance.

The evidence came from food bank feedback and my own experience processing Bankuet's supply orders each week.

<a id="portal--constraints"></a>

### Constraints

The request form is not an isolated interface; it feeds Bankuet's weekly ordering operation. Three constraints shape it:

- **Requests feed a live weekly workflow.** Food bank requests flow directly into Bankuet's supply-ordering process, so errors in the request flow can create operational work as well as user confusion.
- **Exact quantities depend on trustworthy financial context.** Food banks need reliable balances and visible prices before more granular control is useful.
- **Prices can change before fulfilment.** The design needs a workable tolerance for changes between request and fulfilment rather than implying every displayed price is fixed.

<a id="portal--hypothesis"></a>

### Working hypothesis

If food banks can choose exact quantities against visible prices, requests should become more accurate and understandable, reducing manual intervention and algorithmic budget underspend.

<a id="portal--solution"></a>

### Decisions and delivery

I did not jump straight to exact quantities. First I made balances, request history and spending clearer, because precise choices only help if the user can trust the budget they are spending.

With those foundations in place, **v3.0 — Volumes and Values** launched on **9 July 2024**, with:

- unit and case prices alongside each product;
- exact case quantities rather than none/low/medium/high preferences;
- a persistent basket that could be continued across sessions and accounts;
- an additional-supplies route for needs outside Bankuet's normal range.

I kept v3.0 focused on request creation, leaving better search, further request-history improvements and help content for later releases.

I ran research with food banks to test what price changes between request and fulfilment they would accept. We also released v3 to a small group before wider rollout to reduce operational risk.

<a id="portal--comparison"></a>

### What changed

The request model before and after v3:

#### Before — describe priorities

- The food bank chooses an overall budget.
- Products receive high, medium or low priorities.
- An algorithm allocates quantities; Bankuet interprets or adjusts the result.

#### After — choose quantities

- The food bank sees unit and case prices.
- It chooses exact case quantities in a persistent basket.
- The request expresses the supplies wanted rather than a set of relative preferences.

<a id="portal--outcomes"></a>

### Outcomes

The strongest outcomes were operational:

<a id="portal--bankuet-portal-processing-time"></a>

- Weekly request-processing time fell from approximately **four hours to one hour** — a 75% reduction.

<a id="portal--bankuet-portal-budget-underspend"></a>

- Average budget underspend fell from approximately **7% to 0%**, comparing the three months before launch with the three months after.

<a id="portal--bankuet-portal-support-queries"></a>

- Finance- and request-related support queries fell by approximately **50%** across the wider v2→v3 programme — not v3 alone.

### Showcase

[How Bankuet Works For Food Banks](https://vimeo.com/1012782893)

An animated explanation of how Bankuet turns donations into the supplies food banks need most.

How Bankuet Works For Food Banks · 2:20

Published: 5 August 2026

Updated: 6 September 2026

[JSON source and measurement metadata](https://laurence-peberdy.com/projects/portal.json)

<a id="project-funding"></a>

## Bankuet: Creating more flexible ways to fund the mission

> I introduced a general fund in 2020 for donors who did not want to choose a food bank, and Support Bankuet in 2022 for direct, unrestricted donations to the organisation.

Canonical HTML: https://laurence-peberdy.com/w/funding

Project: [Bankuet](https://bankuet.co.uk)

Project date: 2020–2022

### At a glance

#### My role

I originated the **General Fund** and **Support Bankuet** propositions and led their product design, launch and messaging.

#### General Fund

**Decision.** Let donors support food banks without choosing an individual one.

**Result.** A flexible pool of donations for food banks most in need.

#### Support Bankuet

**Decision.** Offer a separate direct-support donation after the primary donation.

**Result.** Unrestricted funding for external engineering capacity.

<a id="funding--summary"></a>

### Context

Bankuet's original donor journey began with a choice: which food bank do you want to support? That worked for donors with a specific food bank in mind, but not for everyone.

I introduced two additional ways to give at different points in the journey. The General Fund let donors support food banks without choosing one. Standard donations already contributed to Bankuet through an operating fee; Support Bankuet added a separate, unrestricted way to support the organisation itself after the donor had funded food bank supplies.

<a id="funding--problem"></a>

### Problems

- **Some donors wanted to help without choosing a food bank.** The original journey still required them to select one before they could fund supplies.
- **Supporting Bankuet itself was a different ask.** Donors who wanted to support the organisation needed a route that could not be confused with money intended for food bank supplies.

<a id="funding--constraints"></a>

### Constraints

Both propositions sit beside the main reason people come to Bankuet: funding food bank supplies. That created three constraints:

- **Keep the purposes distinct.** Donors needed to understand whether they were funding food bank supplies or supporting Bankuet itself.
- **Do not interrupt the primary donation.** Support Bankuet had to sit outside the main giving flow, so the additional ask came after the first donation was complete.
- **Keep the experiment cheap.** Support Bankuet was worth testing because it could use existing Beacon forms rather than require a bespoke payment build.

<a id="funding--hypothesis"></a>

### Working hypothesis

For the **General Fund**, if we removed the need to choose a food bank, donors without a preferred destination could still complete a donation while giving Bankuet a flexible pool to direct where it was most needed.

For **Support Bankuet**, if we made support for the organisation a separate ask after the primary donation, we could create unrestricted income without confusing it with money for food bank supplies.

<a id="funding--solution"></a>

### Decisions and delivery

#### 2020 — General Fund

I introduced a shared fund for donors who wanted to support the wider food bank network. Instead of asking them to choose a destination, Bankuet could direct those donations towards food banks most in need.

#### 2022 — Support Bankuet

I introduced a separate route for direct donations to Bankuet and showed it only after the donor had completed the primary donation. That kept the additional ask out of the main food bank giving flow.

I used Beacon CRM donation forms for this route, providing a low-risk live implementation before the later donor-platform rebuild.

<a id="funding--outcomes"></a>

### Outcomes

<a id="funding--outcomes-general-fund"></a>

#### General Fund

<a id="funding--bankuet-funding-general-fund-durability"></a>

- The **General Fund** created a flexible pool of donations that Bankuet can direct towards food banks most in need, while giving donors a route that does not require choosing an individual food bank.

<a id="funding--outcomes-support-bankuet"></a>

#### Support Bankuet

<a id="funding--bankuet-funding-support-bankuet-durability"></a>

- **Support Bankuet** generated unrestricted funding for external engineering capacity used in major iterations of the food bank portal.

<a id="funding--bankuet-funding-beacon-testbed"></a>

- Using **Beacon CRM** for Support Bankuet provided an early live test of its forms, payment flows and CRM behaviour, reducing uncertainty before the 2025 donor rebuild.

Published: 9 August 2026

Updated: 6 September 2026

[JSON source and measurement metadata](https://laurence-peberdy.com/projects/funding.json)
