1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 7 <!-- Microsoft Clarity --> 8
8<script type="text/javascript"> 9 (function(c,l,a,r,i,t,y){ 10 c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; 11 t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; 12 y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); 13 })(window, document, "clarity", "script", "xb1o8ifxda"); 14 </script>
14 15 16 <title>Orchestrated Clarity In Chaos · Homemove · John Miller</title> 17 <meta name="description" content="How I redesigned Homemove's home-moving platform using OOUX â shifting from a service-first dashboard to a timeline-first task hierarchy." /> 18 <!-- Favicon â the href is set by the script below the theme bootstrap, so it 19 follows the RESOLVED theme (manual data-theme override > OS preference) 20 rather than prefers-color-scheme alone. --> 21 <link rel="icon" id="favicon" href="/favicon.png"> 22 <link rel="apple-touch-icon" href="/apple-touch-icon.png"> 23 <link rel="preload" href="/fonts/geist-latin.woff2" as="font" type="font/woff2" crossorigin> 24 25 <!-- LCP image: preload the responsive hero so it starts downloading during HTML 26 parse. Static pre-generated AVIF (scripts/generate-image-variants.mjs) â no 27 live /cdn-cgi/ transform, so no cold-start latency and no /cdn-cgi/ blocker 28 risk. type="image/avif" gates it to AVIF-capable browsers; the rest fall 29 through the <picture> (WebP â PNG). --> 30 <link 31 rel="preload" 32 as="image" 33 type="image/avif" 34 href="/homemove-mockup-1280.avif" 35 imagesrcset="/homemove-mockup-768.avif 768w, /homemove-mockup-1280.avif 1280w, /homemove-mockup-2880.avif 2880w" 36 imagesizes="(max-width: 767px) 85vw, 1280px" 37 fetchpriority="high"> 38 39 <!-- Theme bootstrap â see index.html for context. Inline + tiny on purpose 40 so the saved theme applies before stylesheets paint. --> 41
41<script> 42 (function () { 43 try { 44 var saved = localStorage.getItem('theme'); 45 var theme = (saved === 'light' || saved === 'dark') ? saved : 'auto'; 46 document.documentElement.setAttribute('data-theme', theme); 47 } catch (e) { 48 document.documentElement.setAttribute('data-theme', 'auto'); 49 } 50 })(); 51 </script>
51 52 53 <!-- Favicon swap. MUST stay below the theme bootstrap above: it reads the 54 resolved data-theme, so running it earlier (when the attribute is still 55 unset) silently falls back to prefers-color-scheme â the dark favicon on 56 a dark OS even when the user has manually switched the page to light. 57 Re-runs on OS-scheme change and the theme toggle (themechange). --> 58
58<script> 59 (function () { 60 var fav = document.getElementById('favicon'); 61 function set() { 62 var t = document.documentElement.getAttribute('data-theme'); 63 var dark = t === 'dark' || (t !== 'light' && matchMedia('(prefers-color-scheme: dark)').matches); 64 fav.href = dark ? '/favicon-dark.png' : '/favicon.png'; 65 } 66 set(); 67 matchMedia('(prefers-color-scheme: dark)').addEventListener('change', set); 68 window.addEventListener('themechange', set); 69 })(); 70 </script>
70 71 72 <!-- Mobile status-bar tint (the strip behind the clock/battery). Sets 73 <meta name="theme-color"> from the --color-bg token, so the OS status bar 74 always matches the page background â and on subpages meets the hero 75 shader's faded top edge with no seam. Sourced live from the token (single 76 source of truth, OKLCH â no duplicated colour to keep in sync); re-synced 77 on OS-scheme change and the theme toggle (themechange). --> 78
78<script> 79 (function () { 80 var meta = document.createElement('meta'); 81 meta.name = 'theme-color'; 82 document.head.appendChild(meta); 83 function paint() { 84 var bg = getComputedStyle(document.documentElement) 85 .getPropertyValue('--color-bg').trim(); 86 if (bg) meta.content = bg; 87 } 88 // tokens.css is render-blocking, so --color-bg resolves by the first frame. 89 requestAnimationFrame(paint); 90 matchMedia('(prefers-color-scheme: dark)').addEventListener('change', paint); 91 window.addEventListener('themechange', paint); 92 })(); 93 </script>
93 94 95 <!-- Warms up the DNS/TCP/TLS handshake to the Cal.com booking embed used by 96 the "Book a call" button in the Next-steps CTA card below; see book-call.js. --> 97 <link rel="preconnect" href="https://app.cal.eu"> 98 <link rel="dns-prefetch" href="https://app.cal.eu"> 99 100 <!-- Shader engine is self-hosted (shader-base.js â /vendor/paper-shaders-<ver>.min.js). 101 Preload the module AND its bundled engine so a fresh tab fetches both in 102 parallel during HTML parse â instead of discovering them late and stalling 103 the entrance animation while the engine downloads. --> 104 <link rel="modulepreload" href="/shader-base.js?v=1"> 105 <link rel="modulepreload" href="/vendor/paper-shaders-0.0.76.min.js"> 106 107 <style>:root{color-scheme:light dark;--color-bg: oklch(.97 .001 106.424);--color-bg-secondary: oklch(.985 .001 106.423);--color-bg-secondary-hover: oklch(1 0 0);--color-bg-secondary-pressed:oklch(.97 .001 106.424);--about-card-surface: var(--color-bg-secondary);--color-bg-inverted: oklch(0 0 0);--color-bg-primary: rgb(12, 10, 9);--color-bg-alpha: oklch(.147 .004 49.25 / .031);--color-bg-behind: oklch(.985 .001 106.423);--color-bg-browser: oklch(.985 .001 106.423 / .502);--color-text-primary: oklch(.147 .004 49.25);--color-text-secondary: oklch(.444 .01 73.639);--color-text-muted: oklch(.716 .009 56.259);--color-text-primary-inverted: oklch(.985 .001 106.423);--color-text-title: oklch(.147 .004 49.25);--color-text-disabled: oklch(.444 .01 73.639);--color-text-secondary-alpha: oklch(.444 .01 73.639);--color-text-secondary-inverted: oklch(.716 .009 56.259);--color-icon-primary: var(--color-text-secondary);--color-icon-secondary: var(--color-text-muted);--color-icon-strong: var(--color-text-primary);--color-icon-hover: var(--color-icon-strong);--color-fg-primary: oklch(.444 .01 73.639);--color-fg-secondary: oklch(.716 .009 56.259);--color-fg-primary-alpha: oklch(.147 .004 49.25 / .7);--color-fg-secondary-alpha: oklch(.147 .004 49.25 / .4);--color-fg-secondary-inverted: oklch(.444 .01 73.639);--color-fg-tertiary-alpha: oklch(.147 .004 49.25 / .102);--color-fg-muted-alpha: oklch(.147 .004 49.25 / .6);--color-border: oklch(.923 .003 48.717);--color-border-hover: oklch(.869 .004 56.366);--color-border-bg: oklch(.923 .003 48.717);--color-border-bg-hover: oklch(.923 .003 48.717);--color-border-inverted: oklch(.216 .006 56.043);--color-border-secondary-alpha: oklch(.147 .004 49.25 / .078);--color-focus-ring: #0C0A09;--color-selection-bg: var(--color-border-hover);--color-selection-text: var(--color-text-primary);--color-bd: oklch(.52 .095 194.8);--color-bd-bg: oklch(.52 .095 194.8 / .161);--color-bd-border: oklch(.52 .095 194.8 / .502);--color-hm: oklch(.665 .223 21.421);--color-hm-bg: oklch(.665 .223 21.421 / .161);--color-hm-border: oklch(.665 .223 21.421 / .502);--color-pikl: oklch(.58 .213 2.613);--color-pikl-bg: oklch(.58 .213 2.613 / .161);--color-pikl-border: oklch(.58 .213 2.613 / .502);--color-wren: oklch(.654 .176 151.021);--color-wren-bg: oklch(.654 .176 151.021 / .161);--color-wren-border: oklch(.654 .176 151.021 / .502);--color-wren-text: oklch(.513 .121 152.545);--color-pikl-ds: oklch(.62 .19 251.1);--color-pikl-ds-bg: oklch(.62 .19 251.1 / .161);--color-pikl-ds-border: oklch(.62 .19 251.1 / .502);--color-white-alpha-5: oklch(1 0 0 / .05);--color-white-alpha-10: oklch(1 0 0 / .1);--color-white-alpha-20: oklch(1 0 0 / .2);--color-white-alpha-30: oklch(1 0 0 / .3);--color-shadow: oklch(.147 .004 49.25 / .02);--color-highlight: oklch(1 0 0);--color-success-fg: oklch(.741 .222 144.737);--color-failure-fg: oklch(.655 .232 28.997);--color-warning-fg: oklch(.765 .175 62.574);--color-informational-fg: oklch(.603 .218 257.424);--color-special-fg: oklch(.603 .195 309.108);--color-success-bg-alpha: oklch(.744 .224 144.612 / .122);--color-failure-bg-alpha: oklch(.655 .232 29.178 / .122);--color-warning-bg-alpha: oklch(.765 .175 62.574 / .122);--color-informational-bg-alpha: oklch(.605 .217 257.214 / .122);--color-special-bg-alpha: oklch(.601 .196 309.226 / .122);--color-success-text: oklch(.512 .163 144.52);--color-failure-text: oklch(.553 .225 27.269);--color-warning-text: oklch(.552 .192 35.326);
107--color-informational-text: oklch(.463 .241 263.288);--color-special-text: oklch(.553 .161 324.469);--color-success-border: oklch(.741 .222 144.737);--color-failure-border: oklch(.655 .232 28.997);--color-warning-border: oklch(.87 .11 74.759);--color-informational-border: oklch(.783 .114 251.286);--color-special-border: oklch(.752 .121 310.907)}@font-face{font-family:Geist;font-style:normal;font-weight:400 600;font-display:fallback;src:url(/fonts/geist-latin.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--font-sans: "Geist", sans-serif;--font-weight-regular: 400;--font-weight-medium: 500;--font-weight-semibold: 600;--font-smoothing-webkit: antialiased;--font-smoothing-moz: grayscale;--type-h1: 40px;--type-h2: 24px;--type-h2nc: 18px;--type-h3: 18px;--type-body: 18px}@media(min-width:810px)and (max-width:1199px){:root{--type-h1: 28px;--type-h2: 18px;--type-h2nc: 18px;--type-h3: 16px;--type-body: 16px}}@media(max-width:809px){:root{--type-h1: 28px;--type-h2: 18px;--type-h2nc: 18px;--type-h3: 16px;--type-body: 16px}}:root{--space-1: 4px;--space-1-5: 6px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-8: 32px;--space-10: 40px;--space-12: 48px;--space-16: 64px;--space-20: 80px;--space-24: 96px;--space-32: 128px;--radius-btn: 20px;--card-padding: 4px;--radius: 40px;--radius-pill: 99px;--button-height: 48px;--button-padding-inline: 16px;--button-gap: var(--space-2);--shadow-ring: oklch(.147 .004 49.25 / .08);--shadow-ring-hover: oklch(.147 .004 49.25 / .18);--shadow-ring-pressed: var(--shadow-ring);--shadow-layer: oklch(.147 .004 49.25 / .06);--shadow-ring-inverted: rgba(250, 250, 249, .16);--shadow-ring-inverted-hover: rgba(250, 250, 249, .28);--shadow-with-ring-inverted: var(--shadow-ring-inverted) 0px 0px 0px 1px, var(--shadow-layer) 0px 1px 1px -.5px, var(--shadow-layer) 0px 3px 3px -1.5px, var(--shadow-layer) 0px 6px 6px -3px;--shadow-btn-hover-inverted: var(--shadow-ring-inverted-hover) 0px 0px 0px 1px, var(--shadow-layer) 0px 2px 2px -1px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px;--shadow-btn-pressed-inverted: var(--shadow-ring-inverted) 0px 0px 0px 1px, var(--shadow-layer) 0px 1px 1px -.5px;--shadow-card-outline: oklch(.147 .004 49.25 / .08);--shadow-card-outline-hover: oklch(.147 .004 49.25 / .18);--inner-frame-border: var(--shadow-ring);--inner-frame-light: var(--shadow-ring);--avatar-border: var(--shadow-ring);--shadow-with-ring: var(--shadow-ring) 0px 0px 0px 1px, var(--shadow-layer) 0px 1px 1px -.5px, var(--shadow-layer) 0px 3px 3px -1.5px, var(--shadow-layer) 0px 6px 6px -3px;--shadow-card: var(--shadow-ring) 0px 1px 0px 0px, var(--shadow-layer) 0px 1px 1px -.5px, var(--shadow-layer) 0px 3px 3px -1.5px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px;--shadow-mockup: var(--shadow-layer) 0px 1px 1px -.5px, var(--shadow-layer) 0px 3px 3px -1.5px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px;--shadow-inset-highlight: inset 0px 1px 2px 0px oklch(1 0 0);--shadow-btn-hover: var(--shadow-ring-hover) 0px 0px 0px 1px, var(--shadow-layer) 0px 2px 2px -1px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px;--shadow-btn-pressed: var(--shadow-ring-pressed) 0px 0px 0px 1px, var(--shadow-layer) 0px 1px 1px -.5px;--shadow-mockup-hover: var(--shadow-layer) 0px 2px 2px -1px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px, var(--shadow-layer) 0px 20px 20px -10px;--shadow-card-hover: var(--shadow-ring-hover) 0px 1px 0px 0px, var(--shadow-layer) 0px 2px 2px -1px, var(--shadow-layer) 0px 6px 6px -3px, var(--shadow-layer) 0px 12px 12px -6px, var(--shadow-layer) 0px 20px 20px -10px;--shadow-card-pressed: var(--shadow-ring-pressed) 0px 1px 0px 0px, var(--shadow-layer) 0px 1px 1px -.5px, var(--shadow-layer) 0px 2px 2px -1px;--shadow-card-opacity: 1}@media(max-width:809px){:root{--radius: 32px}}@supports not (corner-shape: superellipse(1.4)){:root{--radius-btn: 16px;--radius: 32px}@media(max-width:809px){:root{--radius: 24px}}}:root{--page-outer-padding: var(--space-8);--content-indent: var(--page-outer-padding);--card-header-padding: var(--space-8)}@media(min-width:810px)and (max-width:1199px){:root{--page-outer-padding: var(--space-6);--card-header-padding: var(--space-6)}}@media(max-width:809px){:root{--page-outer-padding: var(--space-5);--card-header-padding: var(--space-5)}}:root{--anim-enter-duration: .8s;--anim-enter-easing: cubic-bezier(.23, 1, .32, 1);--anim-enter-rest-opacity: .001;--anim-interactive-duration: .15s;--anim-interactive-easing: cubic-bezier(.2, 0, 0, 1)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]){--color-bg: rgb(0, 0, 0);--color-bg-secondary: rgb(12, 10, 9);--color-bg-secondary-hover: rgb(41, 37, 36);--color-bg-secondary-pressed:rgb(12, 10, 9);--about-card-surface: var(--color-bg-secondary);--color-bg-inverted: rgb(245, 245, 244);--color-bg-primary: oklch(.985 .001 106.423);--color-bg-alpha: rgba(255, 255, 255, .1);--color-bg-behind: rgb(0, 0, 0);--color-bg-browser: rgba(28, 25, 23, .5);--color-text-primary: rgb(250, 250, 249);--color-text-secondary: rgb(168, 162, 158);--color-text-muted: rgb(87, 83, 78);--color-text-primary-inverted: rgb(12, 10, 9);--color-text-title: rgb(0, 0, 0);--color-text-disabled: rgb(87, 83, 78);--color-text-secondary-alpha: rgba(250, 250, 249, .6);--color-text-secondary-inverted: rgb(87, 83, 78);--color-fg-primary: rgb(168, 162, 158);--color-fg-secondary: rgb(87, 83, 78);--color-fg-primary-alpha: rgba(250, 250, 249, .9);--color-fg-secondary-alpha: rgba(250, 250, 249, .4);--color-fg-secondary-inverted: rgb(168, 162, 158);--color-fg-tertiary-alpha: rgba(250, 250, 249, .2);--color-fg-muted-alpha: rgba(250, 250, 249, .6);--color-border: rgb(28, 25, 23);--color-border-hover: rgb(68, 64, 60);--color-border-bg: rgb(41, 37, 36);--color-border-bg-hover: rgb(68, 64, 60);--color-border-inverted: rgb(231, 229, 228);--color-border-secondary-alpha: rgba(250, 250, 249, .08);--color-focus-ring: rgb(250, 250, 249);--color-bd: rgb(22, 188, 187);--color-bd-bg: rgba(22, 188, 187, .2);--color-bd-border: rgba(22, 188, 187, .75);--color-hm: rgb(255, 67, 83);--color-hm-bg: rgba(255, 67, 83, .2);--color-hm-border: rgba(255, 67, 83, .75);--color-pikl: rgb(214, 38, 111);--color-pikl-bg: rgba(214, 38, 111, .2);--color-pikl-border: rgba(212, 37, 110, .75);--color-wren: rgb(0, 173, 84);--color-wren-bg: rgba(37, 177, 98, .2);--color-wren-border: rgba(37, 177, 98, .75);--color-wren-text: rgb(28, 122, 67);--color-pikl-ds: rgb(10, 132, 255);--color-pikl-ds-bg: rgba(10, 132, 255, .2);--color-pikl-ds-border: rgba(10, 132, 255, .75);--color-shadow: rgba(12, 10, 9, .1);--color-highlight: rgba(255, 255, 255, .12);--shadow-ring: rgba(250, 250, 249, .16);--shadow-ring-hover: rgba(250, 250, 249, .28);--shadow-ring-inverted: oklch(.147 .004 49.25 / .08);--shadow-ring-inverted-hover: oklch(.147 .004 49.25 / .18);--shadow-card-outline: oklch(1 0 0 / .16);--shadow-card-outline-hover: oklch(1 0 0 / .32);--shadow-inset-highlight: inset 0px 1px 2px 0px rgba(255, 255, 255, .12);--color-success-fg: rgb(50, 215, 75);--color-failure-fg: rgb(255, 69, 58);
107--color-warning-fg: rgb(255, 159, 11);--color-informational-fg: rgb(11, 132, 255);--color-special-fg: rgb(178, 96, 234);--color-success-bg-alpha: rgba(51, 215, 75, .16);--color-failure-bg-alpha: rgba(255, 66, 56, .2);--color-warning-bg-alpha: rgba(255, 157, 10, .2);--color-informational-bg-alpha: rgba(10, 133, 255, .2);--color-special-bg-alpha: rgba(179, 97, 234, .2);--color-success-text: rgb(50, 222, 75);--color-failure-text: rgb(255, 104, 97);--color-warning-text: rgb(255, 179, 63);--color-informational-text: rgb(65, 156, 255);--color-special-text: rgb(207, 146, 248);--color-success-border: rgb(29, 175, 51);--color-failure-border: rgb(204, 10, 0);--color-warning-border: rgb(255, 159, 11);--color-informational-border: rgb(11, 132, 255);--color-special-border: rgb(178, 96, 234)}}:root[data-theme=dark]{color-scheme:dark;--color-bg: rgb(0, 0, 0);--color-bg-secondary: rgb(12, 10, 9);--color-bg-secondary-hover: rgb(41, 37, 36);--color-bg-secondary-pressed:rgb(12, 10, 9);--about-card-surface: var(--color-bg-secondary);--color-bg-inverted: rgb(245, 245, 244);--color-bg-primary: oklch(.985 .001 106.423);--color-bg-alpha: rgba(255, 255, 255, .1);--color-bg-behind: rgb(0, 0, 0);--color-bg-browser: rgba(28, 25, 23, .5);--color-text-primary: rgb(250, 250, 249);--color-text-secondary: rgb(168, 162, 158);--color-text-muted: rgb(87, 83, 78);--color-text-primary-inverted: rgb(12, 10, 9);--color-text-title: rgb(0, 0, 0);--color-text-disabled: rgb(87, 83, 78);--color-text-secondary-alpha: rgba(250, 250, 249, .6);--color-text-secondary-inverted: rgb(87, 83, 78);--color-fg-primary: rgb(168, 162, 158);--color-fg-secondary: rgb(87, 83, 78);--color-fg-primary-alpha: rgba(250, 250, 249, .9);--color-fg-secondary-alpha: rgba(250, 250, 249, .4);--color-fg-secondary-inverted: rgb(168, 162, 158);--color-fg-tertiary-alpha: rgba(250, 250, 249, .2);--color-fg-muted-alpha: rgba(250, 250, 249, .6);--color-border: rgb(28, 25, 23);--color-border-hover: rgb(68, 64, 60);--color-border-alpha: rgba(250, 250, 249, .12);--color-border-bg: rgb(41, 37, 36);--color-border-bg-hover: rgb(68, 64, 60);--color-border-alpha-hover: rgba(250, 250, 249, .24);--color-border-inverted: rgb(231, 229, 228);--color-border-secondary-alpha: rgba(250, 250, 249, .08);--color-focus-ring: rgb(250, 250, 249);--color-bd: rgb(22, 188, 187);--color-bd-bg: rgba(22, 188, 187, .2);--color-bd-border: rgba(22, 188, 187, .75);--color-hm: rgb(255, 67, 83);--color-hm-bg: rgba(255, 67, 83, .2);--color-hm-border: rgba(255, 67, 83, .75);--color-pikl: rgb(214, 38, 111);--color-pikl-bg: rgba(214, 38, 111, .2);--color-pikl-border: rgba(212, 37, 110, .75);--color-wren: rgb(0, 173, 84);--color-wren-bg: rgba(37, 177, 98, .2);--color-wren-border: rgba(37, 177, 98, .75);--color-wren-text: rgb(28, 122, 67);--color-pikl-ds: rgb(10, 132, 255);--color-pikl-ds-bg: rgba(10, 132, 255, .2);--color-pikl-ds-border: rgba(10, 132, 255, .75);--color-shadow: rgba(12, 10, 9, .1);--color-highlight: rgba(255, 255, 255, .12);--shadow-ring: rgba(250, 250, 249, .16);--shadow-ring-hover: rgba(250, 250, 249, .28);--shadow-ring-inverted: oklch(.147 .004 49.25 / .08);--shadow-ring-inverted-hover: oklch(.147 .004 49.25 / .18);--shadow-card-outline: oklch(1 0 0 / .16);--shadow-card-outline-hover: oklch(1 0 0 / .32);--shadow-inset-highlight: inset 0px 1px 2px 0px rgba(255, 255, 255, .12);--color-success-fg: rgb(50, 215, 75);--color-failure-fg: rgb(255, 69, 58);--color-warning-fg: rgb(255, 159, 11);--color-informational-fg: rgb(11, 132, 255);--color-special-fg: rgb(178, 96, 234);--color-success-bg-alpha: rgba(51, 215, 75, .16);--color-failure-bg-alpha: rgba(255, 66, 56, .2);--color-warning-bg-alpha: rgba(255, 157, 10, .2);--color-informational-bg-alpha: rgba(10, 133, 255, .2);--color-special-bg-alpha: rgba(179, 97, 234, .2);--color-success-text: rgb(50, 222, 75);--color-failure-text: rgb(255, 104, 97);--color-warning-text: rgb(255, 179, 63);--color-informational-text: rgb(65, 156, 255);--color-special-text: rgb(207, 146, 248);--color-success-border: rgb(29, 175, 51);--color-failure-border: rgb(204, 10, 0);
107--color-warning-border: rgb(255, 159, 11);--color-informational-border: rgb(11, 132, 255);--color-special-border: rgb(178, 96, 234)}:root[data-theme=light]{color-scheme:light}[data-project=bella-duke]{--color-selection-bg: var(--color-bd-bg)}[data-project=homemove]{--color-selection-bg: var(--color-hm-bg)}[data-project=pikl]{--color-selection-bg: var(--color-pikl-bg)}[data-project=wren]{--color-selection-bg: var(--color-wren-bg)}[data-project=pikl-ds]{--color-selection-bg: var(--color-pikl-ds-bg)}</style> 108 <link rel="stylesheet" href="../vendor/photoswipe/photoswipe.css?v=1"> 109 <link rel="stylesheet" href="../components.css?v=284"> 110 <link rel="stylesheet" href="../subpage.css?v=20" /> 111
111<script src="../vendor/motion.min.js?v=1" defer></script>
111 112
112<script src="../animations.js?v=skel4" defer></script>
112 113
113<script src="../labelled-section.js?v=1" defer></script>
113 114
114<script src="../contact-button-group.js?v=1" defer></script>
114 115
115<script src="../next-project-card.js?v=1" defer></script>
115 116
116<script src="../card-blur.js?v=22" defer></script>
116 117
117<script src="../next-steps-cta.js?v=1" defer></script>
117 118
118<script> 119 (function(){function l(){var s=document.createElement("script");s.src="../book-call.js?v=35";document.head.appendChild(s)}if("requestIdleCallback"in window){requestIdleCallback(l,{timeout:3000})}else{window.addEventListener("load",l)}})(); 120 </script>
120 121 122 <!-- ThumbHash placeholders --> 123
123<script type="module" src="../thumbhash-loader.js?v=skel10"></script>
123 124 125 <!-- iOS-style 60% corner smoothing (squircles) on safe rounded elements --> 126 127 <!-- PhotoSwipe lightbox (single-image mode); stylesheet is loaded earlier 128 in <head> so components.css can win cascade conflicts against 129 .pswp__button defaults when the close button is styled as a .btn. --> 130
130<script type="module" src="../photoswipe-init.js?v=1"></script>
130 131 132 <!-- Phone-overlay swipe forwarding: lets a swipe that starts on the 133 phone scroll the laptop underneath while keeping the phone tappable 134 for the lightbox. Auto-binds to any .case-mockup-wrap that has both a 135 .case-mockup-scroll and a .mockup--mobile, so future projects with 136 this layout get it for free. --> 137
137<script type="module" src="../mockup-swipe-forward.js?v=1"></script>
137 138 139 <!-- Local dev only: rewrite Cloudflare image URLs to originals (no-op in prod) --> 140 141 <!-- Speculation Rules â prerender same-origin page navigations on link 142 hover/touch (eagerness: moderate), so project pages (and back-to-home) 143 open near-instantly. Progressive enhancement: unsupported browsers 144 ignore the block and links navigate normally. Safe with the entrance 145 cascade â runEntranceCascade() holds the reveal until the page is first 146 visible, so a prerendered page plays its entrance on activation, not in 147 the background. Cloudflare's injected Speed Brain only *prefetches* 148 conservatively; this upgrades the six site pages to full prerender. --> 149
149<script type="speculationrules"> 150 { 151 "prerender": [{ 152 "where": { "href_matches": ["/", "/bella-duke{/}?", "/homemove{/}?", "/pikl-dashboard{/}?", "/pikl-design-system{/}?", "/wren-kitchens{/}?"] }, 153 "eagerness": "moderate" 154 }] 155 } 156 </script>
156 157 158</head> 159<body data-project="homemove"> 160 161<div class="case-page"> 162 163 <!-- Back button --> 164 <a class="btn home-btn" href="../" aria-label="Back to all work" data-stagger-item> 165 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 166 <path d="M5.5 12.002H19"/> 167 <path d="M10.9999 18.002C10.9999 18.002 4.99998 13.583 4.99997 12.0019C4.99996 10.4208 11 6.00195 11 6.00195"/> 168 </svg> 169 <span class="btn__label">All work</span> 170 </a> 171 172 <!-- Hero area --> 173 <div class="case-hero-area"> 174 175 <!-- Shader background --> 176 <div class="case-shader" aria-hidden="true"> 177 <div id="case-shader-gradient" style="width: 100%; height: 100%;"></div> 178 </div> 179 180 <!-- ===== HERO ===== --> 181 <header class="case-hero" data-stagger> 182 183 <!-- Page title --> 184 <h1 class="case-title">Orchestrated Clarity In Chaos</h1> 185 186 <!-- Company badge row --> 187 <div class="case-company"> 188 <!-- Homemove wordmark SVG --> 189 <div class="case-logo-wrap"> 190 <svg class="case-logo" xmlns="http://www.w3.org/2000/svg" width="116" height="18" fill="none" viewBox="0 0 116 18" aria-label="Homemove"> 191 <path d="M 32.118 8.38 C 31.609 8.095 31.026 7.953 30.37 7.953 C 29.713 7.953 29.179 8.093 28.703 8.372 C 28.408 8.545 28.164 8.767 27.973 9.039 L 27.973 4.652 L 25.822 4.652 L 25.822 17.084 L 27.973 17.084 L 27.973 11.845 C 27.973 11.451 28.049 11.112 28.203 10.827 C 28.356 10.542 28.569 10.321 28.843 10.162 C 29.116 10.003 29.428 9.924 29.778 9.924 C 30.129 9.924 30.454 10.003 30.723 10.162 C 30.991 10.32 31.201 10.542 31.355 10.827 C 31.508 11.112 31.585 11.451 31.585 11.845 L 31.585 17.084 L 33.735 17.084 L 33.735 11.336 C 33.735 10.668 33.593 10.08 33.308 9.57 C 33.024 9.061 32.627 8.665 32.118 8.38 Z M 42.358 8.744 C 41.647 8.339 40.853 8.137 39.978 8.137 C 39.102 8.137 38.292 8.34 37.581 8.744 C 36.869 9.149 36.303 9.703 35.882 10.403 C 35.46 11.104 35.249 11.903 35.249 12.8 C 35.249 13.698 35.46 14.498 35.882 15.198 C 36.303 15.899 36.872 16.452 37.589 16.857 C 38.306 17.262 39.102 17.464 39.978 17.464 C 40.854 17.464 41.631 17.262 42.342 16.857 C 43.053 16.451 43.623 15.899 44.05 15.198 C 44.477 14.498 44.69 13.698 44.69 12.8 C 44.69 11.902 44.479 11.09 44.058 10.395 C 43.636 9.7 43.07 9.149 42.359 8.744 Z M 42.214 14.298 C 42.001 14.708 41.711 15.028 41.344 15.258 C 40.977 15.488 40.553 15.603 40.072 15.603 C 39.59 15.603 39.146 15.488 38.775 15.258 C 38.402 15.028 38.11 14.708 37.896 14.298 C 37.683 13.887 37.576 13.425 37.576 12.91 C 37.576 12.395 37.683 11.922 37.896 11.522 C 38.11 11.122 38.402 10.805 38.775 10.57 C 39.147 10.334 39.579 10.216 40.072 10.216 C 40.564 10.216 40.977 10.334 41.344 10.57 C 41.711 10.805 42.001 11.122 42.214 11.522 C 42.427 11.922 42.534 12.384 42.534 12.91 C 42.534 13.435 42.427 13.887 42.214 14.298 Z M 58.186 8.563 C 57.687 8.279 57.121 8.136 56.487 8.136 C 55.775 8.136 55.146 8.314 54.599 8.67 C 54.246 8.899 53.951 9.203 53.712 9.579 C 53.517 9.242 53.243 8.953 52.891 8.711 C 52.333 8.328 51.687 8.136 50.954 8.136 C 50.287 8.136 49.707 8.295 49.214 8.613 C 48.892 8.82 48.64 9.099 48.459 9.447 L 48.459 8.333 L 46.439 8.333 L 46.439 17.267 L 48.59 17.267 L 48.59 12.028 C 48.59 11.634 48.661 11.295 48.803 11.01 C 48.946 10.726 49.148 10.504 49.411 10.345 C 49.674 10.187 49.975 10.107 50.314 10.107 C 50.653 10.107 50.968 10.187 51.225 10.345 C 51.482 10.504 51.682 10.726 51.824 11.01 C 51.967 11.295 52.038 11.634 52.038 12.028 L 52.038 17.267 L 54.189 17.267 L 54.189 12.028 C 54.189 11.634 54.259 11.295 54.402 11.01 C 54.544 10.726 54.747 10.504 55.009 10.345 C 55.272 10.187 55.573 10.107 55.912 10.107 C 56.274 10.107 56.58 10.187 56.832 10.345 C 57.083 10.504 57.28 10.726 57.423 11.01 C 57.565 11.295 57.636 11.634 57.636 12.028 L 57.636 17.267 L 59.787 17.267 L 59.787 11.519 C 59.787 10.852 59.645 10.263 59.36 9.754 C 59.075 9.245 58.684 8.848 58.186 8.563 Z M 69.164 9.434 C 68.791 9.029 68.34 8.711 67.809 8.481 C 67.278 8.251 66.663 8.136 65.962 8.136 C 65.13 8.136 64.378 8.339 63.705 8.744 C 63.032 9.149 62.501 9.699 62.112 10.394 C 61.724 11.09 61.529 11.886 61.529 12.784 C 61.529 13.681 61.721 14.437 62.104 15.149 C 62.487 15.86 63.023 16.424 63.713 16.84 C 64.402 17.256 65.207 17.464 66.126 17.464 C 66.739 17.464 67.3 17.371 67.809 17.185 C 68.318 16.999 68.758 16.741 69.13 16.413 C 69.503 16.085 69.776 15.707 69.952 15.28 L 68.211 14.426 C 68.014 14.777 67.743 15.056 67.399 15.264 C 67.054 15.472 66.635 15.576 66.143 15.576 C 65.65 15.576 65.215 15.461 64.837 15.231 C 64.46 15.001 64.175 14.675 63.984 14.254 C 63.866 13.996 63.793 13.714 63.764 13.408 L 70.198 13.408 C 70.241 13.288 70.272 13.145 70.288 12.981 C 70.304 12.817 70.313
19112.659 70.313 12.505 C 70.313 11.903 70.214 11.342 70.017 10.822 C 69.82 10.302 69.535 9.839 69.164 9.434 Z M 64.86 10.345 C 65.21 10.121 65.609 10.009 66.058 10.009 C 66.507 10.009 66.931 10.124 67.265 10.354 C 67.599 10.584 67.84 10.896 67.987 11.29 C 68.055 11.47 68.096 11.661 68.113 11.864 L 63.906 11.864 C 63.943 11.673 63.995 11.495 64.064 11.331 C 64.244 10.898 64.51 10.57 64.86 10.345 Z M 83.781 8.563 C 83.283 8.279 82.717 8.136 82.082 8.136 C 81.371 8.136 80.741 8.314 80.194 8.67 C 79.842 8.899 79.546 9.203 79.308 9.579 C 79.112 9.242 78.839 8.953 78.487 8.711 C 77.929 8.328 77.283 8.136 76.55 8.136 C 75.882 8.136 75.302 8.295 74.81 8.613 C 74.487 8.82 74.236 9.099 74.054 9.447 L 74.054 8.333 L 72.035 8.333 L 72.035 17.267 L 74.186 17.267 L 74.186 12.028 C 74.186 11.634 74.257 11.295 74.399 11.01 C 74.541 10.726 74.744 10.504 75.007 10.345 C 75.269 10.187 75.57 10.107 75.91 10.107 C 76.249 10.107 76.564 10.187 76.821 10.345 C 77.078 10.504 77.278 10.726 77.42 11.01 C 77.562 11.295 77.633 11.634 77.633 12.028 L 77.633 17.267 L 79.784 17.267 L 79.784 12.028 C 79.784 11.634 79.855 11.295 79.998 11.01 C 80.14 10.726 80.342 10.504 80.605 10.345 C 80.868 10.187 81.169 10.107 81.508 10.107 C 81.869 10.107 82.176 10.187 82.427 10.345 C 82.679 10.504 82.876 10.726 83.019 11.01 C 83.161 11.295 83.232 11.634 83.232 12.028 L 83.232 17.267 L 85.383 17.267 L 85.383 11.519 C 85.383 10.852 85.24 10.263 84.956 9.754 C 84.671 9.245 84.28 8.848 83.782 8.563 Z M 94.234 8.744 C 93.523 8.339 92.729 8.137 91.854 8.137 C 90.978 8.137 90.168 8.34 89.457 8.744 C 88.745 9.149 88.179 9.703 87.758 10.403 C 87.336 11.104 87.125 11.903 87.125 12.8 C 87.125 13.698 87.336 14.498 87.758 15.198 C 88.179 15.899 88.748 16.452 89.465 16.857 C 90.182 17.262 90.978 17.464 91.854 17.464 C 92.73 17.464 93.507 17.262 94.218 16.857 C 94.929 16.451 95.499 15.899 95.926 15.198 C 96.353 14.498 96.566 13.698 96.566 12.8 C 96.566 11.902 96.355 11.09 95.934 10.395 C 95.512 9.7 94.946 9.149 94.235 8.744 Z M 94.09 14.298 C 93.877 14.708 93.586 15.028 93.22 15.258 C 92.853 15.488 92.429 15.603 91.947 15.603 C 91.466 15.603 91.022 15.488 90.651 15.258 C 90.278 15.028 89.986 14.708 89.772 14.298 C 89.559 13.887 89.452 13.425 89.452 12.91 C 89.452 12.395 89.559 11.922 89.772 11.522 C 89.986 11.122 90.278 10.805 90.651 10.57 C 91.023 10.334 91.455 10.216 91.947 10.216 C 92.44 10.216 92.853 10.334 93.22 10.57 C 93.586 10.805 93.876 11.122 94.09 11.522 C 94.303 11.922 94.41 12.384 94.41 12.91 C 94.41 13.435 94.303 13.887 94.09 14.298 Z M 101.747 14.175 L 99.636 8.344 L 97.288 8.344 L 100.802 17.278 L 102.706 17.278 L 106.219 8.344 L 103.872 8.344 Z M 115.426 10.822 C 115.229 10.302 114.944 9.839 114.573 9.434 C 114.2 9.029 113.749 8.711 113.218 8.481 C 112.687 8.251 112.071 8.136 111.371 8.136 C 110.539 8.136 109.787 8.339 109.114 8.744 C 108.441 9.149 107.91 9.699 107.521 10.394 C 107.133 11.09 106.938 11.886 106.938 12.784 C 106.938 13.681 107.13 14.437 107.513 15.149 C 107.896 15.86 108.432 16.424 109.122 16.84 C 109.811 17.256 110.616 17.464 111.535 17.464 C 112.148 17.464 112.709 17.371 113.218 17.185 C 113.727 16.999 114.167 16.741 114.539 16.413 C 114.911 16.085 115.185 15.707 115.36 15.28 L 113.62 14.426 C 113.423 14.777 113.152 15.056 112.807 15.264 C 112.463 15.472 112.044 15.576 111.552 15.576 C 111.059 15.576 110.624 15.461 110.246 15.231 C 109.869 15.001 109.584 14.675 109.393 14.254 C 109.275 13.996 109.202 13.714 109.173 13.408 L 115.607 13.408 C 115.651 13.288 115.681 13.145 115.697 12.981 C 115.713 12.817 115.722 12.659 115.722 12.505 C 115.722 11.903 115.624 11.342 115.426 10.822 Z M 110.269 10.345 C 110.619 10.121 111.018 10.009 111.467 10.009 C 111.916 10.009 112.34 10.124 112.674 10.354 C 113.008 10.584 113.248 10.896 113.396 11.29 C 113.464 11.47 113.505 11.661 113.521 11.864 L 109.315 11.864 C 109.351 11.673 109.404 11.495 109.472 11.331 C 109.653 10.898 109.918 10.57 110.269 10.345 Z M 15.148 2.906 L 9.688 0.307 C 8.885 -0.076 7.963 -0.076 7.16 0.307 L 1.7 2.906 C 0.671 3.396 0.006 4.454 0.006 5.6 L 0.006 15.76
1915 C 0.006 16.338 0.471 16.804 1.045 16.804 C 1.618 16.804 2.083 16.338 2.083 15.765 L 2.083 15.208 C 2.264 14.315 2.864 13.479 3.783 12.847 C 4.229 12.54 4.719 12.303 5.227 12.144 C 5.215 12.283 5.207 12.423 5.206 12.565 C 5.19 13.84 5.78 15.174 6.747 16.046 C 7.465 16.693 8.332 17.037 9.223 17.037 C 9.357 17.037 9.492 17.03 9.627 17.014 C 10.649 16.894 11.488 16.323 11.93 15.446 C 12.4 14.51 12.35 13.377 11.796 12.413 C 11.028 11.076 9.538 10.309 8.301 10.006 C 9.006 9.143 9.986 8.489 10.944 8.197 C 12.037 7.864 13.612 8.018 14.755 9.45 C 14.759 9.454 14.762 9.457 14.765 9.461 L 14.765 15.765 C 14.765 16.338 15.23 16.804 15.803 16.804 C 16.377 16.804 16.842 16.338 16.842 15.765 L 16.842 5.6 C 16.842 4.454 16.177 3.396 15.148 2.906 Z M 10.302 14.11 C 10.504 14.461 10.534 14.869 10.381 15.173 C 10.254 15.426 10.022 15.573 9.692 15.612 C 9.142 15.676 8.71 15.404 8.444 15.164 C 7.916 14.688 7.58 13.937 7.589 13.252 C 7.591 13.031 7.615 12.814 7.658 12.601 C 7.749 12.612 7.839 12.627 7.928 12.644 C 8.786 12.811 9.853 13.328 10.302 14.11 Z M 10.425 6.325 C 8.727 6.843 6.946 8.187 5.992 9.997 C 4.831 10.142 3.679 10.572 2.692 11.25 C 2.512 11.374 2.338 11.506 2.17 11.646 L 2.17 5.715 C 2.17 5.366 2.37 5.045 2.68 4.897 L 8.14 2.298 C 8.376 2.186 8.647 2.186 8.883 2.298 L 14.342 4.897 C 14.652 5.045 14.852 5.366 14.852 5.715 L 14.852 6.864 C 13.524 6.082 11.932 5.865 10.425 6.325 Z" fill="currentColor" /> 192 </svg> 193 </div> 194 <span class="case-company-text">Top 5% Global PropTech Startup, Raised £2M</span> 195 </div> 196 197 <!-- Tags --> 198 <div class="case-tags" aria-label="Project type"> 199 <span class="case-tag"> 200 <svg class="tag-icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M14.25,2H3.75c-1.517,0-2.75,1.233-2.75,2.75V13.25c0,1.517,1.233,2.75,2.75,2.75H14.25c1.517,0,2.75-1.233,2.75-2.75V4.75c0-1.517-1.233-2.75-2.75-2.75ZM4,6c-.552,0-1-.448-1-1s.448-1,1-1,1,.448,1,1-.448,1-1,1Zm3,0c-.552,0-1-.448-1-1s.448-1,1-1,1,.448,1,1-.448,1-1,1Z"/></svg> 201 Web App 202 </span> 203 <span class="case-tag"> 204 <svg class="tag-icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><circle cx="12" cy="8" r="1" fill="none"/><path d="M17.53,5.792l-.988-.397h-.002s-1.041-.42-1.041-.42v-1.475c0-.414-.336-.75-.75-.75s-.75,.336-.75,.75v.872L7.03,1.566c-.384-.156-.822,.032-.976,.416-.155,.384,.031,.821,.416,.976l.53,.213v12.829h3.75v-3.5c0-.69,.56-1.25,1.25-1.25s1.25,.56,1.25,1.25v3.5h2c.965,0,1.75-.785,1.75-1.75V7.19c.082,.029,.167,.048,.25,.048,.297,0,.579-.177,.696-.47,.155-.384-.031-.821-.416-.976Zm-5.53,3.208c-.552,0-1-.448-1-1s.448-1,1-1,1,.448,1,1-.448,1-1,1Z"/><path d="M5.5,14.5v-1.75c0-.414-.336-.75-.75-.75s-.75,.336-.75,.75v1.75h-1.25c-.138,0-.25-.112-.25-.25v-5.157l4.5-1.812v-1.617L.47,8.292c-.384,.155-.57,.592-.416,.976,.118,.292,.399,.47,.696,.47,.083,0,.167-.019,.25-.048v4.56c0,.965,.785,1.75,1.75,1.75H7v-1.5h-1.5Z"/></svg> 205 Proptech 206 </span> 207 </div> 208 209 </header><!-- /.case-hero --> 210 211 <!-- ===== HERO MOCKUP ===== --> 212 <div class="case-mockup-wrap" data-stagger-item> 213 <div class="case-mockup-scroll"> 214 <div class="mockup-group"> 215 <div class="mockup"> 216 <span class="thumb-wrap" data-thumbhash="/PcFBYAtqrKXl3gPXJWWwkHPoKcI"><picture><source type="image/avif" srcset="/homemove-mockup-768.avif 768w, /homemove-mockup-1280.avif 1280w, /homemove-mockup-2880.avif 2880w" sizes="(max-width: 767px) 85vw, 1280px"><source type="image/webp" srcset="/homemove-mockup-768.webp 768w, /homemove-mockup-1280.webp 1280w, /homemove-mockup-2880.webp 2880w" sizes="(max-width: 767px) 85vw, 1280px"><img src="/homemove-mockup-1280.png" srcset="/homemove-mockup-768.png 768w, /homemove-mockup-1280.png 1280w, /homemove-mockup.png 2880w" sizes="(max-width: 767px) 85vw, 1280px" width="2880" height="2048" alt="Homemove project â Orchestrated Clarity in Chaos" fetchpriority="high" decoding="async"></picture></span> 217 </div> 218 </div> 219 </div> 220 <div class="mockup mockup--mobile">
221 <span class="thumb-wrap" data-thumbhash="+RcGDATHtndnh4dZOviXgHYGyA=="><picture><source type="image/avif" srcset="/homemove-mobile-mockup-375.avif 375w, /homemove-mobile-mockup-1125.avif 1125w" sizes="(max-width: 661px) 22vw, 333px"><source type="image/webp" srcset="/homemove-mobile-mockup-375.webp 375w, /homemove-mobile-mockup-1125.webp 1125w" sizes="(max-width: 661px) 22vw, 333px"><img src="/homemove-mobile-mockup-375.png" srcset="/homemove-mobile-mockup-375.png 375w, /homemove-mobile-mockup.png 1125w" sizes="(max-width: 661px) 22vw, 333px" alt="Homemove mobile app welcome screen" width="1125" height="2148" fetchpriority="low" decoding="async"></picture></span> 222 </div> 223 </div> 224 225 </div><!-- /.case-hero-area --> 226 227 <!-- ===== MAIN CONTENT ===== --> 228 <main class="case-content" data-stagger> 229 230 <!-- ===== ROLES ===== --> 231 <div class="case-roles case-narrow" role="region" aria-label="Project roles"> 232 233 <div class="case-role"> 234 <div class="case-role-label"> 235 <svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 236 <path d="M2.60518 13.1674C3.69058 10.7157 6.14168 9 8.99999 9C11.7634 9 14.1462 10.6037 15.2822 12.9257C15.3564 13.0774 15.4289 13.2326 15.4797 13.3894C15.8649 14.5805 15.1811 15.8552 13.9874 16.2313C12.705 16.6354 11.0072 17 8.99999 17C6.99283 17 5.29503 16.6354 4.01259
23616.2313C2.74425 15.8317 2.05162 14.4186 2.60518 13.1674Z"/> 237 <path d="M9 7.50049C10.7952 7.50049 12.25 6.04543 12.25 4.25049C12.25 2.45554 10.7952 1.00049 9 1.00049C7.20482 1.00049 5.75 2.45554 5.75 4.25049C5.75 6.04543 7.20482 7.50049 9 7.50049Z"/> 238 </svg> 239 Responsibilities 240 </div> 241 <p class="case-role-value">UI Design · UX Design · User Research · End-To-End Ownership</p> 242 </div> 243 244 <div class="case-role"> 245 <div class="case-role-label"> 246 <svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 247 <path fill-rule="evenodd" clip-rule="evenodd" d="M0.554137 13.5756C1.34525 11.476 3.36866 9.97803 5.74997 9.97803C8.13128 9.97803 10.1547 11.476 10.9458 13.5756C11.3059 14.5316 10.7272 15.5154 9.84596 15.8103C8.82613 16.1509 7.42657 16.477 5.75097 16.477C4.0754 16.477 2.67527 16.1511 1.65458 15.8105C0.771586 15.5163 0.194851 14.5312 0.554137 13.5756Z"/> 248 <path d="M12.5523 13.9774C13.9847 13.9162 15.1901 13.6251 16.096 13.3225C16.9772 13.0276 17.5559 12.0438 17.1958 11.0878C16.4047 8.98817 14.3813 7.49023 12 7.49023C10.5581 7.49023 9.24737 8.03945 8.26202 8.9389C10.147 9.65833 11.6398 11.1634 12.3495 13.0469C12.4675 13.3603 12.5329 13.6726 12.5523 13.9774Z"/> 249 <path d="M5.75 8.50049C6.99267 8.50049 8 7.49361 8 6.25049C8 5.00736 6.99267 4.00049 5.75 4.00049C4.50733 4.00049 3.5 5.00736 3.5 6.25049C3.5 7.49361 4.50733 8.50049 5.75 8.50049Z"/> 250 <path d="M12 6.00049C13.2427 6.00049 14.25 4.99361 14.25 3.75049C14.25 2.50736 13.2427 1.50049 12 1.50049C10.7573 1.50049 9.75 2.50736 9.75 3.75049C9.75 4.99361 10.7573 6.00049 12 6.00049Z"/> 251 </svg> 252 Team 253 </div> 254 <p class="case-role-value">1 Head of Design · 1 Head of Operations · 2 Full Stack Developers</p> 255 </div> 256 257 </div><!-- /.case-roles --> 258 259 <!-- ===== CAR CARD ===== --> 260 <div class="case-car case-narrow" role="region" aria-label="Context, Action, Results"> 261 262 <!-- Context --> 263 <labelled-section label="Context"> 264 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 265 <path d="M15.487,5.427l-3.914-3.914c-.331-.331-.77-.513-1.237-.513H4.75c-1.517,0-2.75,1.233-2.75,2.75V14.25c0,1.517,1.233,2.75,2.75,2.75H13.25c1.517,0,2.75-1.233,2.75-2.75V6.664c0-.467-.182-.907-.513-1.237Zm-9.737,.573h2c.414,0,.75,.336,.75,.75s-.336,.75-.75,.75h-2c-.414,0-.75-.336-.75-.75s.336-.75,.75-.75Zm6.5,7.5H5.75c-.414,0-.75-.336-.75-.75s.336-.75,.75-.75h6.5c.414,0,.75,.336,.75,.75s-.336,.75-.75,.75Zm0-3H5.75c-.414,0-.75-.336-.75-.75s.336-.75,.75-.75h6.5c.414,0,.75,.336,.75,.75s-.336,.75-.75,.75Zm2.182-4h-2.932c-.55,0-1-.45-1-1V2.579l.013-.005,3.922,3.921-.002,.005Z"/> 266 </svg> 267 <div class="case-car-body-card"> 268 <p class="case-car-body">Homemove is a PropTech startup consolidating the entire home-moving process into one platform. The existing product surfaced everything a mover could do, with no logic about what they should do first. I joined as the sole designer to fix that.</p> 269 </div> 270 </labelled-section> 271 272 <!-- Action --> 273 <labelled-section label="Action"> 274 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 275 <path d="M16.2675 8.4546C15.8398 8.4097 15.5088 8.76952 15.4971 9.18312C15.3994 12.7256 12.545 15.5 8.99995 15.5C6.83525 15.5 4.87116 14.4272 3.67296 12.6987L5.12976 12.9004C5.53016 12.9531 5.91885 12.6704 5.97545 12.2598C6.03205 11.8496 5.74495 11.4712 5.33485 11.4141L2.39055 11.007C1.98915 10.9528 1.60246 11.2364 1.54486 11.647L1.13666 14.5918C1.08006 15.002 1.36615 15.3809 1.77725 15.4375C1.81145 15.4424 1.84655 15.4448 1.88075 15.4448C2.24895 15.4448 2.57025 15.1734 2.62295 14.7979L2.73796 13.9667C4.23026 15.852 6.51045 16.9999 8.99995 16.9999C13.3633 16.9999 16.876 13.5849 16.9971 9.22499C17.0078 8.81089 16.6816 8.4658 16.2675 8.4546Z"/> 276 <path d="M1.73242 9.54688C1.73922 9.54738 1.74611 9.54736 1.75291 9.54736C2.15821 9.54736 2.49121 9.22507 2.50291 8.81787C2.59961 5.27537 5.45411 2.5 9.00001 2.5C11.1629 2.5 13.1295 3.57317 14.3272 5.30127L12.8701 5.09961C12.4678 5.04981 12.081 5.33013 12.0244 5.74023C11.9678 6.15033 12.2549 6.52884 12.665 6.58594L15.6093 6.99304C15.6435 6.99744 15.6786 6.99988 15.7128 6.99988C16.081 6.99988 16.4023 6.72841 16.455 6.35291L16.8632 3.40808C16.9198 2.99788 16.6337 2.61898 16.2226 2.56238C15.8183 2.50328 15.4345 2.79143 15.3769 3.20203L15.2616 4.03552C13.7688 2.14982 11.4876 0.999878 8.99991 0.999878C4.63561 0.999878 1.12292 4.41596 1.00282 8.77686C0.992119 9.19096 1.31832 9.53568 1.73242 9.54688Z"/> 277 </svg> 278 <div class="case-car-body-card"> 279 <p class="case-car-body">I built an Object Map using OOUX to model core entities and their dependencies before touching a single screen. That structural foundation drove a fundamental shift: from a service-first dashboard to a timeline-first task hierarchy.</p> 280 </div> 281 </labelled-section> 282 283 <!-- Results --> 284 <labelled-section label="Results"> 285 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 286 <path d="m15.25,15.5H4.75c-1.5166,0-2.75-1.2334-2.75-2.75V2.75c0-.4141.3359-.75.75-.75s.75.3359.75.75v10c0,.6895.5605,1.25,1.25,1.25h10.5c.4141,0,.75.3359.75.75s-.3359.75-.75.75Z"/> 287 <rect x="10.5" y="3.5" width="3.5" height="8.5" rx="1" ry="1"/> 288 <rect x="5.5" y="7" width="3.5" height="5" rx="1" ry="1"/> 289 </svg> 290 <div class="case-car-body-card"> 291 <p class="case-car-body">Users and stakeholders confirmed the new task hierarchy was significantly clearer than the original</p> 292 <p class="case-car-body">A data-backed artefact resolved a critical stakeholder conflict and became the team's design standard</p> 293 <p class="case-car-body">Developer alignment improved through a shared object language across the team</p> 294 </div> 295 </labelled-section> 296 297 </div><!-- /.case-car --> 298 299 <!-- ===== OUTCOME IMAGERY ===== --> 300 <section class="case-images" aria-label="Outcome imagery"> 301 302 <!-- 1 â Tasks --> 303 <figure class="image-container"> 304 <div class="image-container__media">
305 <span class="thumb-wrap" data-thumbhash="/fcBBYCQVyWXmohlZLdojGDLNo+r"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/homemove/tasks-light.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/homemove/tasks-light.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/homemove/tasks-light.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/homemove/tasks-light.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Task list with integrated booking options for guided home selling" width="2880" height="2048" loading="lazy" decoding="async"></span> 306 </div> 307 <figcaption class="image-container__caption"> 308 <span class="image-container__text">Task list with integrated booking options for guided home selling</span> 309 <span class="image-container__meta"> 310 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 311 <span class="image-container__number" aria-hidden="true"></span> 312 </span> 313 </figcaption> 314 </figure> 315 316 <!-- 2 â Chat --> 317 <figure class="image-container"> 318 <div class="image-container__media"> 319 <span class="thumb-wrap" data-thumbhash="/AcCBYJek0eHiXkPptOl03CZAooY"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/homemove/chat-light.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/homemove/chat-light.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/homemove/chat-light.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/homemove/chat-light.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="In-app messaging with home moving partners for helpful, human support" width="2880" height="2048" loading="lazy" decoding="async"></span> 320 </div> 321 <figcaption class="image-container__caption"> 322 <span class="image-container__text">In-app messaging with home moving partners for helpful, human support</span> 323 <span class="image-container__meta"> 324 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 325 <span class="image-container__number" aria-hidden="true"></span> 326 </span> 327 </figcaption> 328 </figure> 329 330 <!-- 3 â Events --> 331 <figure class="image-container"> 332 <div class="image-container__media"> 333 <span class="thumb-wrap" data-thumbhash="/fcBBYBQUgmXrKpEdKV4mkDbdVz+"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/homemove/events-light.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/homemove/events-light.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/homemove/events-light.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/homemove/events-light.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Event dashboard showing upcoming appointments across services" width="2880" height="2048" loading="lazy" decoding="async"></span> 334 </div>
335 <figcaption class="image-container__caption"> 336 <span class="image-container__text">Event dashboard showing upcoming appointments across services</span> 337 <span class="image-container__meta"> 338 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 339 <span class="image-container__number" aria-hidden="true"></span> 340 </span> 341 </figcaption> 342 </figure> 343 344 <!-- 4 â Documents --> 345 <figure class="image-container"> 346 <div class="image-container__media"> 347 <span class="thumb-wrap" data-thumbhash="/fcBBYAd0DRP+4YYZGeZbUC9FvvJ"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/homemove/documents-light.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/homemove/documents-light.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/homemove/documents-light.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/homemove/documents-light.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Centralised document hub with filters for status and service type" width="2880" height="2048" loading="lazy" decoding="async"></span> 348 </div> 349 <figcaption class="image-container__caption"> 350 <span class="image-container__text">Centralised document hub with filters for status and service type</span> 351 <span class="image-container__meta"> 352 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 353 <span class="image-container__number" aria-hidden="true"></span> 354 </span> 355 </figcaption> 356 </figure> 357 358 <!-- 5 â The mobile app, composited into a single image so the set gets one 359 frame, one caption and one image number. Placed after all four desktop 360 screens rather than paired with each one: the desktop shots are the 361 primary artefact and stay full width, and this makes the responsive 362 point once instead of repeating it per screen. 363 364 Three screens, not four â Chat is left out because its list runs to 365 two threads and stops, so it carries a large empty area the others 366 don't; it still has its own full-width desktop screenshot above. 367 368 It is deliberately ONE image rather than a grid of separate ones. As a 369 grid the block came out 1,131px tall against the desktop screens' 984px 370 â the supporting detail outsized the primary artefact â and the gaps 371 were CSS-driven, so they collapsed as the columns narrowed. Composited, 372 the spacing, corner radii and hairline strokes are part of the asset 373 and hold at every width. Built by scripts/build-mobile-screens.py. --> 374 <figure class="image-container image-container--screen-row"> 375 <div class="image-container__media"> 376 <span class="thumb-wrap" data-thumbhash="/PeBA4A3ZqmBiaCFgAB5eVdYeIeAiDg="><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/homemove/mobile-screens.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/homemove/mobile-screens.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/homemove/mobile-screens.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/homemove/mobile-screens.png 2880w" sizes="(max-width: 809px) 1000px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Homemove mobile app â three screens side by side: the task list filtered to to-do, upcoming events grouped by month, and the document hub showing paperwork ready to sign" width="2880" height="1659" loading="lazy" decoding="async"></span> 377 </div>
378 <figcaption class="image-container__caption"> 379 <span class="image-container__text">The same screens in the pocket app â tasks, events and documents</span> 380 <span class="image-container__meta"> 381 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 382 <span class="image-container__number" aria-hidden="true"></span> 383 </span> 384 </figcaption> 385 </figure> 386 387 <!-- The same screens again, one per row, for phones and small tablets. 388 Below 810px the composited row above is swapped out for these: a wide 389 row has to scroll sideways at that width, and a single caption at the 390 bottom of it ends up a long way from the first screen. Each screen 391 gets its own frame, caption and number instead. 392 393 Exactly one of the two is ever in the layout â the hidden one is 394 display:none, so it generates no boxes and takes no part in the image 395 counter, and its lazy images never enter the viewport so they are not 396 fetched. The trade is that this block and the composite carry the same 397 content twice: edit a caption here and check the one above too. --> 398 <div class="screen-row-stack"> 399 400 <!-- Tasks --> 401 <figure class="image-container"> 402 <div class="image-container__media"> 403 <span class="thumb-wrap" data-thumbhash="/PcBDAD2NqhGiYQbOVj4koKPuQ=="><img src="/cdn-cgi/image/format=auto,width=1125,quality=85/homemove/tasks-mobile.png" srcset="/cdn-cgi/image/format=auto,width=375,quality=85/homemove/tasks-mobile.png 375w, /cdn-cgi/image/format=auto,width=640,quality=85/homemove/tasks-mobile.png 640w, /cdn-cgi/image/format=auto,width=1125,quality=85/homemove/tasks-mobile.png 1125w" sizes="(max-width: 607px) calc(100vw - 48px), 560px" alt="Homemove mobile app â the task list, filtered to to-do, pending and complete" width="1125" height="2088" loading="lazy" decoding="async"></span> 404 </div> 405 <figcaption class="image-container__caption"> 406 <span class="image-container__text">The task list, in the pocket app</span> 407 <span class="image-container__meta"> 408 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 409 <span class="image-container__number" aria-hidden="true"></span> 410 </span> 411 </figcaption> 412 </figure> 413 414 <!-- Events --> 415 <figure class="image-container"> 416 <div class="image-container__media"> 417 <span class="thumb-wrap" data-thumbhash="/PcBBADRCbkFrJMcWinmkQB8nQ=="><img src="/cdn-cgi/image/format=auto,width=1125,quality=85/homemove/events-mobile.png" srcset="/cdn-cgi/image/format=auto,width=375,quality=85/homemove/events-mobile.png 375w, /cdn-cgi/image/format=auto,width=640,quality=85/homemove/events-mobile.png 640w, /cdn-cgi/image/format=auto,width=1125,quality=85/homemove/events-mobile.png 1125w" sizes="(max-width: 607px) calc(100vw - 48px), 560px" alt="Homemove mobile app â upcoming events grouped by month, with mortgage, valuation and conveyancing appointments" width="1125" height="2088" loading="lazy" decoding="async"></span> 418 </div>
419 <figcaption class="image-container__caption"> 420 <span class="image-container__text">Appointments, month by month</span> 421 <span class="image-container__meta"> 422 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 423 <span class="image-container__number" aria-hidden="true"></span> 424 </span> 425 </figcaption> 426 </figure> 427 428 <!-- Documents --> 429 <figure class="image-container"> 430 <div class="image-container__media"> 431 <span class="thumb-wrap" data-thumbhash="/PcBDADWV6gGqXJriUrHkCBKDQ=="><img src="/cdn-cgi/image/format=auto,width=1125,quality=85/homemove/documents-mobile.png" srcset="/cdn-cgi/image/format=auto,width=375,quality=85/homemove/documents-mobile.png 375w, /cdn-cgi/image/format=auto,width=640,quality=85/homemove/documents-mobile.png 640w, /cdn-cgi/image/format=auto,width=1125,quality=85/homemove/documents-mobile.png 1125w" sizes="(max-width: 607px) calc(100vw - 48px), 560px" alt="Homemove mobile app â the document hub, showing paperwork flagged urgent and ready to sign" width="1125" height="2088" loading="lazy" decoding="async"></span> 432 </div> 433 <figcaption class="image-container__caption"> 434 <span class="image-container__text">Paperwork, sorted by urgency</span> 435 <span class="image-container__meta"> 436 <svg class="image-container__icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M13.194,8.384c-1.072-1.072-2.816-1.072-3.889,0L3.067,14.623c.214,.078,.442,.127,.683,.127H14.25c1.105,0,2-.896,2-2v-1.311l-3.056-3.056Z"/><path d="M14.25,15.5H3.75c-1.517,0-2.75-1.233-2.75-2.75V5.25c0-1.517,1.233-2.75,2.75-2.75H14.25c1.517,0,2.75,1.233,2.75,2.75v7.5c0,1.517-1.233,2.75-2.75,2.75ZM3.75,4c-.689,0-1.25,.561-1.25,1.25v7.5c0,.689,.561,1.25,1.25,1.25H14.25c.689,0,1.25-.561,1.25-1.25V5.25c0-.689-.561-1.25-1.25-1.25H3.75Z"/><circle cx="5.75" cy="7.25" r="1.25"/></svg> 437 <span class="image-container__number" aria-hidden="true"></span> 438 </span> 439 </figcaption> 440 </figure> 441 442 </div><!-- /.screen-row-stack --> 443 444 </section><!-- /.case-images --> 445 446 <!-- ===== NEXT PROJECT ===== --> 447 <section class="case-next case-narrow" aria-label="Next project"> 448 <labelled-section label="Next project"> 449 <next-project-card 450 href="../pikl-dashboard/" 451 title="Replaced guesswork with guided workflows"> 452 <div class="mockup"> 453 <span class="thumb-wrap" data-thumbhash="/QcCBYAymHlwqIpRpzmGdlDDw/f+"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard-mockup.png" srcset="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard-mockup.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard-mockup.png 2880w" sizes="(min-width: 1280px) 1216px, 85vw" alt="Pikl Dashboard â Replaced Guesswork With Guided Workflows" width="2880" height="2048" loading="lazy" decoding="async"></span> 454 </div> 455 </next-project-card> 456 </labelled-section> 457 </section> 458 459 <!-- ===== NEXT STEPS CTA ===== --> 460 <section class="case-cta case-narrow" aria-label="Get in touch"> 461 <labelled-section label="Next steps"> 462 <next-steps-cta></next-steps-cta> 463 </labelled-section> 464 </section> 465 466 <!-- ===== FOOTER ===== --> 467 <footer class="case-footer case-narrow"> 468 <p class="copyright">© 2026 John Miller</p> 469 </footer> 470 471 </main><!-- /.case-content --> 472 473</div><!-- /.case-page --> 474 475<!-- Shader initialization -->
476<script type="module"> 477 import { 478 ShaderMount, 479 brandColor, 480 cardBg, 481 buildShaderColors, 482 siteFragmentShader, 483 createShaderUniforms, 484 SHADER_MAX_PIXELS, 485 getCardScale, 486 getHeroScale, 487 getHeroSpeed, 488 onBreakpointChange, 489 shaderFrameOffset, 490 averageColor, 491 createFadeColorSampler, 492 createShaderVisibilityGate, 493 } from '../shader-base.js?v=1'; 494 495 // Brand stops use `brandColor()` so light-mode pastels are auto-darkened 496 // in dark mode. `cardBg()` is white in light, near-black in dark. Wrapped 497 // in builder functions so the matchMedia change listener below can 498 // re-evaluate brandColor/cardBg with the fresh isDark() result when the 499 // user toggles OS theme mid-session. 500 const buildHeroColors = () => buildShaderColors({ brand: [brandColor(0.899, 0.050, 279.1), brandColor(0.915, 0.044, 21.5)], bg: cardBg() }); 501 const buildPiklColors = () => buildShaderColors({ brand: [brandColor(0.896, 0.064, 350.7)], bg: cardBg() }); 502 503 // Tint the next-card's bottom fade with its own shader palette, mirroring the 504 // homepage cards. Re-run on theme change so it tracks light/dark. 505 const applyNextFade = () => 506 document.getElementById('next-card-shader')?.closest('.next-card') 507 ?.style.setProperty('--card-fade-color', averageColor(buildPiklColors())); 508 509 const prefersReducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches; 510 511 // Mount registry â each entry pairs a ShaderMount with its colour builder 512 // so the scheme-change listener can repush fresh u_colors to all mounts. 513 const mounts = []; 514 let fadeSampler = null; // live --card-fade-color sampler for the next-card 515 516 // Lightbox shader-pause integration ('site:pause-shaders' on open, 517 // 'site:resume-shaders' on destroy, from photoswipe-init.js) is handled 518 // inside each mount's visibility gate (createShaderVisibilityGate), which 519 // also pauses a shader whenever it's scrolled off-screen â see 520 // shader-base.js. Resuming through the gate re-checks visibility, so a 521 // lightbox closed mid-article doesn't restart an off-screen shader. 522 const speedFor = () => (prefersReducedMotion ? 0 : getHeroSpeed()); 523 524 async function initShaders() { 525 // Yield frames between compiles so the entrance animation's 526 // main-thread paint (filter blur) isn't blocked by GL compilation. 527 const yieldFrames = () => 528 new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); 529 530 await yieldFrames(); 531 532 let heroMount = null; 533 let nextMount = null; 534 let heroGate = null; 535 let nextGate = null; 536 537 // Hero shader â container opacity fade is handled in homemove.css 538 const heroEl = document.getElementById('case-shader-gradient'); 539 if (heroEl) { 540 const heroColors = buildHeroColors(); 541 try { 542 heroMount = new ShaderMount(heroEl, siteFragmentShader, { 543 ...createShaderUniforms(), 544 u_scale: getHeroScale(), 545 u_colors: heroColors, 546 u_colorsCount: heroColors.length, 547 }, undefined, prefersReducedMotion ? 0 : getHeroSpeed(), shaderFrameOffset('gradient-homemove')); 548 heroMount.setMaxPixelCount(SHADER_MAX_PIXELS); 549 heroGate = createShaderVisibilityGate(heroEl, heroMount, speedFor); 550 mounts.push({ mount: heroMount, build: buildHeroColors }); 551 await yieldFrames(); 552 } catch (e) { heroMount = null; } 553 } 554 555 // Next-card shader (Pikl dashboard colors from homepage) 556 const nextCardShader = document.getElementById('next-card-shader'); 557 if (nextCardShader) { 558 try { 559 const piklColors = buildPiklColors(); 560 nextMount = new ShaderMount(nextCardShader, siteFragmentShader, { 561 ...createShaderUniforms(), 562 u_colors: piklColors, 563 u_colorsCount: piklColors.length, 564 }, undefined, prefersReducedMotion ? 0 : getHeroSpeed(), shaderFrameOffset('gradient-pikl-dashboard')); 565 nextMount.setMaxPixelCount(SHADER_MAX_PIXELS); 566 nextGate = createShaderVisibilityGate(nextCardShader, nextMount, speedFor); 567 mounts.push({ mount: nextMount, build: buildPiklColors }); 568 applyNextFade(); 569 const nextCard = nextCardShader.closest('.next-card'); 570 if (nextCard && !prefersReducedMotion) { 571 fadeSampler = createFadeColorSampler(nextMount, nextCard, { getSpeed: getHeroSpeed, isVisible: () => nextGate.visible }); 572 fadeSampler.start(); 573 } 574 } catch (e) { nextMount = null; } 575 } 576 577 onBreakpointChange(() => { 578 if (heroMount) { 579 heroMount.setUniforms({ u_scale: getHeroScale() }); 580 heroGate.apply(); 581 } 582 if (nextMount) { 583 nextMount.setUniforms({ u_scale: getCardScale() }); 584 nextGate.apply(); 585 } 586 }); 587 } 588 589 // React to OS theme changes without requiring a page refresh â rebuilds 590 // each mount's u_colors via its colour builder so brandColor/cardBg are 591 // re-evaluated with the new isDark() result. URL params (?dark / ?light) 592 // override OS, so the rebuild stays locked if a param is present. 593 matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 594 for (const entry of mounts) { 595 const colors = entry.build(); 596 entry.mount.setUniforms({ u_colors: colors, u_colorsCount: colors.length }); 597 } 598 applyNextFade(); 599 fadeSampler?.snap(); 600 }); 601 602 initShaders(); 603</script>
603 604 605</body> 606</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.