PageSourceSearch

https://aaron-grieve-portfolio-2026.webflow.io/honesty-box

html aaron-grieve-portfolio-2026.webflow.io collected 2026-10-03 12:41:53 UTC 65,692 bytes, 621 lines download raw bytes

1<!DOCTYPE html><!-- This site was created in Webflow. https://webflow.com --><!-- Last Published: Tue Jun 23 2026 21:52:47 GMT+0000 (Coordinated Universal Time) --><html data-wf-domain="aaron-grieve-portfolio-2026.webflow.io" data-wf-page="6a1057363dfd062c0ed6640c" data-wf-site="68ea3c0288fc606920c55310"><head><meta charset="utf-8"/><link href="https://cdn.prod.website-files.com" rel="preconnect" crossorigin="anonymous"/><title>Honesty Box | Card Payments for Rural Sales Webflow &amp; Bubble</title><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow &amp; Stripe Connect. No hardware, no subscriptions—just scan and pay." name="description"/><meta content="Honesty Box | Card Payments for Rural Sales Webflow &amp; Bubble" property="og:title"/><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow &amp; Stripe Connect. No hardware, no subscriptions—just scan and pay." property="og:description"/><meta content="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder.jpg" property="og:image"/><meta content="Honesty Box | Card Payments for Rural Sales Webflow &amp; Bubble" name="twitter:title"/><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow &amp; Stripe Connect. No hardware, no subscriptions—just scan and pay." name="twitter:description"/><meta property="og:type" content="website"/><meta content="summary_large_image" name="twitter:card"/><meta content="width=device-width, initial-scale=1" name="viewport"/><meta content="Webflow" name="generator"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/css/aaron-grieve-portfolio-2026.webflow.shared.f489b4630.min.css" rel="stylesheet" type="text/css" integrity="sha384-9Im0Yw3z+X2zoVQhmxGh+a5ZbCo00zVFUUPiEP7IzjKWlNKQYyYB0kwlcE/iHQRz" crossorigin="anonymous"/><link href="https://fonts.googleapis.com" rel="preconnect"/><link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous"/>
1<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
1<script type="text/javascript">WebFont.load({  google: {    families: ["Allura:300,400,500,600,700","Inter:300,400,500,600,700"]  }});</script>
1<script src="https://use.typekit.net/vjl2cna.js" type="text/javascript"></script>
1<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
1<script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script>
1<link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10132dca1237ce8fde54fb_Bonsai%20Logo%20Small%20Black%20Square%403x.png" rel="icon" type="image/png" sizes="32x32" media="(prefers-color-scheme: light)"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a101351a6fee9a874b61e91_AG%20Logo%20White.png" rel="icon" type="image/png" sizes="32x32" media="(prefers-color-scheme: dark)"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10132d4b199d7fcf9b0228_Bonsai%20Logo%20Small%20Black%20Square%403x.png" rel="icon" type="image/png" sizes="48x48"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10132d4748a61a853db589_Bonsai%20Logo%20Small%20Black%20Square%403x.png" rel="apple-touch-icon" sizes="180x180"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10132dbbe2076d7d3c26cd_Bonsai%20Logo%20Small%20Black%20Square%403x.png" rel="icon" type="image/png" sizes="192x192"/><link href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10132dfb063cb7afa4b07a_Bonsai%20Logo%20Small%20Black%20Square%403x.png" rel="icon" type="image/png" sizes="512x512"/>
1<script type="application/ld+json">
2{
3  "@context": "https://schema.org",
4  "@type": "Article",
5  "headline": "Honesty Box: Card Payments for the Cashless Countryside",
6  "description": "A case study of HonestyBox, a QR code-based card payment system for rural honesty boxes built with Bubble, Webflow, and Stripe Connect. Enables cashless payments for farm stalls, campsites, driveways, and more without hardware or subscriptions.",
7  "url": "/honesty-box",
8  "author": {
9    "@type": "Person",
10    "name": "Aaron Grieve",
11    "url": "/"
12  },
13  "image": [
14    {
15      "@type": "ImageObject",
16      "url": "https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox.avif",
17      "caption": "Woman scanning QR code on honesty box stall holding eggs for card payments via mobile app"
18    },
19    {
20      "@type": "ImageObject",
21      "url": "https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b9fcb5e0e09cbee4811_app%20honesty%20box%20desktop.avif",
22      "caption": "HonestyBox app screen showing simple pricing: 5% per transaction plus Stripe fees"
23    },
24    {
25      "@type": "ImageObject",
26      "url": "https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105bff021e206f6ceb4596_WhatsApp%20Image%202026-04-27%20at%2012.32fds.11.avif",
27      "caption": "Slate sign reading Hill House Farm with QR code, eggs, rosemary, wheat, and honey jar on wooden table"
28    }
29  ],
30  "about": {
31    "@type": "SoftwareApplication",
32    "name": "HonestyBox",
33    "applicationCategory": "BusinessApplication",
34    "description": "QR code-based card payment system for rural honesty boxes. Enables cashless payments without hardware, subscriptions, or customer accounts.",
35    "url": "https://honestybox.app",
36    "offers": {
37      "@type": "Offer",
38      "price": "0",
39      "priceCurrency": "GBP",
40      "priceSpecification": {
41        "@type": "UnitPriceSpecification",
42        "price": "5",
43        "priceCurrency": "GBP",
44        "unitText": "PERCENT"
45      },
46      "description": "5% per transaction plus Stripe fees, no monthly subscription"
47    },
48    "featureList": [
49      "QR code payment system",
50      "No hardware required",
51      "No customer signup needed",
52      "Stripe Connect integration",
53      "Multi-box support",
54      "Apple Pay and Google Pay support",
55      "Real-time transaction dashboard",
56      "Laser-etched Welsh slate plaques"
57    ],
58    "operatingSystem": "Web-based"
59  },
60  "mentions": [
61    {
62      "@type": "Organization",
63      "name": "Stripe",
64      "sameAs": "https://stripe.com"
65    },
66    {
67      "@type": "SoftwareApplication",
68      "name": "Bubble",
69      "applicationCategory": "DeveloperApplication"
70    },
71    {
72      "@type": "SoftwareApplication",
73      "name": "Webflow",
74      "applicationCategory": "DeveloperApplication"
75    }
76  ],
77  "keywords": "Stripe Connect, Bubble, Webflow, QR payments, honesty box, cashless payments, rural commerce, payment marketplace"
78}
79</script>
79<style>
80  ::-moz-selection {
81    /* Code for Firefox */
82    color: black;
83    background: #00ffbf;
84  }
85
86  ::selection {
87    color: black;
88    background: #00ffbf;
89  }
90  .grain {
91    position: fixed;
92    inset: -10%;
93    width: 120%;
94    height: 120%;
95    pointer-events: none;
96    z-index: 9999;
97    opacity: 0.27;
98    background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 140 140' xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.75 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
99    background-size: 140px 140px;
100    background-repeat: repeat;
101    will-change: transform;
102    animation: grain-drift 8s steps(6) infinite;
103  }
104  @keyframes grain-drift {
105    0%,
106    100% {
107      transform: translate3d(0, 0, 0);
108    }
109    10% {
110      transform: translate3d(-3%, -4%, 0);
111    }
112    20% {
113      transform: translate3d(-6%, 3%, 0);
114    }
115    30% {
116      transform: translate3d(4%, -6%, 0);
117    }
118    40% {
119      transform: translate3d(-3%, 8%, 0);
120    }
121    50% {
122      transform: translate3d(-6%, 3%, 0);
123    }
124    60% {
125      transform: translate3d(8%, 0%, 0);
126    }
127    70% {
128      transform: translate3d(0%, 6%, 0);
129    }
130    80% {
131      transform: translate3d(-8%, 0%, 0);
132    }
133    90% {
134      transform: translate3d(6%, 3%, 0);
135    }
136  }
137  @media (prefers-reduced-motion: reduce) {
138    .grain {
139      animation: none;
140    }
141  }
142  :root {
143    --glitch-intensity: 1;
144  }
145
146  .is-glitching {
147    animation: vhs-shake 0.07s steps(2) infinite;
148  }
149  .is-glitching * {
150    text-shadow:
151      calc(3px * var(--glitch-intensity)) 0 0 rgba(255, 30, 80, 0.78),
152      calc(-3px * var(--glitch-intensity)) 0 0 rgba(0, 200, 255, 0.78);
153  }
154  @keyframes vhs-shake {
155    0% {
156      transform: translate(0, 0) skewX(0deg);
157    }
158    25% {
159      transform: translate(calc(-2px * var(--glitch-intensity)), calc(1px * var(--glitch-intensity))) skewX(calc(0.2deg * var(--glitch-intensity)));
160    }
161    50% {
162      transform: translate(calc(3px * var(--glitch-intensity)), calc(-1px * var(--glitch-intensity))) skewX(calc(-0.3deg * var(--glitch-intensity)));
163    }
164    75% {
165      transform: translate(calc(-1px * var(--glitch-intensity)), calc(2px * var(--glitch-intensity))) skewX(calc(0.2deg * var(--glitch-intensity)));
166    }
167    100% {
168      transform: translate(0, 0) skewX(0deg);
169    }
170  }
171  
172[button-hover="glitch"] {
173  position: relative;
174}
175
176[button-hover="glitch"].is-hover-glitch {
177  animation: btn-glitch-shake 0.32s steps(2) 1;
178}
179
180[button-hover="glitch"].is-hover-glitch > *,
181[button-hover="glitch"].is-hover-glitch {
182  animation-name: btn-glitch-shake, btn-glitch-chroma;
183  animation-duration: 0.32s, 0.32s;
184  animation-timing-function: steps(2), steps(4);
185  animation-iteration-count: 1, 1;
186}
187
188[button-hover="glitch"]::before,
189[button-hover="glitch"]::after {
190  content: "";
191  position: absolute;
192  inset: 0;
193  pointer-events: none;
194  opacity: 0;
195  z-index: 2;
196}
197
198/* Scanline overlay */
199[button-hover="glitch"]::before {
200  background-image: repeating-linear-gradient(
201    0deg,
202    rgba(0, 0, 0, 0.12) 0,
203    rgba(0, 0, 0, 0.12) 1px,
204    transparent 1px,
205    transparent 3px
206  );
207  mix-blend-mode: multiply;
208}
209
210/* Slicing band */
211[button-hover="glitch"]::after {
212  background: linear-gradient(rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.04));
213  mix-blend-mode: screen;
214}
215
216[button-hover="glitch"].is-hover-glitch::before {
217  animation: btn-glitch-scan-once 0.32s steps(4) 1;
218}
219
220[button-hover="glitch"].is-hover-glitch::after {
221  animation: btn-glitch-slice-once 0.32s steps(3) 1;
222}
223
224@keyframes btn-glitch-shake {
225  0%   { transform: translate(0, 0) skewX(0deg); }
226  20%  { transform: translate(-1px, 0) skewX(0.2deg); }
227  40%  { transform: translate(1px, -1px) skewX(-0.25deg); }
228  60%  { transform: translate(-1px, 1px) skewX(0.15deg); }
229  80%  { transform: translate(1px, 0) skewX(-0.1deg); }
230  100% { transform: translate(0, 0) skewX(0deg); }
231}
232
233@keyframes btn-glitch-chroma {
234  0% {
235    text-shadow: 0 0 0 transparent, 0 0 0 transparent;
236  }
237  25% {
238    text-shadow:
239      1px 0 0 rgba(255, 30, 80, 0.7),
240      -1px 0 0 rgba(0, 200, 255, 0.7);
241  }
242  60% {
243    text-shadow:
244      1.5px 0 0 rgba(255, 30, 80, 0.55),
245      -1.5px 0 0 rgba(0, 200, 255, 0.55);
246  }
247  100% {
248    text-shadow: 0 0 0 transparent, 0 0 0 transparent;
249  }
250}
251
252@keyframes btn-glitch-scan-once {
253  0%, 100% { opacity: 0; }
254  20%, 70% { opacity: 1; }
255}
256
257@keyframes btn-glitch-slice-once {
258  0%   { opacity: 0; clip-path: inset(0 0 100% 0); transform: translateX(0); }
259  30%  { opacity: 1; clip-path: inset(20% 0 60% 0); transform: translateX(-1px); }
260  60%  { opacity: 1; clip-path: inset(55% 0 25% 0); transform: translateX(1px); }
261  100% { opacity: 0; clip-path: inset(100% 0 0 0); transform: translateX(0); }
262}
263
264@media (prefers-reduced-motion: reduce) {
265  [button-hover="glitch"].is-hover-glitch,
266  [button-hover="glitch"].is-hover-glitch::before,
267  [button-hover="glitch"].is-hover-glitch::after {
268    animation: none;
269  }
270}
271</style></head><body><main data-barba="wrapper" class="page-wrapper"><section data-barba-namespace="case" data-barba="container" class="section"><div class="grain"></div><div class="nav_mobile-logo"><div id="logo-wrapper" class="nav-logo-wrapper"><a href="/" class="nav-logo-black w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68ed5b6ccfb021a99ca0f271_White%20AG%20Logo%202018.svg" alt="Aaron Grieve Logo" class="nav-logo_image"/></a></div><div class="nav_available-for-work-notice"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="svg-icon"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z" fill="currentColor"></path></svg><div>Available for new projects</div></div></div><header class="nav-wrapper"><div class="nav_logo-alert-wrapper"><div class="nav-logo-wrapper"><a href="/" class="nav-logo-black w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68ed5b6ccfb021a99ca0f271_White%20AG%20Logo%202018.svg" alt="Aaron Grieve Logo" class="nav-logo_image"/></a></div><div class="nav_available-for-work-notice"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="svg-icon"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z" fill="currentColor"></path></svg><div>Available for new projects</div></div></div><div class="navbar-wrapper"><nav id="navbar-primary" data-wf--navbar--variant="primary" class="navbar"><a id="home-button" button-hover="glitch" href="/" class="navbar_button w-button">Home</a><a id="projects-button" button-hover="glitch" href="/#projects" class="navbar_button w-button">Projects</a><a id="integrations-button" button-hover="glitch" href="/integrations" class="navbar_button w-button">Integrations</a><a id="get-in-touch-button" button-hover="glitch" href="/contact" class="navbar_button is-highlighted 
271w-button">Get in touch</a></nav></div></header><section data-wf--hero-section--variant="base" class="case-hero-section"><div class="margin-bottom-small"><div class="eyebrow">CASE STUDY · card payments for the cashless countryside</div></div><div class="margin-vertical margin-medium"><h1 class="case_headline-h1">Honesty Box</h1></div><div class="case_hero-bottom-content"><p class="text-size-large">Honesty boxes are a wonderful, slightly anachronistic thing — eggs on a country lane, firewood at a campsite, a fiver in a tin for someone&#x27;s day on a driveway.</p><div class="button-group"><a button-hover="glitch" data-wf--button--variant="base" href="https://go.honestybox.app/box/1002" target="_blank" class="button w-button">Bubble app <strong>⤴</strong></a><a button-hover="glitch" data-wf--button--variant="alternate" href="https://honestybox.app" target="_blank" class="button w-variant-5356b157-9217-1014-dba6-9b92db0bf693 w-button">Webflow marketing site <strong>⤴</strong></a></div></div></section><div class="spacer-medium"><div class="w-layout-blockcontainer container w-container"><div class="case_meta-row"><div class="meta-row_card"><div class="eyebrow text-color-white">ROLES</div><p>Founder &amp; Builder</p></div><div class="meta-row_card"><div class="eyebrow text-color-white">STACK</div><p>Webflow, Bubble, Stripe Connect, Sendgrid</p></div><div class="meta-row_card"><div class="eyebrow text-color-white">LIVE</div><p>March 2026</p></div><div class="meta-row_card"><div class="eyebrow text-color-white">STATUS</div><p>Active</p></div></div></div></div><section class="case_intro-section"><div class="w-layout-blockcontainer container w-container"><div class="case-hero_image-wrapper"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox.avif" loading="lazy" sizes="(max-width: 2098px) 100vw, 2098px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1058e40b96dae5fbf880c3_Macbook-Air-www.honestybox.avif 2098w" alt="Woman scanning QR code on honesty box stall holding eggs for card payments via mobile app." class="case-hero_image"/></div><div class="w-layout-grid case_grid"><div><div class="w-richtext"><h2><strong>THE Brief</strong></h2><p>‍</p><p>Honesty boxes are a wonderful, slightly anachronistic thing — eggs on a country lane, firewood at a campsite, a fiver in a tin for someone&#x27;s day on a driveway. They run on trust and on the assumption that people have coins. Both halves of that are quietly eroding. People don&#x27;t carry cash any more. Honest people walk away because they can&#x27;t pay; the less-honest take what they want and don&#x27;t.</p><p>‍</p><p>The market response has been card readers and POS terminals — kit that costs hundreds, needs power, signal, charging, and someone to mind it. Wrong shape for a farm gate. Wrong shape for an empty driveway. Wrong shape for a campsite firewood pile.</p><p>‍</p><p>HonestyBox is what sits in between: a QR code your customer scans with the camera they already have, that opens a payment page they don&#x27;t need an account for, that pays you directly into your own Stripe account with the platform fee taken out at the point of transaction. No reader. No subscription. No &quot;fleet of devices to manage.&quot; The hardware, if you want hardware, is a piece of slate.</p></div></div><div><div class="case_stat-wrapper"><div class="case_state-card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-medium"><path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm6.226 5.385c-.584 0-.937.164-.937.593 0 .468.607.674 1.36.93 1.228.415 2.844.963 2.851 2.993C11.5 11.868 9.924 13 7.63 13a7.662 7.662 0 0 1-3.009-.626V9.758c.926.506 2.095.88 3.01.88.617 0 1.058-.165 1.058-.671 0-.518-.658-.755-1.453-1.041C6.026 8.49 4.5 7.94 4.5 6.11 4.5 4.165 5.988 3 8.226 3a7.29 7.29 0 0 1 2.734.505v2.583c-.838-.45-1.896-.703-2.734-.703Z" fill="currentColor"></path></svg><div class="headline-h3-style">Stripe Connect</div><p class="text-size-small">sellers onboard their own Stripe accounts and funds settle directly to them, with the platform fee skimmed atomically per transaction</p></div><div class="case_state-card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-medium"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5v-2zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2h-11z" fill="currentColor"></path></svg></svg><div class="headline-h3-style">Six sector-specific funnels</div><p class="text-size-small">live on the marketing site: farm stalls, campsites and glamping, driveways, car parks, boot fairs and markets, Airbnb and holiday lets</p></div><div class="case_state-card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-medium"><path d="M11 5.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1z" fill="currentColor"></path><path d="M2 2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H2zm13 2v5H1V4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1zm-1 9H2a1 1 0 0 1-1-1v-1h14v1a1 1 0 0 1-1 1z" fill="currentColor"></path></svg><div class="headline-h3-style">Seamless purchasing</div><p class="text-size-small">Buyer-side experience requires <strong>no app, no signup, no account</strong> — open the camera, scan, tap to pay</p></div></div></div></div></div><div class="w-layout-blockcontainer container w-container"><div class="cases_card"><h2>What was built</h2><div class="w-layout-grid cases_grid"><div class="cases-card is-not-link">
271<div class="case-card_image-wrapper"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b9fcb5e0e09cbee4811_app%20honesty%20box%20desktop.avif" loading="lazy" sizes="(max-width: 1836px) 100vw, 1836px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b9fcb5e0e09cbee4811_app%20honesty%20box%20desktop-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b9fcb5e0e09cbee4811_app%20honesty%20box%20desktop-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b9fcb5e0e09cbee4811_app%20honesty%20box%20desktop.avif 1836w" alt="HonestyBox app screen showing simple pricing: 5% per transaction plus Stripe fees with payment features listed." class="cases-card_image"/></div><div class="cases-card_body"><div><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f772d5c3de976a6146ea3e_Full_Logo_White.svg" loading="lazy" alt="Webflow logo" class="card_logo invert-image"/></div><div class="cases-card_title">Marketing site</div><ul role="list" class="w-list-unstyled"><li>A hero focused on the &quot;no hardware, no fees&quot; positioning the audience actually wants to hear</li><li><strong>Six sector-specific landing pages</strong> — each with its own imagery, language and worked example (a driveway converter is not a campsite owner is not a boot-fair regular)</li><li>An <strong>
271interactive cost calculator</strong> that lets a prospective seller plug in their product price and see exactly what they&#x27;ll receive after platform and Stripe fees — engineered to make the pricing structure feel transparent rather than buried</li><li>Worked-example pricing breakdowns for four canonical use cases (£20 picnic basket, £5 day parking, £2.50 eggs, £8 boot-fair lamp)</li><li>Short explainer videos addressing the two questions every prospect actually has: &quot;do my customers need an app?&quot; and &quot;why no monthly fees?&quot;</li><li>Full legal stack — privacy, terms, contact, press — and an <code>llms.txt</code> for AI crawler guidance</li></ul></div></div><div class="cases-card is-not-link"><div class="case-card_image-wrapper"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b3982ae69f4999ca0e9_app%20honesty%20box%20mobile.avif" loading="lazy" sizes="(max-width: 1836px) 100vw, 1836px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b3982ae69f4999ca0e9_app%20honesty%20box%20mobile-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b3982ae69f4999ca0e9_app%20honesty%20box%20mobile-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b3982ae69f4999ca0e9_app%20honesty%20box%20mobile-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105b3982ae69f4999ca0e9_app%20honesty%20box%20mobile.avif 1836w" alt="Mobile screen showing honesty box app menu with snacks and prices, plus plus and minus quantity buttons." class="cases-card_image"/></div><div class="cases-card_body"><div><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f77d00e7ac17dd2a4deb20_Bubble_Logo_no_code.svg" loading="lazy" alt="Bubble logo" class="card_logo invert-image"/></div><div class="cases-card_title">Application</div><ul role="list" class="w-list-unstyled"><li>Stripe Connect Express onboarding flow — a seller can go from landing page to &quot;first box live&quot; in a couple of minutes</li><li>Box creation: name, description, fixed-price or pay-what-you-want, multiple items per box</li><li>QR code generation per box, downloadable for print or used as the artwork for a slate plaque</li><li>Live transaction dashboard with real-time payment events</li><li>Multi-box support — sellers can run as many boxes as they want under one account, useful for campsites and farms with several pay-points</li><li>Buyer payment page — no signup, supports Apple Pay, Google Pay and card, optimised for the &quot;phone in one hand, eggs in the other&quot; moment</li></ul></div></div><div class="cases-card is-not-link"><div class="case-card_image-wrapper"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105bff021e206f6ceb4596_WhatsApp%20Image%202026-04-27%20at%2012.32fds.11.avif" loading="lazy" sizes="100vw" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105bff021e206f6ceb4596_WhatsApp%20Image%202026-04-27%20at%2012.32fds.11-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a105bff021e206f6ceb4596_WhatsApp%20Image%202026-04-27%20at%2012.32fds.11.avif 922w" alt="Slate sign reading Hill House Farm with QR code, eggs, rosemary, wheat, and honey jar on wooden table." class="cases-card_image"/></div><div class="cases-card_body"><div class="cases-card_title">Physical product line</div><ul role="list" class="w-list-unstyled"><li><strong>Laser-etched Welsh slate plaques</strong> with the seller&#x27;s QR code engraved into the face — a £34.99 product that ties the digital service to a tangible, weatherproof object you can screw to a fence post</li><li>A real differentiator: nothing about a printed-and-laminated A4 QR code says &quot;this is a serious operation,&quot; but slate does</li></ul></div></div></div></div></div></section><section class="case_section-3"><div class="w-layout-blockcontainer container w-container"><div class="w-layout-grid case_section-3_grid"><div><div class="eyebrow text-color-white">Noteworthy</div><div class="margin-bottom margin-small"><div class="headline-h3-style">Engineering decisions worth calling out</div></div><div class="margin-bottom margin-small"><p><strong>Atomic application fees.</strong> Every transaction carries the platform fee as part of the <code>payment_intent</code> created via Stripe Connect. There&#x27;s no second-step reconciliation, no held balance, no platform sweep. If a refund happens, the platform fee is refunded too — the maths just works.</p></div><p><strong>Subdomain split with shared brand.</strong> <code>honestybox.app</code> (Webflow) and <code>go.honestybox.app</code> (Bubble) live as one product to the user but two technical systems to the team. Each one can be deployed to without risk to the other.</p></div><div class="case_image-wrapper-2"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder.jpg" loading="lazy" sizes="(max-width: 1836px) 100v
271w, 1836px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder-p-500.jpg 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder-p-800.jpg 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder-p-1080.jpg 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder-p-1600.jpg 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a10608d07261170c5e7391c_Galaxy-Nosdfte20-Ultra-app.yonder.jpg 1836w" alt="Smartphone displaying HonestyBox app for Millford Farm parking with hourly and all-day rates." class="case_image"/></div></div></div></section><div class="services-section-wrapper"><div class="w-layout-blockcontainer container w-container"><div id="hero-bottom" class="services-section"><div class="eyebrow">Conclusion</div><div class="cta_row"><h3 class="cta-title">What this project shows about my work</h3></div><div class="case_pre-closer"><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm6.226 5.385c-.584 0-.937.164-.937.593 0 .468.607.674 1.36.93 1.228.415 2.844.963 2.851 2.993C11.5 11.868 9.924 13 7.63 13a7.662 7.662 0 0 1-3.009-.626V9.758c.926.506 2.095.88 3.01.88.617 0 1.058-.165 1.058-.671 0-.518-.658-.755-1.453-1.041C6.026 8.49 4.5 7.94 4.5 6.11 4.5 4.165 5.988 3 8.226 3a7.29 7.29 0 0 1 2.734.505v2.583c-.838-.45-1.896-.703-2.734-.703Z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Stripe Connect at production depth</div><p>Express onboarding, application fees, payout management, the works</p></div></div><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path d="M7.25 6v1c-.14.301-.338.617-.588.95-.537.716-1.259 1.44-2.016 2.196l.708.708.015-.016c.652-.652 1.33-1.33 1.881-2.015V12h1.5V6h1.216a.25.25 0 0 0 .192-.41L8.192 3.23a.25.25 0 0 0-.384 0L5.842 5.59a.25.25 0 0 0 .192.41H7.25Z" fill="currentColor"></path><path d="M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 
2710-2.098L9.05.435Zm-1.4.7a.495.495 0 0 1 .7 0l6.516 6.515a.495.495 0 0 1 0 .7L8.35 14.866a.495.495 0 0 1-.7 0L1.134 8.35a.495.495 0 0 1 0-.7L7.65 1.134Z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Bubble + Webflow combined properly</div><p>Separate domains, separate jobs, one coherent product</p></div></div><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path d="M12 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h8zM4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4z" fill="currentColor"></path><path d="M4 2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-2zm0 4a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm3-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm3-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1zm0 3a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-4z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Product-led pricing thinking</div><p>Designing the pricing page and the calculator as conversion tools, not afterthoughts</p></div></div><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path d="M11 2a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3h6zM5 1a4 4 0 0 0-4 4v6a4 4 0 0 0 4 4h6a4 4 0 0 0 4-4V5a4 4 0 0 0-4-4H5z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Software + physical product integration</div><p>Using a handcrafted Welsh slate product line as a brand and trust anchor for a digital service</p></div></div><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path d="M7 2.5a.5.5 0 0 0-.939-.24l-6 11A.5.5 0 0 0 .5 14h6a.5.5 0 0 0 .5-.5v-11zm2.376-.484a.5.5 0 0 1 .563.245l6 11A.5.5 0 0 1 15.5 14h-6a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .376-.484zM10 4.46V13h4.658L10 4.46z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Vertical funnel architecture</div><p>Six sector pages from one product, each one a credible standalone landing experience</p></div></div><div class="card"><svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="icon-size-small"><path fill-rule="evenodd" d="M3.1 11.2a.5.5 0 0 1 .4-.2H6a.5.5 0 0 1 0 1H3.75L1.5 15h13l-2.25-3H10a.5.5 0 0 1 0-1h2.5a.5.5 0 0 1 .4.2l3 4a.5.5 0 0 1-.4.8H.5a.5.5 0 0 1-.4-.8l3-4z" fill="currentColor"></path><path fill-rule="evenodd" d="M4 4a4 4 0 1 1 4.5 3.969V13.5a.5.5 0 0 1-1 0V7.97A4 4 0 0 1 4 3.999z" fill="currentColor"></path></svg><div><div class="card_title margin-bottom-small">Distinctly local, broadly applicable</div><p>Built in Ceredigion, designed to work anywhere people have stopped carrying coins</p></div></div></div></div><div class="contact-form-wrapper"><code-island data-loader="{&quot;tag&quot;:&quot;FEDERATION&quot;,&quot;val&quot;:{&quot;clientModuleUrl&quot;:&quot;https://code-components.website-files.com/6a3afb5a286b4520fd52be5a%2Fmodule%2Fwf-manifest.json&quot;,&quot;moduleId&quot;:&quot;_6a3afb5a286b4520fd52be5a&quot;,&quot;submoduleId&quot;:&quot;e4aaaca9-5983-b60e-101d-65aaf8886742&quot;,&quot;exportPath&quot;:&quot;default&quot;,&quot;serverModuleUrl&quot;:&quot;_&quot;}}" data-props="{&quot;heading&quot;:&quot;Let&#x27;s work together&quot;,&quot;subheading&quot;:&quot;Tell us about your project and we&#x27;ll get back to you within 24 hours.&quot;}" data-slots="[]" data-hydrate="true" data-webflow-context="{&quot;mode&quot;:&quot;publish&quot;,&quot;interactive&quot;:true,&quot;locale&quot;:null}" data-interactive="true" style="display:contents"><template shadowrootmode="open"><link rel="stylesheet" type="text/css" href="https://code-components.website-files.com/6a3afb5a286b4520fd52be5a%2Fmodule%2F5e96701e62a20a06.css"/><div data-root="true" style="display:contents"><link rel="preload" as="image" href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a147f4d0ad11df49d1c4400_Full_Logo_Blue_White.png"/><link rel="preload" as="image" href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a147f4bc5d6c03e013e6521_Primary%20Logo%20%C3%94%C3%87%C3%B4%20Light.png"/><link rel="preload" as="image" href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1480cf05430ea2c1419524_lifebuoy-xxl.png"/><link rel="preload" as="image" href="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a14806f2a4ef7f37065c84b_loop-circular-xxl.png"/><div class="w-full max-w-[1080px] mx-auto px-4 py-10 max-[480px]:py-6 max-[480px]:px-2"><div class="w-full flex flex-col gap-3 mb-8"><div class="flex justify-between items-center"><span class="text-xs uppercase tracking-widest text-[var(--primary_white)] opacity-50 font-medium">Step <!-- -->1<!-- --> of <!-- -->3</span><span class="text-xs uppercase tracking-widest text-[var(--prime_brand)] font-medium">33<!-- -->%</span></div><div class="w-full h-1 rounded-full bg-[var(--primary_grey-10)] overflow-hidden"><div class="h-full rounded-full bg-[var(--prime_brand)] transition-all duration-500 ease-out" style="width:33.33333333333333%"></div></div></div><h2 class="text-3xl max-[768px]:text-2xl max-[480px]:text-xl font-bold uppercase tracking-tight text-[var(--primary_white)] mb-2" style="font-family:var(--headline);line-height:0.9">What do you need?</h2><p class="text-sm text-[var(--primary_white)] opacity-50 mb-8 max-[480px]:mb-6">Select all that apply.</p><div class="grid grid-cols-2 max-[480px]:grid-cols-1 gap-4"><button type="button" class="
272        relative flex flex-col items-center text-center gap-4 p-6 max-[480px]:p-4 roun
272ded-xl cursor-pointer
273        border transition-all duration-200
274        border-[var(--primary_grey)] bg-[var(--primary_grey-10)] hover:border-[var(--primary_white-10)]
275      "><div class="
276          absolute top-3 right-3 w-5 h-5 rounded-full flex items-center justify-center
277          transition-all duration-200
278          border border-[var(--primary_grey)] bg-transparent
279        "></div><div class="text-[var(--primary_white)] mt-2"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a147f4d0ad11df49d1c4400_Full_Logo_Blue_White.png" alt="Webflow" class="h-6 max-[480px]:h-5 w-auto"/></div><p class="text-sm leading-snug text-[var(--primary_white)] opacity-80 max-w-[40ch]">I want to chat about a Webflow project</p></button><button type="button" class="
280        relative flex flex-col items-center text-center gap-4 p-6 max-[480px]:p-4 rounded-xl cursor-pointer
281        border transition-all duration-200
282        border-[var(--primary_grey)] bg-[var(--primary_grey-10)] hover:border-[var(--primary_white-10)]
283      "><div class="
284          absolute top-3 right-3 w-5 h-5 rounded-full flex items-center justify-center
285          transition-all duration-200
286          border border-[var(--primary_grey)] bg-transparent
287        "></div><div class="text-[var(--primary_white)] mt-2"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a147f4bc5d6c03e013e6521_Primary%20Logo%20%C3%94%C3%87%C3%B4%20Light.png" alt="Bubble" class="h-6 max-[480px]:h-5 w-auto"/></div><p class="text-sm leading-snug text-[var(--primary_white)] opacity-80 max-w-[40ch]">I want to chat about a Bubble project</p></button><button type="button" class="
288        relative flex flex-col items-center text-center gap-4 p-6 max-[480px]:p-4 rounded-xl cursor-pointer
289        border transition-all duration-200
290        border-[var(--primary_grey)] bg-[var(--primary_grey-10)] hover:border-[var(--primary_white-10)]
291      "><div class="
292          absolute top-3 right-3 w-5 h-5 rounded-full flex items-center justify-center
293          transition-all duration-200
294          border border-[var(--primary_grey)] bg-transparent
295        "></div><div class="text-[var(--primary_white)] mt-2"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a1480cf05430ea2c1419524_lifebuoy-xxl.png" alt="Rescue" class="w-8 h-8 max-[480px]:w-6 max-[480px]:h-6"/></div><p class="text-sm leading-snug text-[var(--primary_white)] opacity-80 max-w-[40ch]">Rescue mission for a project that&#x27;s gone off track or lost its designer/developer</p></button><button type="button" class="
296        relative flex flex-col items-center text-center gap-4 p-6 max-[480px]:p-4 rounded-xl cursor-pointer
297        border transition-all duration-200
298        border-[var(--primary_grey)] bg-[var(--primary_grey-10)] hover:border-[var(--primary_white-10)]
299      "><div class="
300          absolute top-3 right-3 w-5 h-5 rounded-full flex items-center justify-center
301          transition-all duration-200
302          border border-[var(--primary_grey)] bg-transparent
303        "></div><div class="text-[var(--primary_white)] mt-2"><img src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a14806f2a4ef7f37065c84b_loop-circular-xxl.png" alt="Retainer" class="w-8 h-8 max-[480px]:w-6 max-[480px]:h-6"/></div><p class="text-sm leading-snug text-[var(--primary_white)] opacity-80 max-w-[40ch]">Discuss a monthly retainer or continued Webflow/Bubble support</p></button></div><div class="flex justify-between items-center mt-10 max-[480px]:mt-8"><div></div><button type="button" disabled="" class="
304              flex items-center gap-2 px-6 py-3 rounded-full text-sm font-semibold uppercase tracking-wider
305              transition-all duration-200 cursor-pointer
306              bg-[var(--primary_grey-10)] text-[var(--primary_white)] opacity-30 cursor-not-allowed
307            ">Continue<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></button></div></div></div></template></code-island></div></div></div><div id="projects" data-wf--cases-list--variant="cases-closing" data-wf-component-id="ef89cc75-1f08-b244-31ce-7c0de98ced33" data-wf-variant-state="c7431580-7a5b-2e9a-7a0e-680aec2eeaa2" class="cases-section w-variant-c7431580-7a5b-2e9a-7a0e-680aec2eeaa2"><div class="cases_card"><h2>SELECTED PROJECTS</h2><div class="w-layout-grid cases_grid"><a href="/yonder" class="cases-card w-inline-block"><div data-poster-url="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg" data-video-urls="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4,https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-autoplay="true" data-loop="true" data-wf-ignore="true" class="card_video-overlay w-background-video w-background-video-atom"><video id="4b97f793-a400-699f-d884-bfb01988bbde-video" autoplay="" loop="" style="background-image:url(&quot;https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg&quot;)" muted="" playsinline="" data-wf-ignore="true" data-object-fit="cover"><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4" data-wf-ignore="true"/><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-wf-ignore="true"/></video></div>
307<div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204.avif" alt="Yonder food app open on phone showing restaurant options including Waters Edge Café with burger and fries image." sizes="(max-width: 3000px) 100vw, 3000px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-1600.avif 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-2000.avif 2000w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204-p-2600.avif 2600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/69a17730664fe8d30bd302d0_Artboard%204.avif 3000w" class="cases-card_image"/></div><div class="cases-card_body"><div class="cases-card_title">Yonder<sup>®</sup> — food delivery for towns the big platforms skip</div></div></a><a href="#" class="cases-card is-not-link w-inline-block"><div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202.avif" alt="Modern cabin with large glass doors lit warmly, set in a grassy, tree-surrounded landscape at dusk." sizes="(max-width: 3000px) 100vw, 3000px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202-p-1600.avif 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202-p-2000.avif 2000w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f78c7207c5279e37a0d3aa_Artboard%202.avif 3000w" class="cases-card_image"/></div><div class="cases-card_body"><div class="case_coming-soon-tag w-variant-c7431580-7a5b-2e9a-7a0e-680aec2eeaa2">CASE STUDY COMING SOON</div><div class="cases-card_title">Your Retreats — a flagship Webflow build for a luxury lodge manufacturer</div></div></a><a href="/honesty-box" aria-current="page" class="cases-card w-inline-block w--current"><div data-poster-url="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg" data-video-urls="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4,https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-autoplay="true" data-loop="true" data-wf-ignore="true" class="card_video-overlay w-background-video w-background-video-atom"><video id="6f266f36-70ca-aca3-9f6a-a2fa88bf0b02-video" autoplay="" loop="" style="background-image:url(&quot;https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg&quot;)" muted="" playsinline="" data-wf-ignore="true" data-object-fit="cover"><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4" data-wf-ignore="true"/><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-wf-ignore="true"/></video></div><div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox.av
307if" alt="Woman in red beanie pays £3 parking fee with phone at honesty box in rural field parking." sizes="(max-width: 1897px) 100vw, 1897px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox-p-1600.avif 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0ef09223ff168e0e773d76_HonestyBox.avif 1897w" class="cases-card_image"/></div><div class="cases-card_body"><div class="cases-card_title">Honesty Box — card payments for the cashless countryside</div></div></a><a href="#" class="cases-card is-not-link w-inline-block"><div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f646bc78c6a663467e0aa0_607ff530f7dfc47b8030530a_Sidemen.avif" alt="Sidemen Website Design &amp; Development" sizes="(max-width: 900px) 100vw, 900px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f646bc78c6a663467e0aa0_607ff530f7dfc47b8030530a_Sidemen-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f646bc78c6a663467e0aa0_607ff530f7dfc47b8030530a_Sidemen-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f646bc78c6a663467e0aa0_607ff530f7dfc47b8030530a_Sidemen.avif 900w" class="cases-card_image"/></div><div class="cases-card_body"><div class="case_coming-soon-tag w-variant-c7431580-7a5b-2e9a-7a0e-680aec2eeaa2">CASE STUDY COMING SOON</div><div class="cases-card_title">Sidemen<sup>®</sup> — Webflow development for Europe&#x27;s largest YouTube collective</div></div></a><a href="/pestalozzi-digital" class="cases-card w-inline-block"><div data-poster-url="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg" data-video-urls="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4,https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-autoplay="true" data-loop="true" data-wf-ignore="true" class="card_video-overlay w-background-video w-background-video-atom"><video id="646f957d-1a86-3549-bb14-747247236057-video" autoplay="" loop="" style="background-image:url(&quot;https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg&quot;)" muted="" playsinline="" data-wf-ignore="true" data-object-fit="cover"><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_mp4.mp4" data-wf-ignore="true"/><source src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_webm.webm" data-wf-ignore="true"/></video></div><div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi.jpg" alt="Black text reading &#x27;Pestalozzi&#x27; on a white background." sizes="(max-width: 3000px) 100vw, 3000px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi-p-500.jpg 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi-p-800.jpg 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi-p-1080.jpg 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi-p-1600.jpg 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi-p-2000.jpg 2000w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5
307d1044f107c8077deb4_Pestalozzi-p-2600.jpg 2600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68f8a5d1044f107c8077deb4_Pestalozzi.jpg 3000w" class="cases-card_image"/></div><div class="cases-card_body"><div class="cases-card_title">Pestalozzi — a high-craft Webflow build for a leading ads consultant</div></div></a><a href="#" class="cases-card is-not-link w-inline-block"><div class="case-card_image-wrapper"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis.avif" alt="Dish of black pasta topped with shrimp in red sauce, lemon wedges, and chopped herbs." sizes="(max-width: 1897px) 100vw, 1897px" srcset="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis-p-500.avif 500w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis-p-800.avif 800w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis-p-1080.avif 1080w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis-p-1600.avif 1600w, https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/6a0edf7fb084f8e114417ea3_mannuccis.avif 1897w" class="cases-card_image"/></div><div class="cases-card_body"><div class="case_coming-soon-tag w-variant-c7431580-7a5b-2e9a-7a0e-680aec2eeaa2">CASE STUDY COMING SOON</div><div class="cases-card_title">Mannuccis — full design and build for Cardigan&#x27;s family Italian trattoria</div></div></a></div></div><div class="meta-row w-variant-c7431580-7a5b-2e9a-7a0e-680aec2eeaa2"><div class="meta-row_card"><div class="eyebrow">Freelance Since</div><div class="meta-row_stat">2014</div></div><div class="meta-row_card"><div class="eyebrow">Projects shipped</div><div class="meta-row_stat">40+</div></div><div class="meta-row_card"><div class="eyebrow">AVG. Timeline</div><div class="meta-row_stat">6-10 Weeks</div></div><div class="meta-row_card"><div class="eyebrow">Reply Within</div><div class="meta-row_stat">2 Working Days</div></div></div></div></section><footer id="hero-bottom" class="footer"><div class="footer_left-column"><div class="nav-logo-wrapper"><div class="nav-logo-black"><img loading="lazy" src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/68ed5b6ccfb021a99ca0f271_White%20AG%20Logo%202018.svg" alt="Aaron Grieve Logo" class="nav-logo_image"/></div></div><div class="footer_title">Built with Webflow in West Wales.</div><p class="no-max-width">Built with Webflow in West Wales.<br/>Aaron Grieve — complex Webflow &amp; Bubble platforms, built solo.</p></div><div><div class="nav_available-for-work-notice is-footer"><svg data-w-id="5ed91599-fc3a-d0b8-f1f8-3d0360c50375" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="svg-icon"><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M10.01 21.01c0 1.1.89 1.99 1.99 1.99s1.99-.89 1.99-1.99h-3.98zm8.87-4.19V11c0-3.25-2.25-5.97-5.29-6.69v-.72C13.59 2.71 12.88 2 12 2s-1.59.71-1.59 1.59v.72C7.37 5.03 5.12 7.75 5.12 11v5.82L3 18.94V20h18v-1.06l-2.12-2.12zM16 13.01h-3v3h-2v-3H8V11h3V8h2v3h3v2.01z" fill="currentColor"></path></svg><div>Available for new projects</div></div><a href="/" class="footer_link">Home</a><a href="/#projects" class="footer_link">Projects</a><a href="/integrations" class="footer_link">Integrations</a><a href="https://llmsgen.app" target="_blank" class="footer_link">LLM_GEN</a><a href="/contact" class="footer_link">Contact</a></div></footer></main>
307<script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=68ea3c0288fc606920c55310" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
307<script src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/js/webflow.schunk.d793fc09cb483c6e.js" type="text/javascript" integrity="sha384-zfWb7dBEt5HYogGcmgm7+2qg+FJqq8XvCaNF8+npFVqhRkjBbCjE4dOoZH1vbUsm" crossorigin="anonymous"></script>
307<script src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/js/webflow.schunk.3813f40e85c2e686.js" type="text/javascript" integrity="sha384-m+Ft7X8dnJOo3mc0hXZ+Ip4WVTkrdXDyLu2eXu5H7Odm2YERV1ycROPKMLFAb5yL" crossorigin="anonymous"></script>
307<script src="https://cdn.prod.website-files.com/68ea3c0288fc606920c55310/js/webflow.30a452e3.38499c3ded356fd4.js" type="text/javascript" integrity="sha384-nKji/dSit0BL7KKnU6r5t5SJcO7ScR+PZJFqUwjKT8pdG794vitZK+4q6lV4Al3/" crossorigin="anonymous"></script>
307<script src="https://cdn.prod.website-files.com/gsap/3.15.0/gsap.min.js" type="text/javascript"></script>
307<script src="https://cdn.prod.website-files.com/gsap/3.15.0/SplitText.min.js" type="text/javascript"></script>
307<script src="https://cdn.prod.website-files.com/gsap/3.15.0/ScrollTrigger.min.js" type="text/javascript"></script>
307<script src="https://cdn.prod.website-files.com/gsap/3.15.0/ScrollSmoother.min.js" type="text/javascript"></script>
307<script src="https://cdn.prod.website-files.com/gsap/3.15.0/EasePack.min.js" type="text/javascript"></script>
307<script type="text/javascript">gsap.registerPlugin(SplitText,ScrollTrigger,ScrollSmoother,EasePack);</script>
307<script src="https://cdn.jsdelivr.net/npm/@barba/[email protected]/dist/barba.umd.js"></script>
vendor: 1 bytes, line 307
307
308<script>
309class VHSOverlay {
310  constructor() {
311    this.grainCanvas = this._makeCanvas('z-index:9000;mix-blend-mode:multiply;');
312    this.fxCanvas = this._makeCanvas('z-index:9001;mix-blend-mode:normal;');
313    this.grainCtx = this.grainCanvas.getContext('2d');
314    this.fxCtx = this.fxCanvas.getContext('2d');
315    this.noiseFrames = [];
316    this.frameIndex = 0;
317    this.opacity = 0;
318    this.glitchActive = false;
319    this.bands = [];
320    this.intensity = 1.0;
321    this.running = false;
322    this.lastNoiseTime = 0;
323    this.noiseInterval = 1000 / 24;
324    this._resize();
325    this._generateNoiseFrames();
326    window.addEventListener('resize', () => { this._resize(); this._generateNoiseFrames(); });
327  }
328  _makeCanvas(extraStyle) {
329    const c = document.createElement('canvas');
330    c.style.cssText = 'position:fixed;inset:0;pointer-events:none;width:100%;height:100%;opacity:0;will-change:opacity;' + extraStyle;
331    c.setAttribute('aria-hidden', 'true');
332    document.body.appendChild(c);
333    return c;
334  }
335  _resize() {
336    const w = window.innerWidth, h = window.innerHeight;
337    this.grainCanvas.width = Math.max(1, Math.floor(w / 2));
338    this.grainCanvas.height = Math.max(1, Math.floor(h / 2));
339    this.fxCanvas.width = w;
340    this.fxCanvas.height = h;
341  }
342  _generateNoiseFrames() {
343    this.noiseFrames = [];
344    const { width, height } = this.grainCanvas;
345    for (let f = 0; f < 6; f++) {
346      const img = this.grainCtx.createImageData(width, height);
347      const d = img.data;
348      for (let i = 0; i < d.length; i += 4) {
349        const v = (Math.random() * 255) | 0;
350        d[i] = v; d[i+1] = v; d[i+2] = v; d[i+3] = 255;
351      }
352      this.noiseFrames.push(img);
353    }
354  }
355  setIntensity(v) { this.intensity = v; }
356  setOpacity(v) {
357    this.opacity = v;
358    this.grainCanvas.style.opacity = v;
359    this.fxCanvas.style.opacity = this.glitchActive ? v : 0;
360    this._ensureRunning(v);
361  }
362  setGlitchActive(active) {
363    this.glitchActive = active;
364    this.fxCanvas.style.opacity = active ? this.opacity : 0;
365    if (!active) this.bands = [];
366    this._ensureRunning(this.opacity);
367  }
368  _ensureRunning(opacity) {
369    const should = opacity > 0.01 || (this.glitchActive && opacity > 0.001);
370    if (should && !this.running) {
371      this.running = true;
372      this.lastNoiseTime = 0;
373      requestAnimationFrame(t => this._tick(t));
374    } else if (!should) {
375      this.running = false;
376    }
377  }
378  _tick(now) {
379    if (!this.running) return;
380    if (now - this.lastNoiseTime >= this.noiseInterval) {
381      this.frameIndex = (this.frameIndex + 1) % this.noiseFrames.length;
382      this.grainCtx.putImageData(this.noiseFrames[this.frameIndex], 0, 0);
383      this.lastNoiseTime = now;
384    }
385    if (this.glitchActive) this._renderEffects();
386    requestAnimationFrame(t => this._tick(t));
387  }
388  _renderEffects() {
389    const ctx = this.fxCtx;
390    const { width: w, height: h } = this.fxCanvas;
391    ctx.clearRect(0, 0, w, h);
392    if (Math.random() < 0.05 * this.intensity) {
393      this.bands.push({
394        y: Math.random() * h,
395        height: 8 + Math.random() * 70,
396        speed: 3 + Math.random() * 14,
397        brightness: 0.4 + Math.random() * 0.5,
398        life: 1.0,
399        decay: 0.025 + Math.random() * 0.04
400      });
401    }
402    this.bands = this.bands.filter(b => {
403      const grad = ctx.createLinearGradient(0, b.y, 0, b.y + b.height);
404      grad.addColorStop(0, 'rgba(255,255,255,0)');
405      grad.addColorStop(0.4, `rgba(255,255,255,${b.brightness * b.life * 0.6})`);
406      grad.addColorStop(0.55, `rgba(255,255,255,${b.brightness * b.life})`);
407      grad.addColorStop(1, 'rgba(255,255,255,0)');
408      ctx.fillStyle = grad;
409      ctx.fillRect(0, b.y, w, b.height);
410      ctx.fillStyle = `rgba(0,0,0,${0.4 * b.life})`;
411      ctx.fillRect(0, b.y + b.height, w, 1);
412      b.y += b.speed;
413      b.life -= b.decay;
414      return b.y < h + b.height && b.life > 0;
415    });
416    if (Math.random() < 0.018 * this.intensity) {
417      const y = Math.random() * h;
418      ctx.fillStyle = 'rgba(255,255,255,0.94)';
419      ctx.fillRect(0, y, w, 1 + Math.random() * 2);
420    }
421    ctx.fillStyle = 'rgba(0,0,0,0.1)';
422    for (let y = 0; y < h; y += 3) ctx.fillRect(0, y, w, 1);
423  }
424}
425
426const vhs = new VHSOverlay();
427const easeInOut = t => t * t * (3 - 2 * t);
428
429function tween(from, to, duration, onUpdate) {
430  return new Promise(resolve => {
431    const start = performance.now();
432    function frame(now) {
433      const t = Math.min(1, (now - start) / duration);
434      onUpdate(from + (to - from) * easeInOut(t));
435      if (t < 1) requestAnimationFrame(frame);
436      else resolve();
437    }
438    requestAnimationFrame(frame);
439  });
440}
441const wait = ms => new Promise(r => setTimeout(r, ms));
442
443let baseIntensity = 1.0; // 0.45 = subtle, 1.0 = standard, 1.9 = heavy
444function setIntensity(v) {
445  baseIntensity = v;
446  vhs.setIntensity(v);
447  document.documentElement.style.setProperty('--glitch-intensity', v);
448}
449setIntensity(1.0);
450
451let pendingHash = null;
452
453async function vhsLeave(container) {
454  container.classList.add('is-glitching');
455  vhs.setGlitchActive(true);
456  await tween(0, 1, 380, v => vhs.setOpacity(v));
457  document.documentElement.style.setProperty('--glitch-intensity', baseIntensity * 2.5);
458  await wait(120);
459  container.style.opacity = '0';
460}
461
462async function vhsEnter(container, hash) {
463  await new Promise(r => requestAnimationFrame(r));
464
465  let target = null;
466  if (hash) target = document.querySelector(hash);
467
468  if (target) {
469    target.scrollIntoView({ behavior: 'auto', block: 'start' });
470    history.replaceState(null, '', window.location.pathname + hash);
471  } else {
472    window.scrollTo(0, 0);
473  }
474
475  container.style.opacity = '1';
476  container.classList.add('is-glitching');
477  await wait(100);
478  document.documentElement.style.setProperty('--glitch-intensity', baseIntensity);
479  await tween(1, 0, 520, v => vhs.setOpacity(v));
480  container.classList.remove('is-glitching');
481  vhs.setGlitchActive(false);
482}
483
484// Recursive manual hydration of declarative shadow DOM (fallback for older browsers)
485function hydrateShadowRootsManual(root) {
486  if (!root) return;
487  const scope = root.shadowRoot || root;
488  if (!scope.querySelectorAll) return;
489
490  const templates = [...scope.querySelectorAll('template[shadowrootmode], template[shadowroot]')];
491
492  templates.forEach(template => {
493    const host = template.parentNode;
494    if (!host || host.shadowRoot) return;
495    const mode = template.getAttribute('shadowrootmode') || template.getAttribute('shadowroot') || 'open';
496    try {
497      const shadow = host.attachShadow({ mode });
498      shadow.appendChild(template.content);
499      template.remove();
500      hydrateShadowRootsManual(shadow);
501    } catch (e) {
502      console.warn('Shadow root attach failed:', e);
503    }
504  });
505
506  scope.querySelectorAll('*').forEach(el => {
507    if (el.shadowRoot) hydrateShadowRootsManual(el.shadowRoot);
508  });
509}
510
511// Swap the container's CONTENTS with freshly-parsed nodes (preserving shadow DOM),
512// without replacing the container element itself (which would break Barba's references).
513function preprocessNextContainer(data) {
514  const html = data.next.html;
515  let doc;
516
517  if (typeof Document.parseHTMLUnsafe === 'function') {
518    doc = Document.parseHTMLUnsafe(html);
519  } else {
520    doc = new DOMParser().parseFromString(html, 'text/html');
521  }
522
523  const freshContainer = doc.querySelector('[data-barba="container"]');
524  if (!freshContainer) return;
525
526  if (typeof Document.parseHTMLUnsafe !== 'function') {
527    hydrateShadowRootsManual(freshContainer);
528  }
529
530  const container = data.next.container;
531  container.innerHTML = '';
532  while (freshContainer.firstChild) {
533    container.appendChild(freshContainer.firstChild);
534  }
535
536  // Sync container attributes (except Barba's own)
537  [...freshContainer.attributes].forEach(attr => {
538    if (attr.name !== 'data-barba' && attr.name !== 'data-barba-namespace') {
539      container.setAttribute(attr.name, attr.value);
540    }
541  });
542}
543
544function resetWebflow(data) {
545  const dom = new DOMParser().parseFromString(data.next.html, 'text/html');
546
547  const newPage = dom.querySelector('html').getAttribute('data-wf-page');
548  document.documentElement.setAttribute('data-wf-page', newPage);
549  document.body.setAttribute('class', dom.querySelector('body').getAttribute('class'));
550
551  // 1. Load any <head> scripts that aren't already on the page
552  dom.querySelectorAll('head script').forEach(oldScript => {
553    const src = oldScript.getAttribute('src');
554    if (src) {
555      if (document.querySelector(`head script[src="${src}"]`)) return;
556      const s = document.createElement('script');
557      [...oldScript.attributes].forEach(a => s.setAttribute(a.name, a.value));
558      document.head.appendChild(s);
559    } else if (oldScript.textContent.trim()) {
560      const s = document.createElement('script');
561      [...oldScript.attributes].forEach(a => s.setAttribute(a.name, a.value));
562      s.textContent = oldScript.textContent;
563      document.head.appendChild(s);
564    }
565  });
566
567  // 2. Re-execute any inline scripts inside the new container
568  data.next.container.querySelectorAll('script').forEach(oldScript => {
569    const s = document.createElement('script');
570    [...oldScript.attributes].forEach(a => s.setAttribute(a.name, a.value));
571    s.textContent = oldScript.textContent;
572    oldScript.parentNode.replaceChild(s, oldScript);
573  });
574
575  // 3. Re-init Webflow IX2 + forms
576  if (window.Webflow) {
577    window.Webflow.destroy();
578    window.Webflow.ready();
579    window.Webflow.require('ix2').init();
580    document.dispatchEvent(new Event('readystatechange'));
581  }
582
583  // 4. Re-scan button-hover glitch elements
584  if (typeof window.scanGlitchButtons === 'function') {
585    window.scanGlitchButtons(data.next.container);
586  }
587}
588
589barba.hooks.before((data) => {
590  if (data.trigger && data.trigger.href) {
591    try {
592      const url = new URL(data.trigger.href, window.location.origin);
593      pendingHash = url.hash || null;
594    } catch (e) {
595      pendingHash = null;
596    }
597  } else {
598    pendingHash = null;
599  }
600});
601
602barba.hooks.beforeEnter((data) => preprocessNextContainer(data));
603
604barba.init({
605  transitions: [{
606    name: 'vhs',
607    async leave({ current }) { await vhsLeave(current.container); },
608    async enter({ next }) {
609      const hash = pendingHash || next.url.hash;
610      pendingHash = null;
611      await vhsEnter(next.container, hash);
612    }
613  }],
614  views: [{
615    namespace: 'home',
616    afterEnter() {}
617  }]
618});
619
620barba.hooks.afterEnter((data) => resetWebflow(data));
621</script>
621</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.