PageSourceSearch

https://portfolio.gateaux-09suntan.workers.dev/pikl-dashboard/

html portfolio.gateaux-09suntan.workers.dev collected 2026-10-03 08:09:17 UTC 52,198 bytes, 528 lines download raw bytes

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 &pound;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 &middot; UX Design &middot; 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 &middot; 1 Head of Design &middot; 1 Head of Operations &middot; 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">&copy; 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.