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>Replaced Guesswork With Guided Workflows · Pikl Dashboard · John Miller</title> 17 <meta name="description" content="How I redesigned Pikl's internal policy admin dashboard â turning a fragmented support tool into a unified workflow for the insurance team behind the customer." /> 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="/pikl-dashboard-mockup-1280.avif" 35 imagesrcset="/pikl-dashboard-mockup-768.avif 768w, /pikl-dashboard-mockup-1280.avif 1280w, /pikl-dashboard-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="pikl"> 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">Replaced Guesswork With Guided Workflows</h1> 185 186 <!-- Company badge row --> 187 <div class="case-company"> 188 <!-- Pikl wordmark SVG --> 189 <div class="case-logo-wrap"> 190 <svg class="case-logo" xmlns="http://www.w3.org/2000/svg" width="47" height="20" fill="none" viewBox="0 0 47 20" aria-label="Pikl"> 191 <path d="M 13.559 3.428 C 13.808 3.208 13.946 2.88 13.946 2.523 L 13.946 1.291 C 13.946 0.606 13.366 0.03 12.677 0.03 L 2.716 0.03 C 2.026 0.03 1.446 0.606 1.446 1.291 L 1.446 2.523 C 1.446 2.88 1.584 3.181 1.833 3.428 L 0.094 6.003 C -0.154 6.359 0.122 6.825 0.536 6.825 L 1.446 6.825 L 1.446 12.989 C 1.446 14.167 2.412 15.126 3.598 15.126 L 11.794 15.099 C 12.98 15.099 13.946 14.14 13.946 12.962 L 13.946 6.797 L 14.856 6.797 C 15.27 6.797 15.519 6.332 15.298 5.976 Z M 2.55 1.455 C 2.55 1.263 2.716 1.099 2.909 1.099 L 12.483 1.099 C 12.677 1.099 12.842 1.263 12.842 1.455 L 12.842 2.332 C 12.842 2.523 12.677 2.688 12.483 2.688 L 2.909 2.688 C 2.716 2.688 2.55 2.523 2.55 2.332 Z M 10.938 10.195 L 10.938 13.975 L 6.937 13.975 L 6.937 11.838 L 5.613 11.838 L 5.613 13.975 L 4.454 13.975 L 4.454 10.195 L 3.461 10.195 L 7.682 6.14 L 11.959 10.195 Z M 9.145 4.77 C 8.013 4.77 7.931 5.373 6.579 5.373 C 5.364 5.373 5.226 4.77 4.095 4.77 C 2.964 4.77 2.108 5.702 1.584 5.756 C 2.026 5.154 2.881 3.839 2.881 3.839 L 12.428 3.839 C 12.428 3.839 13.118 4.852 13.725 5.756 C 11.269 5.893 11.021 4.77 9.145 4.77 Z" fill="currentColor" /> 192 <path d="M 7.324 10.959 C 7.324 11.397 7.82 11.945 8.731 12.438 C 9.586 12.027 10.138 11.37 10.138 10.959 C 10.138 10.521 9.779 10.164 9.338 10.164 C 9.09 10.164 8.869 10.274 8.731 10.438 C 8.593 10.274 8.372 10.164 8.124 10.164 C 7.655 10.164 7.324 10.521 7.324 10.959 Z" fill="currentColor" /> 193 <path d="M 23.217 2.877 C 26.639 2.877 29.426 5.644 29.426 9.041 C 29.426 12.438 26.722 15.205 23.217 15.205 C 21.424 15.205 19.906 14.521 19.051 13.562 L 19.051 17.973 C 19.051 19.096 18.14 20 17.009 20 L 17.009 9.041 C 17.009 5.644 19.796 2.877 23.217 2.877 Z M 35.965 0.027 L 35.965 7.342 C 35.965 7.342 35.967 7.34 35.97 7.336 L 35.983 7.323 C 36.183 7.135 38.013 5.627 39.966 3.973 C 41.345 2.817 43.044 2.898 43.722 2.989 L 43.789 2.999 C 43.938 3.021 44.022 3.041 44.022 3.041 L 37.234 8.904 L 44.078 15.068 C 44.078 15.068 41.649 15.644 40.021 14.192 C 39.666 13.877 39.26 13.512 38.846 13.136 L 38.699 13.003 C 38.65 12.958 38.601 12.914 38.552 12.869 L 38.405 12.735 C 37.158 11.599 35.965 10.493 35.965 10.493 L 35.965 15.068 L 33.923 15.068 L 33.923 2.082 C 33.923 0.932 34.834 0.027 35.965 0.027 Z M 32.378 3.041 L 32.378 15.068 L 30.336 15.068 L 30.336 3.041 Z M 46.809 0 L 46.837 13.014 C 46.837 14.137 45.926 15.041 44.823 15.041 L 44.795 2.027 C 44.795 0.904 45.706 0 46.809 0 Z M 23.217 4.932 C 20.927 4.932 19.078 6.767 19.078 9.041 C 19.078 11.315 20.927 13.151 23.217 13.151 C 25.507 13.151 27.356 11.315 27.356 9.041 C 27.356 6.767 25.507 4.932 23.217 4.932 Z M 32.351 0 L 32.351 2.027 L 30.309 2.027 C 30.336 0.904 31.247 0 32.351 0 Z" fill="currentColor" /> 194 </svg> 195 </div> 196 <span class="case-company-text">Airbnb Insurtech Startup, Raised £2.5M</span> 197 </div> 198 199 <!-- Tags --> 200 <div class="case-tags" aria-label="Project type">
201 <span class="case-tag"> 202 <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> 203 Web App 204 </span> 205 <span class="case-tag"> 206 <svg class="tag-icon" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 18" aria-hidden="true"><path d="M14.783,2.813l-5.25-1.68c-.349-.112-.718-.111-1.066,0L3.216,2.813c-.728,.233-1.216,.903-1.216,1.667v6.52c0,3.508,4.946,5.379,6.46,5.869,.177,.057,.358,.086,.54,.086s.362-.028,.538-.085c1.516-.49,6.462-2.361,6.462-5.869V4.48c0-.764-.489-1.434-1.217-1.667Z"/></svg> 207 Insurtech 208 </span> 209 </div> 210 211 </header><!-- /.case-hero --> 212 213 <!-- ===== HERO MOCKUP ===== --> 214 <div class="case-mockup-wrap" data-stagger-item> 215 <div class="case-mockup-scroll"> 216 <div class="mockup-group"> 217 <div class="mockup"> 218 <span class="thumb-wrap" data-thumbhash="/QcCBYAyqHlwqIpRpzmGdlDDw/f+"><picture><source type="image/avif" srcset="/pikl-dashboard-mockup-768.avif 768w, /pikl-dashboard-mockup-1280.avif 1280w, /pikl-dashboard-mockup-2880.avif 2880w" sizes="(max-width: 767px) 85vw, 1280px"><source type="image/webp" srcset="/pikl-dashboard-mockup-768.webp 768w, /pikl-dashboard-mockup-1280.webp 1280w, /pikl-dashboard-mockup-2880.webp 2880w" sizes="(max-width: 767px) 85vw, 1280px"><img src="/pikl-dashboard-mockup-1280.png" srcset="/pikl-dashboard-mockup-768.png 768w, /pikl-dashboard-mockup-1280.png 1280w, /pikl-dashboard-mockup.png 2880w" sizes="(max-width: 767px) 85vw, 1280px" width="2880" height="2048" alt="Pikl Dashboard â Replaced Guesswork With Guided Workflows" fetchpriority="high" decoding="async"></picture></span> 219 </div> 220 </div> 221 </div> 222 </div> 223 224 </div><!-- /.case-hero-area --> 225 226 <!-- ===== MAIN CONTENT ===== --> 227 <main class="case-content" data-stagger> 228 229 <!-- ===== ROLES ===== --> 230 <div class="case-roles case-narrow" role="region" aria-label="Project roles"> 231 232 <div class="case-role"> 233 <div class="case-role-label"> 234 <svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 235 <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
23516.2313C2.74425 15.8317 2.05162 14.4186 2.60518 13.1674Z"/> 236 <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"/> 237 </svg> 238 Responsibilities 239 </div> 240 <p class="case-role-value">UI Design · UX Design · User Research</p> 241 </div> 242 243 <div class="case-role"> 244 <div class="case-role-label"> 245 <svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 246 <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"/> 247 <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"/> 248 <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"/> 249 <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"/> 250 </svg> 251 Team 252 </div> 253 <p class="case-role-value">1 Product Owner · 1 Head of Design · 1 Head of Operations · 2 Full Stack Developers</p> 254 </div> 255 256 </div><!-- /.case-roles --> 257 258 <!-- ===== CAR CARD ===== --> 259 <div class="case-car case-narrow" role="region" aria-label="Context, Action, Results"> 260 261 <!-- Context --> 262 <labelled-section label="Context"> 263 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 264 <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"/> 265 </svg> 266 <div class="case-car-body-card"> 267 <p class="case-car-body">Pikl's internal policy admin tool was outdated and badly aligned to how support staff actually worked. Manual calculations, disconnected scripts and no proper customer search meant staff were compensating daily for what the system couldn't do.</p> 268 </div> 269 </labelled-section> 270 271 <!-- Action --> 272 <labelled-section label="Action"> 273 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 274 <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"/> 275 <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"/> 276 </svg> 277 <div class="case-car-body-card"> 278 <p class="case-car-body">I ran sessions with the support team to find where the friction was. The pattern was consistent: the tool was offloading its own complexity onto its users. I redesigned the dashboard to surface scripts, customer history and task context at the right moment, replacing memory-based decisions with structured guidance and clearer navigation throughout.</p> 279 </div> 280 </labelled-section> 281 282 <!-- Results --> 283 <labelled-section label="Results"> 284 <svg slot="icon" width="18" height="18" viewBox="0 0 18 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 285 <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"/> 286 <rect x="10.5" y="3.5" width="3.5" height="8.5" rx="1" ry="1"/> 287 <rect x="5.5" y="7" width="3.5" height="5" rx="1" ry="1"/> 288 </svg> 289 <div class="case-car-body-card"> 290 <p class="case-car-body">Research with five support staff reframed the project from a visual refresh into a structural redesign</p> 291 <p class="case-car-body">Scripted guidance and an activity log replaced memory-based decisions and manual workarounds</p> 292 <p class="case-car-body">The concept gave Pikl a scalable foundation for modernising a business-critical internal tool</p> 293 </div> 294 </labelled-section> 295 296 </div><!-- /.case-car --> 297 298 <!-- ===== OUTCOME IMAGERY ===== --> 299 <section class="case-images" aria-label="Outcome imagery"> 300 301 <!-- 1 â Dashboard --> 302 <figure class="image-container"> 303 <div class="image-container__media">
304 <span class="thumb-wrap" data-thumbhash="/QcCBYAymHlwqIpRpzmGdlDDw/f+"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/dashboard.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/pikl-dashboard/dashboard.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/dashboard.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard/dashboard.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Dashboard concept for managing policy tasks, metrics, and customer interactions" width="2880" height="2048" loading="lazy" decoding="async"></span> 305 </div> 306 <figcaption class="image-container__caption"> 307 <span class="image-container__text">Dashboard concept for managing policy tasks, metrics, and customer interactions</span> 308 <span class="image-container__meta"> 309 <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> 310 <span class="image-container__number" aria-hidden="true"></span> 311 </span> 312 </figcaption> 313 </figure> 314 315 <!-- 2 â New quote --> 316 <figure class="image-container"> 317 <div class="image-container__media"> 318 <span class="thumb-wrap" data-thumbhash="/QcCBYAV9bmGiXVSl3eXlyow5QrS"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/new-quote.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/pikl-dashboard/new-quote.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/new-quote.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard/new-quote.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Step-by-step quote form with scripted assistance for policy customisation" width="2880" height="2048" loading="lazy" decoding="async"></span> 319 </div> 320 <figcaption class="image-container__caption"> 321 <span class="image-container__text">Step-by-step quote form with scripted assistance for policy customisation</span> 322 <span class="image-container__meta"> 323 <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> 324 <span class="image-container__number" aria-hidden="true"></span> 325 </span> 326 </figcaption> 327 </figure> 328 329 <!-- 3 â Quote results --> 330 <figure class="image-container"> 331 <div class="image-container__media"> 332 <span class="thumb-wrap" data-thumbhash="/AcCBYCXGPJ1datkh2l3ZSaAlDB3"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/quote-results.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/pikl-dashboard/quote-results.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/quote-results.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard/quote-results.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Quote comparison interface for selecting and purchasing insurance policies" width="2880" height="2048" loading="lazy" decoding="async"></span> 333 </div>
334 <figcaption class="image-container__caption"> 335 <span class="image-container__text">Quote comparison interface for selecting and purchasing insurance policies</span> 336 <span class="image-container__meta"> 337 <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> 338 <span class="image-container__number" aria-hidden="true"></span> 339 </span> 340 </figcaption> 341 </figure> 342 343 <!-- 4 â Customers --> 344 <figure class="image-container"> 345 <div class="image-container__media"> 346 <span class="thumb-wrap" data-thumbhash="/QcCBYAzdbyBiWhQlnmZd3VQ8xvi"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/customers.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/pikl-dashboard/customers.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/customers.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard/customers.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Customer dashboard for managing policies and tracking status updates" width="2880" height="2048" loading="lazy" decoding="async"></span> 347 </div> 348 <figcaption class="image-container__caption"> 349 <span class="image-container__text">Customer dashboard for managing policies and tracking status updates</span> 350 <span class="image-container__meta"> 351 <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> 352 <span class="image-container__number" aria-hidden="true"></span> 353 </span> 354 </figcaption> 355 </figure> 356 357 <!-- 5 â Customer details --> 358 <figure class="image-container"> 359 <div class="image-container__media"> 360 <span class="thumb-wrap" data-thumbhash="/fcBBYAFtLaDmYcitmqZcxhw9wry"><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/customer-details.png" srcset="/cdn-cgi/image/format=auto,width=768,quality=85/pikl-dashboard/customer-details.png 768w, /cdn-cgi/image/format=auto,width=1280,quality=85/pikl-dashboard/customer-details.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/pikl-dashboard/customer-details.png 2880w" sizes="(max-width: 809px) 600px, (max-width: 1199px) calc(100vw - 48px), 1280px" alt="Customer profile interface for managing policy details and tracking changes" width="2880" height="2048" loading="lazy" decoding="async"></span> 361 </div> 362 <figcaption class="image-container__caption"> 363 <span class="image-container__text">Customer profile interface for managing policy details and tracking changes</span> 364 <span class="image-container__meta"> 365 <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>
366 <span class="image-container__number" aria-hidden="true"></span> 367 </span> 368 </figcaption> 369 </figure> 370 371 </section><!-- /.case-images --> 372 373 <!-- ===== NEXT PROJECT ===== --> 374 <section class="case-next case-narrow" aria-label="Next project"> 375 <labelled-section label="Next project"> 376 <next-project-card 377 href="../wren-kitchens/" 378 title="Accelerated time-on-task by 58%"> 379 <div class="mockup"> 380 <span class="thumb-wrap" data-thumbhash="7/cJDICvYzWIiHZspoZvevWl5g=="><img src="/cdn-cgi/image/format=auto,width=1280,quality=85/wren-kitchens-mockup.png" srcset="/cdn-cgi/image/format=auto,width=1280,quality=85/wren-kitchens-mockup.png 1280w, /cdn-cgi/image/format=auto,width=2880,quality=85/wren-kitchens-mockup.png 2880w" sizes="(min-width: 1280px) 1216px, 85vw" alt="Wren Kitchens â Accelerated Time-On-Task By 58%" width="2880" height="1620" loading="lazy" decoding="async"></span> 381 </div> 382 </next-project-card> 383 </labelled-section> 384 </section> 385 386 <!-- ===== NEXT STEPS CTA ===== --> 387 <section class="case-cta case-narrow" aria-label="Get in touch"> 388 <labelled-section label="Next steps"> 389 <next-steps-cta></next-steps-cta> 390 </labelled-section> 391 </section> 392 393 <!-- ===== FOOTER ===== --> 394 <footer class="case-footer case-narrow"> 395 <p class="copyright">© 2026 John Miller</p> 396 </footer> 397 398 </main><!-- /.case-content --> 399 400</div><!-- /.case-page --> 401 402<!-- Shader initialization -->
403<script type="module"> 404 import { 405 ShaderMount, 406 brandColor, 407 cardBg, 408 buildShaderColors, 409 siteFragmentShader, 410 createShaderUniforms, 411 SHADER_MAX_PIXELS, 412 getCardScale, 413 getHeroScale, 414 getHeroSpeed, 415 onBreakpointChange, 416 shaderFrameOffset, 417 averageColor, 418 createFadeColorSampler, 419 createShaderVisibilityGate, 420 } from '../shader-base.js?v=1'; 421 422 // Brand stops use `brandColor()` so light-mode pastels are auto-darkened 423 // in dark mode. `cardBg()` is white in light, near-black in dark. Wrapped 424 // in builder functions so the matchMedia change listener below can 425 // re-evaluate brandColor/cardBg with the fresh isDark() result. 426 const buildHeroColors = () => buildShaderColors({ brand: [brandColor(0.896, 0.064, 350.7)], bg: cardBg() }); 427 const buildNextColors = () => buildShaderColors({ brand: [brandColor(0.891, 0.051, 162.3)], transition: [brandColor(0.966, 0.007, 160.1)], bg: cardBg() }); 428 429 // Tint the next-card's bottom fade with its own shader palette, mirroring the 430 // homepage cards. Re-run on theme change so it tracks light/dark. 431 const applyNextFade = () => 432 document.getElementById('next-card-shader')?.closest('.next-card') 433 ?.style.setProperty('--card-fade-color', averageColor(buildNextColors())); 434 435 const prefersReducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches; 436 const mounts = []; 437 let fadeSampler = null; // live --card-fade-color sampler for the next-card 438 439 // Lightbox shader-pause integration ('site:pause-shaders' on open, 440 // 'site:resume-shaders' on destroy, from photoswipe-init.js) is handled 441 // inside each mount's visibility gate (createShaderVisibilityGate), which 442 // also pauses a shader whenever it's scrolled off-screen â see 443 // shader-base.js. Resuming through the gate re-checks visibility, so a 444 // lightbox closed mid-article doesn't restart an off-screen shader. 445 const speedFor = () => (prefersReducedMotion ? 0 : getHeroSpeed()); 446 447 async function initShaders() { 448 // Yield frames between compiles so the entrance animation's 449 // main-thread paint (filter blur) isn't blocked by GL compilation. 450 const yieldFrames = () => 451 new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); 452 453 await yieldFrames(); 454 455 let heroMount = null; 456 let nextMount = null; 457 let heroGate = null; 458 let nextGate = null; 459 460 // Hero shader â container opacity fade is handled in subpage.css 461 const heroEl = document.getElementById('case-shader-gradient'); 462 if (heroEl) { 463 const heroColors = buildHeroColors(); 464 try { 465 heroMount = new ShaderMount(heroEl, siteFragmentShader, { 466 ...createShaderUniforms(), 467 u_scale: getHeroScale(), 468 u_colors: heroColors, 469 u_colorsCount: heroColors.length, 470 }, undefined, prefersReducedMotion ? 0 : getHeroSpeed(), shaderFrameOffset('gradient-pikl-dashboard')); 471 heroMount.setMaxPixelCount(SHADER_MAX_PIXELS); 472 heroGate = createShaderVisibilityGate(heroEl, heroMount, speedFor); 473 mounts.push({ mount: heroMount, build: buildHeroColors }); 474 await yieldFrames(); 475 } catch (e) { heroMount = null; } 476 } 477 478 // Next-card shader (Wren Kitchens colors from homepage) 479 const nextCardShader = document.getElementById('next-card-shader'); 480 if (nextCardShader) { 481 try { 482 const nextColors = buildNextColors(); 483 nextMount = new ShaderMount(nextCardShader, siteFragmentShader, { 484 ...createShaderUniforms(), 485 u_colors: nextColors, 486 u_colorsCount: nextColors.length, 487 }, undefined, prefersReducedMotion ? 0 : getHeroSpeed(), shaderFrameOffset('gradient-wren')); 488 nextMount.setMaxPixelCount(SHADER_MAX_PIXELS); 489 nextGate = createShaderVisibilityGate(nextCardShader, nextMount, speedFor); 490 mounts.push({ mount: nextMount, build: buildNextColors }); 491 applyNextFade(); 492 const nextCard = nextCardShader.closest('.next-card'); 493 if (nextCard && !prefersReducedMotion) { 494 fadeSampler = createFadeColorSampler(nextMount, nextCard, { getSpeed: getHeroSpeed, isVisible: () => nextGate.visible }); 495 fadeSampler.start(); 496 } 497 } catch (e) { nextMount = null; } 498 } 499 500 onBreakpointChange(() => { 501 if (heroMount) { 502 heroMount.setUniforms({ u_scale: getHeroScale() }); 503 heroGate.apply(); 504 } 505 if (nextMount) { 506 nextMount.setUniforms({ u_scale: getCardScale() }); 507 nextGate.apply(); 508 } 509 }); 510 } 511 512 // React to OS theme changes without requiring a page refresh â rebuilds 513 // each mount's u_colors via its colour builder so brandColor/cardBg are 514 // re-evaluated with the new isDark() result. 515 matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { 516 for (const entry of mounts) { 517 const colors = entry.build(); 518 entry.mount.setUniforms({ u_colors: colors, u_colorsCount: colors.length }); 519 } 520 applyNextFade(); 521 fadeSampler?.snap(); 522 }); 523 524 initShaders(); 525</script>
525 526 527</body> 528</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.