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 & Bubble</title><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow & Stripe Connect. No hardware, no subscriptionsâjust scan and pay." name="description"/><meta content="Honesty Box | Card Payments for Rural Sales Webflow & Bubble" property="og:title"/><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow & 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 & Bubble" name="twitter:title"/><meta content="QR-based card payment system for honesty boxes. Built with Bubble, Webflow & 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'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 & 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'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't carry cash any more. Honest people walk away because they can't pay; the less-honest take what they want and don'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'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 "fleet of devices to manage." 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 "no hardware, no fees" 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'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: "do my customers need an app?" and "why no monthly fees?"</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 "first box live" 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 "phone in one hand, eggs in the other" 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'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 "this is a serious operation," 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'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="{"tag":"FEDERATION","val":{"clientModuleUrl":"https://code-components.website-files.com/6a3afb5a286b4520fd52be5a%2Fmodule%2Fwf-manifest.json","moduleId":"_6a3afb5a286b4520fd52be5a","submoduleId":"e4aaaca9-5983-b60e-101d-65aaf8886742","exportPath":"default","serverModuleUrl":"_"}}" data-props="{"heading":"Let's work together","subheading":"Tell us about your project and we'll get back to you within 24 hours."}" data-slots="[]" data-hydrate="true" data-webflow-context="{"mode":"publish","interactive":true,"locale":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'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("https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg")" 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("https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg")" 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 & 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'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("https://cdn.prod.website-files.com/68ea3c0288fc606920c55310%2F6a18320347d8b03e7a1190e4_10438234-uhd_3840_2160_25fps_poster.0000000.jpg")" 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 'Pestalozzi' 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'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 & 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.