{"id":11193,"date":"2025-07-09T02:16:02","date_gmt":"2025-07-08T23:16:02","guid":{"rendered":"https:\/\/minifigure-collection.org\/resources-4\/"},"modified":"2026-10-31T17:20:37","modified_gmt":"2026-10-31T14:20:37","slug":"mobile-app","status":"publish","type":"page","link":"https:\/\/minifigure-collection.org\/en\/mobile-app\/","title":{"rendered":"Studio"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"11193\" class=\"elementor elementor-11193\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-buhero1 elementor-section-full_width elementor-section-height-default elementor-section-height-default\" data-id=\"buhero1\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-no\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-buhero2\" data-id=\"buhero2\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-element elementor-element-buhero3 elementor-widget elementor-widget-html\" data-id=\"buhero3\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<style>\n.bu-interior{--ink:var(--p1);font-family:Montserrat,Arial,sans-serif;color:#171717;background:#fff}.bu-interior,.bu-interior *{box-sizing:border-box}.bu-hero{width:82%;max-width:none;margin:0 auto;display:grid;grid-template-columns:minmax(0,1fr) 360px;align-items:center;gap:clamp(24px,3vw,42px);padding:52px 0 64px;min-height:350px}.bu-hero-copy{min-width:0}.bu-hero h1{font-family:Montserrat,Arial,sans-serif!important;font-weight:900!important;font-size:clamp(38px,3.8vw,56px)!important;line-height:1.16!important;margin:0 0 20px!important;color:#fff!important;-webkit-text-stroke:3px var(--ink);paint-order:stroke fill;text-shadow:1px 1px 0 var(--ink),-1px -1px 0 var(--ink);overflow-wrap:anywhere}.bu-hero-bars{width:80%;height:10px;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin:0 0 24px;gap:0}.bu-hero-bars span:nth-child(1){background:var(--p1)}.bu-hero-bars span:nth-child(2){background:var(--p2)}.bu-hero-bars span:nth-child(3){background:var(--p3)}.bu-hero-bars span:nth-child(4){background:var(--p4)}.bu-hero-art{width:360px;max-width:100%;height:220px;border:2px solid var(--p4);border-radius:18px;background:#f6f6f6}.bu-page-space{min-height:120px}.bu-stud-closing{position:relative;min-height:104px;background:var(--p1);margin-top:24px}.bu-stud-closing:before{content:\"\";position:absolute;left:0;right:0;bottom:100%;height:24px;background:var(--p1);border-radius:0;-webkit-mask-image:url(\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAAyCAYAAACqECmXAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA\/\/8AAAKgBAABAAAA9AEAAAOgBAABAAAAMgAAAAAAAAAaX4ZAAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFQmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTAyLTE3PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUhCbkN0NVJacyZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBR0tUNEtfZV9vJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O0JBR0tUNnRGa29ZJnF1b3Q7fTwvQXR0cmliOkRhdGE+CiAgICAgPEF0dHJpYjpFeHRJZD5iYzIyOTU2OS0yMDk5LTRiMWMtOGQ0Zi03NTFlZDM1ODc3MjE8L0F0dHJpYjpFeHRJZD4KICAgICA8QXR0cmliOkZiSWQ+NTI1MjY1OTE0MTc5NTgwPC9BdHRyaWI6RmJJZD4KICAgICA8QXR0cmliOlRvdWNoVHlwZT4yPC9BdHRyaWI6VG91Y2hUeXBlPgogICAgPC9yZGY6bGk+CiAgIDwvcmRmOlNlcT4KICA8L0F0dHJpYjpBZHM+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmRjPSdodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyc+CiAgPGRjOnRpdGxlPgogICA8cmRmOkFsdD4KICAgIDxyZGY6bGkgeG1sOmxhbmc9J3gtZGVmYXVsdCc+VW50aXRsZWQgZGVzaWduIC0gMTwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzp0aXRsZT4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4KICA8cGRmOkF1dGhvcj5FcmVuIENvc2t1bjwvcGRmOkF1dGhvcj4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6eG1wPSdodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvJz4KICA8eG1wOkNyZWF0b3JUb29sPkNhbnZhIGRvYz1EQUhCbkN0NVJacyB1c2VyPVVBR0tUNEtfZV9vIGJyYW5kPUJBR0tUNnRGa29ZPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic\/PtKkNWgAAAbWSURBVHic7ZzNr11jFIevuqVNVKXfEaSVfmkrEf8BIukEiTARUx0ghgbiLzCSiEF9jMpAxEBjgDBgIkLSqlaLjqTaMiFBFb3nZ713v6t73ZMr2vTe867e\/TzJL9sddq31rmefffYxNQUAAAAAAAAAAAAAAAAAAACpkXQNWfy07vPVSOueDSWt+3w10rpnQ0nrPl9VULTJQZ0vHeZyclDry4d6TQZm8zLwQtn1Oss6yxbLjtFodAe58pRa1pqus7+vH687zE+Yy2WWVZZNltuZzQWby52WbZZbLWt8Nlme\/0+sD3tz0ebT9+baUuPxusMY4XHGtOU2y2OW\/ZZPraBf2fWo5Ri5opQaHi41tbxqedyy2QcU5scPrs3hjfbf91iet7xt+cxyJEFfl0LKbH5p+dDyUplNq\/ct4jHnJaFO5JvrmX6tnvHDCfq6FOJ78xN1TipuKo6abt33tNRDWz75PGh513LaDvQ5u84IFppRre1py3uWRyyrxdL8T6w2Gy3P1JvLXy1\/NeveEsbqe8Euv1tOWV633K3uJp\/ZnAd1e7Oc3XKGy1k+Y\/mzlLJJA5c2M2FvFkcVVxVnMZuO+keZpTD7LCfqofYCXqh\/z5Arz1g9fYF+b3nSsjr2ZOiE2bzZ8oLV6kd1i\/JiHZnNhZ\/NWtNS5z8sH1vutyxvPQ+ZCLNZZP605SR7c\/Fns\/7te7O46gm7roo9GSxhKMsd+AOWb+tA\/lMTizoiC5JYU69zoUi93OXz\/dDUnNm8yfKc5Ww4yMzlIs9mWKBlPj+y3NV6JrKgue8ZPWo56bPJfC7+bGru3jyhzl3TsTeDJAxmedngoB\/gcIfugYXlYm3HlsD7lm2xN0NF3aPMay171d3siLmcCPPN5m+WFy3rvDet56Ml6vdmeYnwA9UbIM0VOCw8c2ZTvasOWrbE3gwO9UO5wrLPCvNTKBRLc\/EZH86yDH62PGVZGXs0NNTPZnlb+BV130nO94kHFofx2Sx8Z7lXA39BTv1srlR3VsuZnWFvTozx2RxVd5Wvi1fEHg0K9YO5wfKmupeMxpcmLC7jj5NKD8rb25tij4aG+tncbYf1UK2VfwXEXE6G8aX5i12ftdwQezQ01M9m+dnkO5a\/xd6cNON787zN5xt23RB7NCjUD+ZWyxezVQovHcBE8eEslJ9n7Ig9GhrqPgWW35vfZzP5Q60LMm+Df\/osb76\/bFnjPWo9Jy1Qvze3qzurszUSs9mCmfAE6XPL1tijQeH\/6Pqj\/WNeIDGYLYhC\/8ayJ\/ZoaKj\/\/yE8pO4nKhI3mq3wpVk+BR2w63rvUes5aYF6oe8Se7M1cW+WXuyKPRoUQuiZiIN53HJn7NGQUL8wl6t7g\/hsrQtCb0P8OugtDfmx5tTcr4PU3Xx7jdibk2dc6LtjjwaFEHom4mCWn2EgdISeBZ9NF\/rG2KehIYSeifEnmwgdoacAoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO6oH8ydlqO1KAxmG+JgHrfsiT0aGuXfbZm2m82H7Xqm1gWht8Fn87z144Bd13uPWs9JC4TQMxH35rHy4TT2aFCoH8xtVohDs9UZjS6IwWzBqNa+cMSyM\/ZoaKgT+jLLXsupWheWZhtml6bN5zm77res9R61npMWaO4Hoa9nC8TebMXFvVkdtj32qAX\/AjNYiqbkFMcOAAAAAElFTkSuQmCC\");mask-image:url(\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAAyCAYAAACqECmXAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA\/\/8AAAKgBAABAAAA9AEAAAOgBAABAAAAMgAAAAAAAAAaX4ZAAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFQmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTAyLTE3PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUhCbkN0NVJacyZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBR0tUNEtfZV9vJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O0JBR0tUNnRGa29ZJnF1b3Q7fTwvQXR0cmliOkRhdGE+CiAgICAgPEF0dHJpYjpFeHRJZD5iYzIyOTU2OS0yMDk5LTRiMWMtOGQ0Zi03NTFlZDM1ODc3MjE8L0F0dHJpYjpFeHRJZD4KICAgICA8QXR0cmliOkZiSWQ+NTI1MjY1OTE0MTc5NTgwPC9BdHRyaWI6RmJJZD4KICAgICA8QXR0cmliOlRvdWNoVHlwZT4yPC9BdHRyaWI6VG91Y2hUeXBlPgogICAgPC9yZGY6bGk+CiAgIDwvcmRmOlNlcT4KICA8L0F0dHJpYjpBZHM+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmRjPSdodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyc+CiAgPGRjOnRpdGxlPgogICA8cmRmOkFsdD4KICAgIDxyZGY6bGkgeG1sOmxhbmc9J3gtZGVmYXVsdCc+VW50aXRsZWQgZGVzaWduIC0gMTwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzp0aXRsZT4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4KICA8cGRmOkF1dGhvcj5FcmVuIENvc2t1bjwvcGRmOkF1dGhvcj4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6eG1wPSdodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvJz4KICA8eG1wOkNyZWF0b3JUb29sPkNhbnZhIGRvYz1EQUhCbkN0NVJacyB1c2VyPVVBR0tUNEtfZV9vIGJyYW5kPUJBR0tUNnRGa29ZPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic\/PtKkNWgAAAbWSURBVHic7ZzNr11jFIevuqVNVKXfEaSVfmkrEf8BIukEiTARUx0ghgbiLzCSiEF9jMpAxEBjgDBgIkLSqlaLjqTaMiFBFb3nZ713v6t73ZMr2vTe867e\/TzJL9sddq31rmefffYxNQUAAAAAAAAAAAAAAAAAAACpkXQNWfy07vPVSOueDSWt+3w10rpnQ0nrPl9VULTJQZ0vHeZyclDry4d6TQZm8zLwQtn1Oss6yxbLjtFodAe58pRa1pqus7+vH687zE+Yy2WWVZZNltuZzQWby52WbZZbLWt8Nlme\/0+sD3tz0ebT9+baUuPxusMY4XHGtOU2y2OW\/ZZPraBf2fWo5Ri5opQaHi41tbxqedyy2QcU5scPrs3hjfbf91iet7xt+cxyJEFfl0LKbH5p+dDyUplNq\/ct4jHnJaFO5JvrmX6tnvHDCfq6FOJ78xN1TipuKo6abt33tNRDWz75PGh513LaDvQ5u84IFppRre1py3uWRyyrxdL8T6w2Gy3P1JvLXy1\/NeveEsbqe8Euv1tOWV633K3uJp\/ZnAd1e7Oc3XKGy1k+Y\/mzlLJJA5c2M2FvFkcVVxVnMZuO+keZpTD7LCfqofYCXqh\/z5Arz1g9fYF+b3nSsjr2ZOiE2bzZ8oLV6kd1i\/JiHZnNhZ\/NWtNS5z8sH1vutyxvPQ+ZCLNZZP605SR7c\/Fns\/7te7O46gm7roo9GSxhKMsd+AOWb+tA\/lMTizoiC5JYU69zoUi93OXz\/dDUnNm8yfKc5Ww4yMzlIs9mWKBlPj+y3NV6JrKgue8ZPWo56bPJfC7+bGru3jyhzl3TsTeDJAxmedngoB\/gcIfugYXlYm3HlsD7lm2xN0NF3aPMay171d3siLmcCPPN5m+WFy3rvDet56Ml6vdmeYnwA9UbIM0VOCw8c2ZTvasOWrbE3gwO9UO5wrLPCvNTKBRLc\/EZH86yDH62PGVZGXs0NNTPZnlb+BV130nO94kHFofx2Sx8Z7lXA39BTv1srlR3VsuZnWFvTozx2RxVd5Wvi1fEHg0K9YO5wfKmupeMxpcmLC7jj5NKD8rb25tij4aG+tncbYf1UK2VfwXEXE6G8aX5i12ftdwQezQ01M9m+dnkO5a\/xd6cNON787zN5xt23RB7NCjUD+ZWyxezVQovHcBE8eEslJ9n7Ig9GhrqPgWW35vfZzP5Q60LMm+Df\/osb76\/bFnjPWo9Jy1Qvze3qzurszUSs9mCmfAE6XPL1tijQeH\/6Pqj\/WNeIDGYLYhC\/8ayJ\/ZoaKj\/\/yE8pO4nKhI3mq3wpVk+BR2w63rvUes5aYF6oe8Se7M1cW+WXuyKPRoUQuiZiIN53HJn7NGQUL8wl6t7g\/hsrQtCb0P8OugtDfmx5tTcr4PU3Xx7jdibk2dc6LtjjwaFEHom4mCWn2EgdISeBZ9NF\/rG2KehIYSeifEnmwgdoacAoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO6oH8ydlqO1KAxmG+JgHrfsiT0aGuXfbZm2m82H7Xqm1gWht8Fn87z144Bd13uPWs9JC4TQMxH35rHy4TT2aFCoH8xtVohDs9UZjS6IwWzBqNa+cMSyM\/ZoaKgT+jLLXsupWheWZhtml6bN5zm77res9R61npMWaO4Hoa9nC8TebMXFvVkdtj32qAX\/AjNYiqbkFMcOAAAAAElFTkSuQmCC\");-webkit-mask-size:auto 24px;mask-size:auto 24px;-webkit-mask-position:center bottom;mask-position:center bottom;-webkit-mask-repeat:repeat-x;mask-repeat:repeat-x}@media(max-width:992px){.bu-hero{grid-template-columns:minmax(0,1fr) 300px;gap:28px}.bu-hero-art{width:300px}}@media(max-width:768px){.bu-hero{width:92%;grid-template-columns:minmax(0,1fr);gap:28px;padding:38px 0 48px;min-height:0}.bu-hero h1{font-size:38px!important;-webkit-text-stroke-width:2px}.bu-hero-bars{width:100%}.bu-hero-art{width:100%;height:auto;aspect-ratio:18\/11}.bu-page-space{min-height:64px}.bu-stud-closing{min-height:80px}}\n<\/style><div class=\"bu-interior\" data-project=\"design-bricks\" style=\"--p1:#237841;--p2:#E52828;--p3:#0055BF;--p4:#858585;\"><section class=\"bu-hero\" aria-labelledby=\"bu-page-title\"><div class=\"bu-hero-copy\"><h1 id=\"bu-page-title\">Studio<\/h1><div class=\"bu-hero-bars\" aria-hidden=\"true\"><span><\/span><span><\/span><span><\/span><span><\/span><\/div><\/div><div class=\"bu-hero-art\" role=\"img\" aria-label=\"Illustration placeholder\"><\/div><\/section><!-- DESIGN-BRICKS NATIVE STUDIO START -->\n<style id=\"dbstudio-page-focus\">body.page-id-11193 .bu-hero{display:none!important}<\/style>\n<style id=\"dbstudio-native-style\">#dbstudio{--ms-green:#237841;--ms-red:#E52828;--ms-blue:#0055BF;--ms-gray:#858585;--ms-ink:#18231d;--ms-line:#dfe5e0;display:block;font-family:Arial,Helvetica,sans-serif;color:var(--ms-ink);background:#fff;width:100%;max-width:1500px;margin:0 auto;padding:24px 22px 18px;font-size:13px;line-height:1.5;isolation:isolate}#dbstudio,#dbstudio *{box-sizing:border-box}#dbstudio [hidden]{display:none!important}#dbstudio h1,#dbstudio h2,#dbstudio h3,#dbstudio h4,#dbstudio p{font-family:inherit;color:inherit;padding:0;text-transform:none;text-shadow:none;-webkit-text-stroke:0;letter-spacing:normal;margin:0;line-height:1.4}#dbstudio button,#dbstudio .ms-button,#dbstudio a[download]{appearance:none;box-shadow:none;font-family:inherit;font-size:11px;line-height:1.4;font-weight:700;text-transform:none;letter-spacing:0;text-decoration:none;min-height:34px;border:1px solid var(--ms-line);border-radius:6px;padding:8px 11px;background:#fff;color:var(--ms-ink);display:inline-flex;justify-content:center;align-items:center;gap:7px;cursor:pointer;vertical-align:middle;margin:0;transition:background .15s,border-color .15s}#dbstudio button:hover,#dbstudio .ms-button:hover,#dbstudio a[download]:hover{background:#eef5ef;border-color:#a3bfaa;color:var(--ms-ink)}#dbstudio button:disabled{opacity:.4;cursor:default}#dbstudio button[aria-pressed=true],#dbstudio .ms-view-tabs button[aria-pressed=true]{background:#eaf4ed;border-color:var(--ms-green);color:#1b6333}#dbstudio .ms-primary,#dbstudio a[download]{background:var(--ms-green);border-color:var(--ms-green);color:#fff}#dbstudio .ms-primary:hover,#dbstudio a[download]:hover{background:#175e30;border-color:#175e30;color:#fff}#dbstudio button:focus-visible,#dbstudio label.ms-button:focus-within,#dbstudio input:focus-visible,#dbstudio select:focus-visible,#dbstudio a:focus-visible,#dbstudio canvas:focus-visible,#dbstudio summary:focus-visible{outline:3px solid #0055bf;outline-offset:3px}#dbstudio svg{width:17px;height:17px;flex-shrink:0;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}#dbstudio input,#dbstudio select{font-family:inherit;font-size:11px;color:var(--ms-ink);background:#fff;box-shadow:none;border-radius:5px;line-height:1.4;text-transform:none;letter-spacing:normal;margin:0;min-width:0;max-width:100%}#dbstudio input[type=number],#dbstudio input[type=search],#dbstudio select{padding:8px 9px;border:1px solid #cfd9d1;min-height:34px;width:100%}#dbstudio input[type=range]{appearance:auto;accent-color:var(--ms-green);border:0;box-shadow:none;background:transparent;padding:0;height:15px;min-height:0;width:100%}#dbstudio input[type=checkbox]{appearance:auto;accent-color:var(--ms-green);padding:0;width:14px;height:14px;min-height:0;flex-shrink:0;display:inline-block;border:1px solid #a5b5a8;border-radius:3px;vertical-align:middle}#dbstudio .ms-head{display:flex;align-items:center;justify-content:space-between;gap:20px;margin:0 0 18px}#dbstudio .ms-kicker{font-size:9px;font-weight:800;letter-spacing:1.9px;color:var(--ms-green);margin-bottom:7px}#dbstudio .ms-head h1{font-size:36px;font-weight:800;letter-spacing:-1.7px;line-height:1.1;margin:0 0 7px}#dbstudio .ms-head h1>span{color:var(--ms-red)}#dbstudio .ms-subtitle{font-size:12px;color:#67736a}#dbstudio .ms-file-actions{display:flex;flex-wrap:wrap;gap:7px;justify-content:flex-end;align-items:center}#dbstudio .ms-file-actions>button:not(.ms-primary){padding:8px 10px}#dbstudio .ms-file-actions label{position:relative;overflow:hidden}#dbstudio .ms-file{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;overflow:hidden}#dbstudio .ms-sr{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip-path:inset(50%)!important;white-space:nowrap!important}#dbstudio .ms-statusbar{display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:9px;color:#6b766e;padding:10px 0 14px;border-bottom:1px solid var(--ms-line);margin-bottom:16px}#dbstudio #ms-state{display:inline-flex;gap:6px;align-items:center;color:var(--ms-green);font-weight:700;min-width:130px}#dbstudio #ms-state i{display:block;width:6px;height:6px;background:var(--ms-green);border-radius:50%}#dbstudio .ms-statusbar>button{padding:3px 0;border:0;min-height:24px;font-size:9px;background:transparent}#dbstudio .ms-statusbar svg{width:13px;height:13px}#dbstudio .ms-layout{display:grid;grid-template-columns:228px minmax(0,1fr) 218px;gap:14px;align-items:start}#dbstudio .ms-controls{border:1px solid var(--ms-line);border-radius:9px;min-width:0;background:#fff;overflow:hidden}#dbstudio .ms-controls>summary{display:none}#dbstudio .ms-settings-scroll{max-height:790px;overflow:auto;scrollbar-width:thin;scrollbar-color:#bac8be transparent}#dbstudio .ms-control-block{padding:17px 14px;border-bottom:1px solid var(--ms-line)}#dbstudio .ms-control-block:last-child{border:0}#dbstudio .ms-control-block h3{font-size:11px;font-weight:800;display:flex;align-items:center;gap:8px;margin:0 0 15px}#dbstudio .ms-control-block h3 b{display:grid;place-items:center;background:var(--ms-green);color:white;width:23px;height:23px;border-radius:5px;font-size:9px;letter-spacing:.5px}#dbstudio .ms-control-block:nth-child(2) h3 b{background:var(--ms-red)}#dbstudio .ms-control-block:nth-child(3) h3 b{background:var(--ms-blue)}#dbstudio .ms-source-preview{padding:0;display:block;width:100%;position:relative;overflow:hidden;background:#eef3ef}#dbstudio .ms-source-preview canvas{display:block;width:100%;height:95px;object-fit:cover}#dbstudio .ms-source-preview>span{display:flex;gap:5px;align-items:center;justify-content:center;padding:7px;font-size:10px;border-top:1px solid var(--ms-line);background:#fff}#dbstudio .ms-source-preview svg{width:12px;height:12px}#dbstudio .ms-shapes{display:flex;gap:3px;margin:12px 0}#dbstudio .ms-shapes button{flex:1;padding:5px 3px;min-height:29px;font-size:9px}#dbstudio .ms-field-row{display:flex;gap:6px;align-items:flex-end}#dbstudio .ms-field-row label{font-size:9px;font-weight:700;flex:1;min-width:0}#dbstudio .ms-field-row input{margin-top:4px}#dbstudio .ms-field-row>span{font-size:10px;padding-bottom:9px;color:#718176}#dbstudio .ms-field-row>span:last-child{font-size:8px}#dbstudio .ms-sizes{display:flex;gap:4px;margin:9px 0 13px}#dbstudio .ms-sizes button{flex:1;min-width:0;font-size:9px;min-height:27px;padding:4px 0}#dbstudio .ms-field{display:block;font-size:10px;font-weight:700;margin:12px 0 0}#dbstudio .ms-field>select{margin-top:5px}#dbstudio .ms-muted{font-size:9px;line-height:1.7;color:#718075;margin-top:10px}#dbstudio .ms-filter-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px;margin-bottom:18px}#dbstudio .ms-filter-grid button{font-size:9px;min-height:31px;padding:6px 2px}#dbstudio .ms-filter-grid button:nth-child(2){background:#f4dfcd}#dbstudio .ms-filter-grid button:nth-child(3){background:#f8e8d7}#dbstudio .ms-filter-grid button:nth-child(4){background:#e2edf5}#dbstudio .ms-filter-grid button:nth-child(5){background:#e3e5e4}#dbstudio .ms-filter-grid button:nth-child(6){background:#f4dfdf}#dbstudio .ms-filter-grid button:nth-child(7){background:#efefea}#dbstudio .ms-filter-grid button:nth-child(8){background:#e4e0ef}#dbstudio .ms-filter-grid button[aria-pressed=true]{border-color:var(--ms-green);box-shadow:inset 0 0 0 1px var(--ms-green);color:var(--ms-green)}#dbstudio .ms-range{display:block;font-size:10px;font-weight:700;margin:14px 0 0}#dbstudio .ms-range output{float:right;color:#6c7e70;font-weight:400;font-size:10px}#dbstudio .ms-range input{display:block;margin:7px 0 0}#dbstudio .ms-advanced{margin-top:15px}#dbstudio .ms-advanced summary{font-size:10px;color:#566b5d;cursor:pointer}#dbstudio .ms-text-button{padding:9px 0;min-height:0;border:0;color:var(--ms-green);background:none;font-size:9px;margin-top:10px}#dbstudio .ms-check{display:flex;align-items:center;gap:7px;font-size:10px;font-weight:400;margin:13px 0 0;cursor:pointer;line-height:1.5}#dbstudio .ms-import-button{margin-top:12px;min-height:31px;padding:7px 6px;font-size:9px;width:100%;position:relative;overflow:hidden}#dbstudio .ms-workspace{min-width:0;border:1px solid var(--ms-line);border-radius:9px;overflow:hidden;background:#fff}#dbstudio .ms-canvas-top{display:flex;justify-content:space-between;align-items:center;gap:15px;padding:16px 16px 14px}#dbstudio .ms-mini-label{font-size:8px;font-weight:700;letter-spacing:1.5px;color:#7d8b81}#dbstudio #ms-title{font-size:15px;line-height:1.4;font-weight:700;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:3px;border-bottom:1px dashed transparent;min-width:70px}#dbstudio #ms-title:focus{outline:0;border-color:#708e79}#dbstudio .ms-view-tabs{display:flex;gap:2px;background:#f1f4f2;border-radius:5px;padding:3px}#dbstudio .ms-view-tabs button{border:0;min-height:27px;font-size:9px;padding:6px 8px;background:transparent}#dbstudio .ms-view-tabs button[aria-pressed=true]{background:white;box-shadow:0 1px 4px #0001;color:var(--ms-green)}#dbstudio .ms-editor-row{display:flex;border-top:1px solid var(--ms-line);border-bottom:1px solid var(--ms-line)}#dbstudio .ms-tools{display:flex;flex-direction:column;padding:8px 5px;background:#fafcfb;border-right:1px solid var(--ms-line);width:43px;flex-shrink:0;gap:5px}#dbstudio .ms-tools button{border:1px solid transparent;border-radius:5px;padding:5px;min-height:31px;width:31px;background:transparent}#dbstudio .ms-tools button[aria-pressed=true]{background:#e5f1e8;border-color:#bfd6c5;color:var(--ms-green)}#dbstudio .ms-tools>span{height:1px;background:var(--ms-line);margin:4px 1px}#dbstudio .ms-tools svg{width:17px;height:17px}#dbstudio .ms-stage{position:relative;overflow:hidden;min-width:0;flex:1;height:560px;background-color:#f1f4f1;background-image:radial-gradient(#cad4cd .65px,transparent .65px);background-size:16px 16px;touch-action:none}#dbstudio #ms-canvas,#dbstudio #ms-3d{display:block;width:100%;height:100%;touch-action:none;max-width:none;max-height:none;margin:0;cursor:crosshair}#dbstudio #ms-3d{cursor:grab}#dbstudio .ms-stage-hint{position:absolute;bottom:13px;left:12px;pointer-events:none;color:#6a7b6e;font-size:8px;background:#ffffffcc;border-radius:4px;padding:4px 6px}#dbstudio .ms-zoom{display:flex;align-items:center;position:absolute;right:12px;bottom:12px;border:1px solid #d1dad4;border-radius:5px;overflow:hidden;box-shadow:0 2px 10px #1126190b;background:#fff}#dbstudio .ms-zoom button{border:0;border-radius:0;min-height:28px;padding:4px 8px;min-width:28px;font-size:12px}#dbstudio .ms-zoom button:last-child{font-size:9px;border-left:1px solid var(--ms-line)}#dbstudio .ms-zoom output{font-size:9px;min-width:32px;text-align:center}#dbstudio .ms-tool-options{display:flex;flex-wrap:wrap;align-items:center;gap:12px;padding:10px 12px;min-height:42px;font-size:9px;border-bottom:1px solid var(--ms-line)}#dbstudio #ms-brush-colour{display:flex;align-items:center;gap:5px;font-size:9px;min-width:58px}#dbstudio #ms-brush-colour i{display:block;width:14px;height:14px;border-radius:3px;border:1px solid #0002}#dbstudio .ms-tool-options label{display:flex;align-items:center;gap:6px;font-size:9px;margin:0}#dbstudio .ms-tool-options label input[type=range]{width:43px}#dbstudio .ms-tool-options .ms-check{font-size:9px}#dbstudio .ms-tool-options button{font-size:8px;min-height:22px;padding:4px 6px}#dbstudio .ms-coordinate{margin-left:auto;color:#6b7c70;font-size:8px}#dbstudio .ms-stat-row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;padding:17px 14px 14px}#dbstudio .ms-stat-row>div{border-right:1px solid var(--ms-line);padding-left:9px;min-width:0}#dbstudio .ms-stat-row>div:first-child{padding:0}#dbstudio .ms-stat-row>div:last-child{border:0}#dbstudio .ms-stat-row strong{display:block;font-size:18px;font-weight:800;letter-spacing:-.6px;white-space:nowrap}#dbstudio .ms-stat-row span{font-size:8px;color:#748478;display:block;margin-top:3px}#dbstudio .ms-message{font-size:10px;color:#657a6b;margin:0 14px 14px;padding:10px 11px;background:#f0f6f1;border-radius:5px;line-height:1.6}#dbstudio .ms-message.ms-error{background:#fff0ef;color:#a62222;border-left:3px solid var(--ms-red)}#dbstudio .ms-palette-panel{border:1px solid var(--ms-line);border-radius:9px;overflow:hidden;min-width:0}#dbstudio .ms-panel-tabs{display:flex;border-bottom:1px solid var(--ms-line);gap:18px;padding:0 13px}#dbstudio .ms-panel-tabs button{border:0;border-radius:0;border-bottom:2px solid transparent;background:transparent;padding:13px 0 11px;font-size:11px}#dbstudio .ms-panel-tabs button[aria-selected=true]{color:var(--ms-green);border-bottom-color:var(--ms-green)}#dbstudio #ms-count-badge{font-size:8px;border-radius:3px;background:#eef2ef;padding:2px 4px}#dbstudio .ms-panel-head{padding:12px}#dbstudio .ms-panel-head input{font-size:10px;min-height:30px;padding:7px 8px}#dbstudio .ms-palette-presets{display:flex;gap:5px;margin-top:9px}#dbstudio .ms-palette-presets button{flex:1;font-size:8px;min-height:24px;padding:4px}#dbstudio #ms-palette-view>.ms-muted{margin:0 12px 10px;font-size:8px}#dbstudio .ms-swatches{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:0 10px 10px;max-height:550px;overflow:auto;scrollbar-width:thin}#dbstudio .ms-swatch{min-width:0;border:1px solid #e0e6e1;border-radius:5px;overflow:hidden;position:relative;background:#fff}#dbstudio .ms-swatch.ms-active{border-color:var(--ms-green);box-shadow:0 0 0 1px var(--ms-green)}#dbstudio .ms-swatch.ms-off{opacity:.4}#dbstudio .ms-swatch>button{display:block;border:0;border-radius:0;min-height:0;width:100%;padding:4px;background:#fff;text-align:left}#dbstudio .ms-swatch .ms-chip{height:27px;display:block;border:1px solid #0001;border-radius:3px}#dbstudio .ms-swatch .ms-colour-name{font-size:9px;line-height:1.2;min-height:23px;display:block;margin-top:4px;overflow-wrap:anywhere;font-weight:400}#dbstudio .ms-swatch>label{display:flex;align-items:center;gap:2px;font-size:7px;padding:0 3px 5px;cursor:pointer}#dbstudio .ms-swatch input[type=checkbox]{width:10px;height:10px}#dbstudio .ms-swatch em{position:absolute;top:6px;right:5px;font-style:normal;font-size:7px;background:#ffffffd9;color:#202a23;padding:1px 3px;border-radius:3px;pointer-events:none}#dbstudio .ms-palette-foot{font-size:8px;color:#7a897e;border-top:1px solid var(--ms-line);padding:10px 12px;margin:0;line-height:1.7}#dbstudio .ms-table-scroll{max-height:485px;overflow:auto;scrollbar-width:thin}#dbstudio table{width:100%;border:0;border-collapse:collapse;table-layout:auto;font-family:inherit;font-size:9px;margin:0;text-align:left}#dbstudio th{font-size:8px;font-weight:700;background:#f2f6f3;position:sticky;top:0;z-index:1}#dbstudio th,#dbstudio td{padding:8px 6px;border:0;border-bottom:1px solid #e8ede9;text-align:left;vertical-align:middle;line-height:1.3}#dbstudio td input{max-width:54px;padding:5px!important;min-height:26px!important;font-size:9px!important}#dbstudio .ms-table-colour{display:flex;align-items:center;gap:4px;min-width:70px;font-size:8px}#dbstudio .ms-table-colour>i{display:block;width:13px;height:13px;border-radius:3px;flex-shrink:0;border:1px solid #0002}#dbstudio .ms-missing{color:#b82121;font-weight:700}#dbstudio .ms-stock-summary{font-size:9px;background:#f0f6f2;color:var(--ms-green);padding:9px 10px}#dbstudio .ms-stock-actions{display:flex;gap:8px;padding:7px 10px}#dbstudio .ms-stock-actions button{font-size:8px;padding:5px;border:0;min-height:20px}#dbstudio .ms-foot{display:flex;justify-content:space-between;align-items:center;gap:15px;font-size:9px;color:#79887e;margin-top:18px;padding-top:13px;border-top:1px solid var(--ms-line)}#dbstudio .ms-foot button{border:0;padding:4px 0;min-height:0;font-size:9px;background:none;color:var(--ms-green)}#dbstudio .ms-busy{position:absolute;inset:0;z-index:3;display:flex;gap:9px;align-items:center;justify-content:center;background:#f4f8f2c7;font-size:11px;font-weight:700;color:var(--ms-green);pointer-events:none}#dbstudio .ms-busy>span{width:17px;height:17px;border:2px solid #23784133;border-top-color:var(--ms-green);border-radius:50%;animation:ms-spin .8s linear infinite}#dbstudio #ms-drop-overlay{position:absolute;inset:10px;z-index:4;border:2px dashed var(--ms-green);background:#f0faf0ee;display:grid;place-items:center;font-size:20px;font-weight:700;color:var(--ms-green);border-radius:10px;pointer-events:none}#dbstudio .ms-dialog{position:fixed;inset:0;margin:auto;padding:25px;border:1px solid #d3ded6;border-radius:12px;background:#fff;color:var(--ms-ink);box-shadow:0 15px 90px #102b1c40;width:min(900px,calc(100vw - 32px));max-width:900px;height:fit-content;max-height:calc(100dvh - 48px);overflow:auto;z-index:2147483647;font:13px\/1.6 Arial,Helvetica,sans-serif}#dbstudio .ms-dialog::backdrop{background:#102d20aa;backdrop-filter:blur(4px)}#dbstudio .ms-dialog-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:20px;margin-bottom:22px}#dbstudio .ms-dialog-heading h3{font-size:25px;font-weight:800;letter-spacing:-.8px}#dbstudio .ms-dialog-heading>button{border:0;background:#edf4ef;width:31px;height:31px;min-height:31px;padding:7px;border-radius:50%;flex-shrink:0}#dbstudio .ms-crop-layout{display:grid;grid-template-columns:minmax(0,1fr) 220px;gap:25px;align-items:center}#dbstudio #ms-crop-canvas{width:100%;height:auto;display:block;background:#eef3ee;border:1px solid var(--ms-line);border-radius:7px;touch-action:none;cursor:move}#dbstudio .ms-crop-actions{display:flex;flex-wrap:wrap;gap:7px;margin:20px 0}#dbstudio .ms-crop-actions button{font-size:10px;min-height:31px;padding:7px}#dbstudio .ms-wide{width:100%}#dbstudio .ms-export-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}#dbstudio .ms-export-grid>div{display:flex;align-items:flex-start;flex-direction:column;gap:9px;border:1px solid var(--ms-line);padding:17px;border-radius:8px;background:#fff;min-height:190px}#dbstudio .ms-export-grid>div>svg{width:22px;height:22px;color:var(--ms-green)}#dbstudio .ms-export-grid strong{font-size:12px;font-weight:700}#dbstudio .ms-export-grid p{font-size:10px;line-height:1.7;color:#738276;margin-bottom:auto}#dbstudio .ms-export-grid a,#dbstudio .ms-export-grid button{font-size:9px;min-height:31px;padding:7px 9px;margin-top:5px;width:100%}#dbstudio .ms-export-summary{font-size:11px;color:var(--ms-green);padding:12px;background:#f0f6f2;border-radius:5px;margin-bottom:16px}#dbstudio #ms-export-message{font-size:11px;color:var(--ms-green);margin-top:10px}#dbstudio .ms-build-nav{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:17px}#dbstudio .ms-build-layout{display:grid;grid-template-columns:minmax(0,1fr) 200px;gap:24px}#dbstudio #ms-build-canvas{display:block;width:100%;height:auto;border:1px solid var(--ms-line);border-radius:6px}#dbstudio .ms-section-map{display:grid;gap:3px;margin-bottom:15px}#dbstudio .ms-section-map button{font-size:9px;min-height:27px;padding:3px}#dbstudio .ms-section-map button.ms-done{background:#d7efdf}#dbstudio #ms-section-key{font-size:10px;max-height:340px;overflow:auto;margin:12px 0}#dbstudio #ms-section-key>div{display:flex;gap:8px;align-items:center;padding:4px 0}#dbstudio #ms-section-key i{display:inline-block;height:17px;width:17px;border:1px solid #0002;border-radius:3px}#dbstudio #ms-section-key b{font-size:9px}#dbstudio #ms-section-coordinates{font-size:11px;line-height:1.8}#dbstudio .ms-about{max-width:650px}#dbstudio .ms-about p{font-size:12px;line-height:1.9;margin:12px 0}#dbstudio .ms-about h4{font-size:14px;font-weight:700;margin-top:20px}#dbstudio .ms-toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%);max-width:min(90vw,550px);padding:12px 18px;border-radius:7px;background:#163e24;color:white;box-shadow:0 5px 30px #0002;font-size:12px;z-index:2147483646}#dbstudio.ms-expanded{position:fixed;inset:0;max-width:none;width:100vw;height:100dvh;overflow:auto;margin:0;padding:20px;background:#fff;z-index:2147483000}#dbstudio.ms-expanded .ms-stage{height:calc(100dvh - 290px);min-height:400px}#dbstudio.ms-expanded .ms-settings-scroll{max-height:calc(100dvh - 180px)}@keyframes ms-spin{to{transform:rotate(360deg)}}\n@media(min-width:1550px){#dbstudio .ms-stage{height:650px}#dbstudio .ms-settings-scroll{max-height:880px}#dbstudio .ms-swatches{max-height:650px}}@media(max-width:1160px){#dbstudio .ms-layout{grid-template-columns:225px minmax(0,1fr)}#dbstudio .ms-palette-panel{grid-column:1\/-1}#dbstudio .ms-swatches{grid-template-columns:repeat(auto-fill,minmax(70px,1fr));max-height:230px}#dbstudio .ms-swatch .ms-colour-name{font-size:9px;min-height:23px}#dbstudio .ms-swatch>label{font-size:9px}#dbstudio .ms-swatch .ms-chip{height:29px}#dbstudio .ms-palette-panel .ms-table-scroll{max-height:300px}#dbstudio .ms-table-colour{font-size:10px}#dbstudio .ms-stage{height:550px}#dbstudio .ms-file-actions{max-width:520px}#dbstudio .ms-head h1{font-size:32px}#dbstudio .ms-canvas-top{flex-wrap:wrap;gap:8px}#dbstudio .ms-view-tabs{margin-left:auto}}@media(max-width:760px){#dbstudio{padding:20px 12px 14px}#dbstudio .ms-head{display:block;margin-bottom:12px}#dbstudio .ms-head h1{font-size:34px}#dbstudio .ms-file-actions{margin-top:17px;max-width:none;justify-content:flex-start;gap:6px}#dbstudio .ms-file-actions>button,#dbstudio .ms-file-actions>.ms-button{font-size:10px;padding:8px;min-height:33px}#dbstudio .ms-file-actions svg{width:15px;height:15px}#dbstudio .ms-statusbar{font-size:8px;flex-wrap:wrap;gap:5px}#dbstudio .ms-statusbar>span:nth-child(2){display:none}#dbstudio .ms-layout{grid-template-columns:1fr;gap:12px}#dbstudio .ms-controls>summary{display:flex;align-items:center;gap:8px;padding:12px;cursor:pointer;list-style:none;font-size:11px;font-weight:700}#dbstudio .ms-controls>summary::-webkit-details-marker{display:none}#dbstudio .ms-controls>summary>span{margin-left:auto}#dbstudio .ms-controls[open]>summary>span{font-size:0}#dbstudio .ms-controls[open]>summary>span:before{content:'\u2212';font-size:13px}#dbstudio .ms-settings-scroll{max-height:570px;display:grid;grid-template-columns:1fr 1fr}#dbstudio .ms-control-block{padding:14px 12px}#dbstudio .ms-control-block:first-child{border-right:1px solid var(--ms-line)}#dbstudio .ms-control-block:last-child{grid-column:1\/-1}#dbstudio .ms-palette-panel{grid-column:auto}#dbstudio .ms-stage{height:430px}#dbstudio .ms-tools{width:36px;padding:6px 3px;gap:4px}#dbstudio .ms-tools button{width:28px;min-height:28px;padding:5px}#dbstudio .ms-tools svg{width:16px;height:16px}#dbstudio .ms-stage-hint{left:7px;bottom:48px;font-size:7px;padding:3px 5px}#dbstudio .ms-zoom{bottom:10px;right:8px}#dbstudio .ms-canvas-top{padding:12px;gap:8px}#dbstudio #ms-title{font-size:13px;max-width:150px}#dbstudio .ms-view-tabs button{font-size:8px;padding:5px 7px;min-height:25px}#dbstudio .ms-tool-options{padding:9px;gap:9px;font-size:8px}#dbstudio .ms-coordinate{display:none}#dbstudio .ms-stat-row{padding:14px 10px;gap:4px}#dbstudio .ms-stat-row strong{font-size:15px}#dbstudio .ms-stat-row span{font-size:7px}#dbstudio .ms-stat-row>div{padding-left:7px}#dbstudio .ms-message{font-size:9px;margin:0 10px 10px}#dbstudio .ms-swatches{grid-template-columns:repeat(auto-fill,minmax(60px,1fr));max-height:280px}#dbstudio .ms-swatch .ms-colour-name{font-size:8px;min-height:20px}#dbstudio .ms-swatch>label{font-size:8px}#dbstudio .ms-dialog{padding:18px;width:calc(100vw - 24px);max-height:calc(100dvh - 24px);border-radius:10px}#dbstudio .ms-dialog-heading h3{font-size:22px}#dbstudio .ms-crop-layout{grid-template-columns:1fr;gap:15px}#dbstudio #ms-crop-canvas{max-height:45dvh;width:100%;object-fit:contain}#dbstudio .ms-export-grid{grid-template-columns:1fr 1fr;gap:8px}#dbstudio .ms-export-grid>div{padding:12px;min-height:190px}#dbstudio .ms-export-grid strong{font-size:11px}#dbstudio .ms-export-grid p{font-size:9px}#dbstudio .ms-build-layout{grid-template-columns:1fr;gap:14px}#dbstudio .ms-build-layout>div{display:grid;grid-template-columns:1fr 1fr;gap:10px}#dbstudio .ms-build-layout #ms-section-key{grid-column:2;grid-row:1\/4;max-height:240px;margin:0}#dbstudio .ms-build-layout .ms-check{margin-top:0}#dbstudio .ms-foot{font-size:8px}#dbstudio .ms-foot button{font-size:8px;text-align:right}#dbstudio.ms-expanded{padding:12px}#dbstudio.ms-expanded .ms-stage{height:calc(100dvh - 370px);min-height:350px}}@media(max-width:370px){#dbstudio .ms-stat-row strong{font-size:12px}#dbstudio .ms-stat-row span{font-size:6px}#dbstudio .ms-settings-scroll{display:block}#dbstudio .ms-control-block:first-child{border-right:0}#dbstudio .ms-view-tabs{width:100%;justify-content:flex-end}#dbstudio .ms-file-actions{gap:4px}#dbstudio .ms-file-actions>button,#dbstudio .ms-file-actions>.ms-button{padding:7px 6px;font-size:9px}#dbstudio .ms-tool-options{gap:7px}#dbstudio .ms-export-grid{grid-template-columns:1fr}#dbstudio .ms-export-grid>div{min-height:155px}}\n<\/style><!-- DESIGN-BRICKS MOSAIC START -->\n<section id=\"dbstudio\" lang=\"en\" translate=\"no\" aria-label=\"Design-Bricks Mosaic Studio\">\n <header class=\"ms-head\"><div><p class=\"ms-kicker\">DESIGN-BRICKS \u00b7 MAKE IT YOURS<\/p><h1>Mosaic Studio<span aria-hidden=\"true\">.<\/span><\/h1><p class=\"ms-subtitle\">From your first pixel to your next build.<\/p><\/div><div class=\"ms-file-actions\"><label class=\"ms-button ms-primary\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M12 16V3m-5 5 5-5 5 5M4 15v5h16v-5\"\/><\/svg> Upload photo<input id=\"ms-photo\" class=\"ms-file\" type=\"file\" accept=\"image\/jpeg,image\/png,image\/webp\" aria-label=\"Upload photo\"><\/label><button type=\"button\" data-action=\"sample\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 3h18v18H3zM3 17l5-5 4 4 3-3 6 6M8 8h.01\"\/><\/svg> Sample<\/button><button type=\"button\" data-action=\"blank\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 4h16v16H4z\"\/><\/svg> Blank<\/button><label class=\"ms-button ms-icon-button\" title=\"Open a saved project\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 6h7l2 3h9v11H3z\"\/><\/svg><span class=\"ms-sr\">Open project<\/span><input id=\"ms-project-file\" class=\"ms-file\" type=\"file\" accept=\".json,application\/json\" aria-label=\"Open project\"><\/label><button type=\"button\" data-action=\"save\" title=\"Save editable project\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 3h13l4 4v14H3V3h1m3 0v7h10V3M7 21v-7h10v7\"\/><\/svg><span class=\"ms-sr\">Save project<\/span><\/button><button type=\"button\" data-action=\"build\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m3 10 9-5 9 5-9 5-9-5m0 0v8l9 5 9-5v-8m-9 5v8M7 7l9 5\"\/><\/svg> Build<\/button><button type=\"button\" data-action=\"export\" class=\"ms-primary\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5\"\/><\/svg> Export<\/button><\/div><\/header>\n <div class=\"ms-statusbar\"><span id=\"ms-state\"><i><\/i> Ready to create<\/span><span>Private by design \u00b7 your photos stay on your device<\/span><button type=\"button\" data-action=\"fullscreen\" title=\"Expand the workspace\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5\"\/><\/svg> Expand<\/button><\/div>\n <div class=\"ms-layout\">\n  <details class=\"ms-controls\" open><summary><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 6h18M3 12h18M3 18h18M7 3v6m10 0v6M9 15v6\"\/><\/svg> Image & design settings <span>+<\/span><\/summary><div class=\"ms-settings-scroll\">\n   <section class=\"ms-control-block\"><h3><b>01<\/b> Image & canvas<\/h3><button type=\"button\" data-action=\"crop\" class=\"ms-source-preview\" aria-label=\"Crop and position image\"><canvas id=\"ms-thumbnail\" width=\"360\" height=\"150\"><\/canvas><span><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M7 3v14h14M3 7h14v14\"\/><\/svg> Crop & position<\/span><\/button><div class=\"ms-shapes\" role=\"group\" aria-label=\"Mosaic shape\"><button type=\"button\" data-shape=\"square\" aria-pressed=\"true\">Square<\/button><button type=\"button\" data-shape=\"portrait\">Portrait<\/button><button type=\"button\" data-shape=\"landscape\">Landscape<\/button><\/div><div class=\"ms-field-row\"><label>Width<input id=\"ms-width\" type=\"number\" min=\"8\" max=\"128\" value=\"48\" step=\"1\"><\/label><span>\u00d7<\/span><label>Height<input id=\"ms-height\" type=\"number\" min=\"8\" max=\"128\" value=\"48\" step=\"1\"><\/label><span>studs<\/span><\/div><div class=\"ms-sizes\" aria-label=\"Grid size\"><button type=\"button\" data-size=\"16\">16<\/button><button type=\"button\" data-size=\"32\">32<\/button><button type=\"button\" data-size=\"48\" aria-pressed=\"true\">48<\/button><button type=\"button\" data-size=\"64\">64<\/button><button type=\"button\" data-size=\"96\">96<\/button><button type=\"button\" data-size=\"128\">128<\/button><\/div><label class=\"ms-field\">Pixel piece<select id=\"ms-part\"><\/select><\/label><p id=\"ms-part-note\" class=\"ms-muted\"><\/p><\/section>\n   <section class=\"ms-control-block\"><h3><b>02<\/b> Make it your style<\/h3><div id=\"ms-filters\" class=\"ms-filter-grid\"><button type=\"button\" data-filter=\"natural\" aria-pressed=\"true\">Original<\/button><button type=\"button\" data-filter=\"vivid\">Vivid<\/button><button type=\"button\" data-filter=\"warm\">Warm<\/button><button type=\"button\" data-filter=\"cool\">Cool<\/button><button type=\"button\" data-filter=\"mono\">Mono<\/button><button type=\"button\" data-filter=\"poster\">Poster<\/button><button type=\"button\" data-filter=\"sketch\">Sketch<\/button><button type=\"button\" data-filter=\"invert\">Invert<\/button><\/div><label class=\"ms-range\">Brightness <output id=\"ms-brightness-value\">0<\/output><input id=\"ms-brightness\" type=\"range\" min=\"-100\" max=\"100\" value=\"0\"><\/label><label class=\"ms-range\">Contrast <output id=\"ms-contrast-value\">0<\/output><input id=\"ms-contrast\" type=\"range\" min=\"-100\" max=\"100\" value=\"0\"><\/label><label class=\"ms-range\">Saturation <output id=\"ms-saturation-value\">0<\/output><input id=\"ms-saturation\" type=\"range\" min=\"-100\" max=\"100\" value=\"0\"><\/label><details class=\"ms-advanced\"><summary>Fine adjustments<\/summary><label class=\"ms-range\">Hue <output id=\"ms-hue-value\">0\u00b0<\/output><input id=\"ms-hue\" type=\"range\" min=\"-180\" max=\"180\" value=\"0\"><\/label><label class=\"ms-range\">Gamma <output id=\"ms-gamma-value\">1.0<\/output><input id=\"ms-gamma\" type=\"range\" min=\"0.4\" max=\"2.4\" step=\"0.1\" value=\"1\"><\/label><label class=\"ms-range\">Sharpness <output id=\"ms-sharpen-value\">0<\/output><input id=\"ms-sharpen\" type=\"range\" min=\"0\" max=\"4\" step=\"0.2\" value=\"0\"><\/label><\/details><button type=\"button\" data-action=\"reset-adjust\" class=\"ms-text-button\">Reset adjustments<\/button><button type=\"button\" data-action=\"reset-edits\" class=\"ms-text-button\">Reset manual edits<\/button><\/section>\n   <section class=\"ms-control-block\"><h3><b>03<\/b> Colour & detail<\/h3><label class=\"ms-range\">Photo colours <output id=\"ms-limit-value\">16<\/output><input id=\"ms-limit\" type=\"range\" min=\"2\" max=\"58\" value=\"16\"><\/label><label class=\"ms-field\">Image sampling<select id=\"ms-sampling\"><option value=\"area\">Area average \u00b7 photos<\/option><option value=\"nearest\">Nearest pixel \u00b7 pixel art<\/option><\/select><\/label><label class=\"ms-field\">Dithering<select id=\"ms-dither\"><option value=\"none\">None \u00b7 clean shapes<\/option><option value=\"floyd\">Floyd\u2013Steinberg \u00b7 smooth tones<\/option><option value=\"ordered\">Ordered \u00b7 patterned texture<\/option><\/select><\/label><label class=\"ms-check\"><input id=\"ms-stock-mode\" type=\"checkbox\"> Use only my available pieces<\/label><p id=\"ms-stock-note\" class=\"ms-muted\">Enter quantities in the Pieces panel or import your stock.<\/p><label class=\"ms-button ms-import-button\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 6h7l2 3h9v11H3z\"\/><\/svg> Import stock CSV \/ XML<input id=\"ms-stock-file\" class=\"ms-file\" type=\"file\" accept=\".csv,.xml,text\/csv,application\/xml\" aria-label=\"Import stock\"><\/label><\/section>\n  <\/div><\/details>\n  <div class=\"ms-workspace\">\n   <div class=\"ms-canvas-top\"><div><p class=\"ms-mini-label\">YOUR WORKSPACE<\/p><h3 id=\"ms-title\" contenteditable=\"true\" role=\"textbox\" aria-label=\"Project title\" spellcheck=\"false\">Sunset study<\/h3><\/div><div class=\"ms-view-tabs\" role=\"group\" aria-label=\"Preview\"><button type=\"button\" data-view=\"mosaic\" aria-pressed=\"true\">Mosaic<\/button><button type=\"button\" data-view=\"original\">Original<\/button><button type=\"button\" data-view=\"split\">Compare<\/button><button type=\"button\" data-view=\"3d\">3D<\/button><\/div><\/div>\n   <div class=\"ms-editor-row\"><div class=\"ms-tools\" role=\"toolbar\" aria-label=\"Drawing tools\"><button type=\"button\" data-tool=\"pan\" title=\"Move \u00b7 H\" aria-label=\"Move canvas\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M8 13V5a2 2 0 0 1 4 0v6-7a2 2 0 0 1 4 0v7-5a2 2 0 0 1 4 0v10c0 4-3 6-7 6-4 0-6-3-9-7a2 2 0 0 1 3-3l1 1\"\/><\/svg><\/button><button type=\"button\" data-tool=\"paint\" aria-pressed=\"true\" title=\"Brush \u00b7 B\" aria-label=\"Brush\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m14 3 7 7-10 10H4v-7L14 3m-8 9 6 6\"\/><\/svg><\/button><button type=\"button\" data-tool=\"fill\" title=\"Fill connected area \u00b7 F\" aria-label=\"Fill area\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m4 10 8-8 9 9-8 8-9-9m0 0h17M5 2l7 8m8 6c0 0-3 3-3 5a3 3 0 0 0 6 0c0-2-3-5-3-5\"\/><\/svg><\/button><button type=\"button\" data-tool=\"pick\" title=\"Pick colour \u00b7 I\" aria-label=\"Pick colour\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m15 3 6 6m-4-6 4 4-4 4-4-4 4-4M13 7 3 17v4h4L17 11\"\/><\/svg><\/button><button type=\"button\" data-tool=\"replace\" title=\"Replace this colour everywhere \u00b7 R\" aria-label=\"Replace colour\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 7h17m-5-5 5 5-5 5M21 17H4m5-5-5 5 5 5\"\/><\/svg><\/button><span><\/span><button type=\"button\" data-tool=\"select\" title=\"Rectangle selection \u00b7 S\" aria-label=\"Select rectangle\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 7V3h4m10 0h4v4m0 10v4h-4M7 21H3v-4M3 11v2m18-2v2M11 3h2m-2 18h2\"\/><\/svg><\/button><button type=\"button\" data-tool=\"smart\" title=\"Select a connected colour region \u00b7 W\" aria-label=\"Smart select\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m12 3 2.5 6.5L21 12l-6.5 2.5L12 21l-2.5-6.5L3 12l6.5-2.5L12 3\"\/><\/svg><\/button><button type=\"button\" data-tool=\"erase\" title=\"Erase to white \u00b7 E\" aria-label=\"Erase\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m15 3 6 6-12 12H4l-3-3L15 3m-9 10 6 6m-1 2h11\"\/><\/svg><\/button><span><\/span><button type=\"button\" data-action=\"undo\" id=\"ms-undo\" title=\"Undo \u00b7 Ctrl\/\u2318 Z\" aria-label=\"Undo\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 5 3 11l6 6M3 11h11a7 7 0 0 1 7 7\"\/><\/svg><\/button><button type=\"button\" data-action=\"redo\" id=\"ms-redo\" title=\"Redo \u00b7 Ctrl\/\u2318 Shift Z\" aria-label=\"Redo\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m15 5 6 6-6 6m6-6H10a7 7 0 0 0-7 7\"\/><\/svg><\/button><\/div>\n    <div class=\"ms-stage\" id=\"ms-stage\"><canvas id=\"ms-canvas\" tabindex=\"0\" aria-label=\"Editable mosaic. Arrow keys move the cursor; Space paints a piece.\"><\/canvas><canvas id=\"ms-3d\" hidden aria-label=\"3D LEGO mosaic. Drag to orbit and scroll to zoom.\"><\/canvas><div id=\"ms-busy\" class=\"ms-busy\" hidden><span><\/span>Updating your mosaic<\/div><div id=\"ms-drop-overlay\" hidden>Drop your photo here<\/div><div class=\"ms-stage-hint\" id=\"ms-stage-hint\">Scroll to zoom \u00b7 H to move \u00b7 B to paint<\/div><div class=\"ms-zoom\"><button type=\"button\" data-action=\"zoom-out\" aria-label=\"Zoom out\">\u2212<\/button><output id=\"ms-zoom-value\">Fit<\/output><button type=\"button\" data-action=\"zoom-in\" aria-label=\"Zoom in\">+<\/button><button type=\"button\" data-action=\"fit\">Fit<\/button><\/div><\/div>\n   <\/div>\n   <div class=\"ms-tool-options\"><span id=\"ms-brush-colour\"><i><\/i><b>Red<\/b><\/span><label>Brush<input id=\"ms-brush\" type=\"range\" min=\"1\" max=\"9\" step=\"2\" value=\"1\"><output id=\"ms-brush-value\">1<\/output><\/label><label id=\"ms-tolerance-wrap\" hidden>Tolerance<input id=\"ms-tolerance\" type=\"range\" min=\"0\" max=\"50\" value=\"12\"><output id=\"ms-tolerance-value\">12<\/output><\/label><label class=\"ms-check\"><input id=\"ms-grid\" type=\"checkbox\">Grid<\/label><button type=\"button\" data-action=\"selection-fill\" id=\"ms-selection-fill\" hidden>Fill selection<\/button><button type=\"button\" data-action=\"selection-clear\" id=\"ms-selection-clear\" hidden>Clear<\/button><span id=\"ms-coordinate\" class=\"ms-coordinate\">Ready<\/span><\/div>\n   <div class=\"ms-stat-row\"><div><strong id=\"ms-stat-grid\">48 \u00d7 48<\/strong><span>studs<\/span><\/div><div><strong id=\"ms-stat-pieces\">2,304<\/strong><span>pieces<\/span><\/div><div><strong id=\"ms-stat-colours\">\u2014<\/strong><span>colours<\/span><\/div><div><strong id=\"ms-stat-size\">38.4 \u00d7 38.4<\/strong><span>cm \u00b7 stud grid<\/span><\/div><\/div><p id=\"ms-message\" class=\"ms-message\" role=\"status\" aria-live=\"polite\">Choose a photo, start blank or explore the original sample.<\/p>\n  <\/div>\n  <aside class=\"ms-palette-panel\" aria-label=\"Colours and pieces\"><div class=\"ms-panel-tabs\" role=\"tablist\"><button type=\"button\" data-panel=\"palette\" role=\"tab\" aria-selected=\"true\">Palette<\/button><button type=\"button\" data-panel=\"pieces\" role=\"tab\" aria-selected=\"false\">Pieces <span id=\"ms-count-badge\"><\/span><\/button><\/div><div class=\"ms-panel-head\"><input id=\"ms-colour-search\" type=\"search\" placeholder=\"Find a colour or ID\" aria-label=\"Find a colour\"><div class=\"ms-palette-presets\"><button type=\"button\" data-palette=\"all\">All<\/button><button type=\"button\" data-palette=\"used\">Used<\/button><button type=\"button\" data-palette=\"mono\">Mono<\/button><button type=\"button\" data-palette=\"earth\">Earth<\/button><\/div><\/div><div id=\"ms-palette-view\"><p class=\"ms-muted\">Choose a brush colour. Use the tick to include or exclude it from conversion.<\/p><div id=\"ms-palette\" class=\"ms-swatches\"><\/div><\/div><div id=\"ms-pieces-view\" hidden><div id=\"ms-stock-summary\" class=\"ms-stock-summary\"><\/div><div class=\"ms-stock-actions\"><button type=\"button\" data-action=\"stock-from-design\">Use these quantities<\/button><button type=\"button\" data-action=\"clear-stock\">Clear stock<\/button><\/div><div class=\"ms-table-scroll\"><table><thead><tr><th>Colour<\/th><th>Need<\/th><th>Have<\/th><\/tr><\/thead><tbody id=\"ms-pieces-body\"><\/tbody><\/table><\/div><\/div><p class=\"ms-palette-foot\" id=\"ms-palette-foot\"><\/p><\/aside>\n <\/div>\n <footer class=\"ms-foot\"><span>Created for Design-Bricks. Built around real LEGO\u00ae pieces.<\/span><button type=\"button\" data-action=\"about\">About this studio & shortcuts<\/button><\/footer>\n <dialog id=\"ms-crop-dialog\" class=\"ms-dialog\" aria-labelledby=\"ms-crop-title\"><div class=\"ms-dialog-heading\"><div><p class=\"ms-kicker\">FRAME YOUR IDEA<\/p><h3 id=\"ms-crop-title\">Crop & position<\/h3><\/div><button type=\"button\" data-close aria-label=\"Close crop\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m6 6 12 12M18 6 6 18\"\/><\/svg><\/button><\/div><div class=\"ms-crop-layout\"><div><canvas id=\"ms-crop-canvas\" width=\"600\" height=\"480\" aria-label=\"Drag the image to adjust its position\"><\/canvas><p class=\"ms-muted\">Drag the image to position it inside your mosaic.<\/p><\/div><div><label class=\"ms-field\">Placement<select id=\"ms-placement\"><option value=\"cover\">Fill mosaic \u00b7 crop edges<\/option><option value=\"contain\">Fit entire photo \u00b7 white border<\/option><\/select><\/label><label class=\"ms-range\">Crop zoom <output id=\"ms-cropzoom-value\">100%<\/output><input id=\"ms-cropzoom\" type=\"range\" min=\"1\" max=\"3\" step=\"0.05\" value=\"1\"><\/label><label class=\"ms-range\">Horizontal <output id=\"ms-cropx-value\">0<\/output><input id=\"ms-cropx\" type=\"range\" min=\"-100\" max=\"100\" value=\"0\"><\/label><label class=\"ms-range\">Vertical <output id=\"ms-cropy-value\">0<\/output><input id=\"ms-cropy\" type=\"range\" min=\"-100\" max=\"100\" value=\"0\"><\/label><div class=\"ms-crop-actions\"><button type=\"button\" data-action=\"rotate\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 10a8 8 0 1 1 1 8M4 3v7h7\"\/><\/svg>Rotate 90\u00b0<\/button><button type=\"button\" data-action=\"flip\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 7h17m-5-5 5 5-5 5M21 17H4m5-5-5 5 5 5\"\/><\/svg>Mirror<\/button><button type=\"button\" data-action=\"reset-crop\">Reset crop<\/button><\/div><button type=\"button\" data-close class=\"ms-primary ms-wide\">Done<\/button><\/div><\/div><\/dialog>\n <dialog id=\"ms-export-dialog\" class=\"ms-dialog\" aria-labelledby=\"ms-export-title\"><div class=\"ms-dialog-heading\"><div><p class=\"ms-kicker\">READY FOR YOUR BUILD TABLE<\/p><h3 id=\"ms-export-title\">Take your mosaic with you<\/h3><\/div><button type=\"button\" data-close aria-label=\"Close exports\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m6 6 12 12M18 6 6 18\"\/><\/svg><\/button><\/div><div id=\"ms-export-summary\" class=\"ms-export-summary\"><\/div><div class=\"ms-export-grid\"><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M5 2h10l5 5v15H5V2m10 0v6h5M8 13h8M8 17h6\"\/><\/svg><strong>Building instructions<\/strong><p>Numbered 16 \u00d7 16 sections, overview and colour key.<\/p><button type=\"button\" data-export=\"pdf\">Create PDF<\/button><a id=\"ms-download-pdf\" hidden download>Download PDF \u2193<\/a><\/div><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 3h18v18H3zM3 17l5-5 4 4 3-3 6 6M8 8h.01\"\/><\/svg><strong>Mosaic image<\/strong><p>A high resolution PNG in your chosen piece style.<\/p><button type=\"button\" data-export=\"png\">Create PNG<\/button><a id=\"ms-download-png\" hidden download>Download PNG \u2193<\/a><\/div><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M8 5h13M8 12h13M8 19h13M3 5h.01M3 12h.01M3 19h.01\"\/><\/svg><strong>Parts list<\/strong><p>Piece IDs, BrickLink colours and exact quantities.<\/p><a id=\"ms-download-csv\" download>Download CSV \u2193<\/a><\/div><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M3 8h18v13H3V8m3 0V4h4v4m4 0V4h4v4\"\/><\/svg><strong>BrickLink wanted list<\/strong><p>Import directly into your own BrickLink wanted list.<\/p><a id=\"ms-download-xml\" download>Download XML \u2193<\/a><\/div><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m12 2 10 6v10l-10 6-10-6V8l10-6m0 11v11M2 8l10 5 10-5\"\/><\/svg><strong>BrickLink Studio model<\/strong><p>Real LDraw part references, placed on a stud grid.<\/p><a id=\"ms-download-ldr\" download>Download model \u2193<\/a><\/div><div><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M4 3h13l4 4v14H3V3h1m3 0v7h10V3M7 21v-7h10v7\"\/><\/svg><strong>Editable project<\/strong><p>Keep your mosaic, colours, stock and source photo.<\/p><a id=\"ms-download-json\" download>Download project \u2193<\/a><\/div><\/div><p class=\"ms-muted\">Part lists and model files contain the coloured pixel layer. Choose suitable baseplates and a frame separately.<\/p><p id=\"ms-export-message\" role=\"status\"><\/p><\/dialog>\n <dialog id=\"ms-build-dialog\" class=\"ms-dialog\" aria-labelledby=\"ms-build-title\"><div class=\"ms-dialog-heading\"><div><p class=\"ms-kicker\">ONE SECTION AT A TIME<\/p><h3 id=\"ms-build-title\">Build your mosaic<\/h3><\/div><button type=\"button\" data-close aria-label=\"Close build guide\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m6 6 12 12M18 6 6 18\"\/><\/svg><\/button><\/div><div class=\"ms-build-nav\"><button type=\"button\" data-action=\"section-prev\">\u2190 Previous<\/button><strong id=\"ms-section-title\"><\/strong><button type=\"button\" data-action=\"section-next\">Next \u2192<\/button><\/div><div class=\"ms-build-layout\"><canvas id=\"ms-build-canvas\" width=\"640\" height=\"640\"><\/canvas><div><div id=\"ms-section-map\" class=\"ms-section-map\"><\/div><p id=\"ms-section-coordinates\"><\/p><div id=\"ms-section-key\"><\/div><label class=\"ms-check\"><input id=\"ms-section-done\" type=\"checkbox\">This section is built<\/label><p id=\"ms-build-progress\" class=\"ms-muted\"><\/p><\/div><\/div><\/dialog>\n <dialog id=\"ms-about-dialog\" class=\"ms-dialog ms-about\" aria-labelledby=\"ms-about-title\"><div class=\"ms-dialog-heading\"><h3 id=\"ms-about-title\">Made for your next build<\/h3><button type=\"button\" data-close aria-label=\"Close information\"><svg viewbox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m6 6 12 12M18 6 6 18\"\/><\/svg><\/button><\/div><p>An original Design-Bricks tool. Your photos are processed on your device, without an account or upload to a server.<\/p><h4>Real parts, documented colours<\/h4><p>Round tile 98138, round plate 4073, square tile 3070 and square plate 3024. Colours are limited to opaque, documented combinations in the installed BrickLink Studio catalogue snapshot of 31 October 2026. This is not live shop stock. Screen colours are approximate.<\/p><h4>Editing & projects<\/h4><p>H: move \u00b7 B: brush \u00b7 F: fill \u00b7 I: pick \u00b7 R: replace \u00b7 S: rectangle selection \u00b7 W: smart select \u00b7 E: erase to white. Ctrl\/\u2318 Z: undo. Ctrl\/\u2318 Shift Z: redo. Scroll to zoom. Arrow keys move the keyboard cursor; Space applies your current tool.<\/p><p>Project files are saved on your device and include the source image so you can continue adjusting it later. Nothing is stored remotely. Hand-painted pieces stay in place when you adjust the photo; resizing rebuilds the grid.<\/p><h4>3D geometry<\/h4><p>The 3D preview uses real Studio\/LDraw part geometry. Geometry by the LDraw Parts Library contributors, including Magnus Forsberg and Chris Dee, under the licences recorded with the original part files (CCAL 2.0 and CC BY 4.0). The application code and interface are original Design-Bricks work.<\/p><p class=\"ms-muted\">LEGO\u00ae is a trademark of the LEGO Group, which does not sponsor, authorize or endorse this site.<\/p><\/dialog>\n <div id=\"ms-toast\" class=\"ms-toast\" role=\"status\" hidden><\/div>\n<\/section>\n<!-- DESIGN-BRICKS MOSAIC END --><script data-cfasync=\"false\" data-no-optimize=\"1\">(function(){\n\"use strict\";\n\/* Original Design-Bricks Mosaic Studio engine. *\/\nconst clamp=(x,a,b)=>Math.max(a,Math.min(b,x));\nconst rgb=hex=>hex.match(\/[a-f\\d]{2}\/ig).map(v=>parseInt(v,16));\nfunction lab(c){\n const v=c.map(x=>{x=clamp(x,0,255)\/255;return x>.04045?((x+.055)\/1.055)**2.4:x\/12.92;});\n const f=x=>x>.008856?Math.cbrt(x):7.787*x+16\/116;\n const x=f((v[0]*.4124564+v[1]*.3575761+v[2]*.1804375)\/.95047),y=f(v[0]*.2126729+v[1]*.7151522+v[2]*.072175),z=f((v[0]*.0193339+v[1]*.119192+v[2]*.9503041)\/1.08883);\n return [116*y-16,500*(x-y),200*(y-z)];\n}\nconst delta=(a,b)=>(a[0]-b[0])**2+(a[1]-b[1])**2+(a[2]-b[2])**2;\nfunction adjusted(pixels,w,h,opt={}){\n const {brightness=0,contrast=0,saturation=0,hue=0,gamma=1,sharpen=0,filter='natural'}=opt;\n const out=new Float32Array(w*h*3),cf=259*(contrast+255)\/(255*(259-contrast)),angle=hue*Math.PI\/180,co=Math.cos(angle),si=Math.sin(angle);\n for(let i=0,j=0;i<pixels.length;i+=4,j+=3){\n  const alpha=pixels[i+3]\/255;let [r,g,b]=[0,1,2].map(k=>pixels[i+k]*alpha+255*(1-alpha));\n  if(hue){const y=.299*r+.587*g+.114*b,I=.596*r-.274*g-.322*b,Q=.211*r-.523*g+.312*b,ii=I*co-Q*si,qq=I*si+Q*co;r=y+.956*ii+.621*qq;g=y-.272*ii-.647*qq;b=y-1.106*ii+1.703*qq;}\n  if(filter==='warm'){r+=14;g+=3;b-=16;}if(filter==='cool'){r-=10;g+=3;b+=16;}\n  const l=.2126*r+.7152*g+.0722*b,sat=filter==='mono'?-100:saturation+(filter==='vivid'?25:0);\n  let c=[r,g,b].map(v=>clamp(cf*(l+(v-l)*(1+sat\/100)-128)+128+brightness,0,255));\n  if(filter==='poster')c=c.map(v=>Math.round(v\/64)*64);if(filter==='invert')c=c.map(v=>255-v);\n  for(let k=0;k<3;k++)out[j+k]=255*(clamp(c[k],0,255)\/255)**(1\/Math.max(.1,gamma));\n }\n if(sharpen>0||filter==='sketch'){\n  const copy=out.slice();for(let y=0;y<h;y++)for(let x=0;x<w;x++)for(let k=0;k<3;k++){\n   const i=(y*w+x)*3+k;let avg=0;for(const [dx,dy]of [[0,-1],[-1,0],[1,0],[0,1]])avg+=copy[(clamp(y+dy,0,h-1)*w+clamp(x+dx,0,w-1))*3+k]\/4;\n   out[i]=filter==='sketch'?clamp(255-Math.abs(copy[i]-avg)*8,0,255):clamp(copy[i]+(copy[i]-avg)*sharpen,0,255);\n  }\n }\n return out;\n}\nfunction selectPalette(samples,colors,limit){\n if(limit>=colors.length)return colors;\n \/\/ Greedy weighted error reduction preserves small, distinctive subject colours.\n const histogram=new Map();for(let i=0;i<samples.length;i+=3){const key=(samples[i]>>5)*64+(samples[i+1]>>5)*8+(samples[i+2]>>5),entry=histogram.get(key);if(entry){entry.n++;for(let k=0;k<3;k++)entry.sum[k]+=samples[i+k];}else histogram.set(key,{n:1,sum:[samples[i],samples[i+1],samples[i+2]]});}\n const bins=[...histogram.values()].map(v=>({n:v.n,lab:lab(v.sum.map(x=>x\/v.n))}));\n const dist=colors.map(c=>bins.map(b=>delta(c.lab,b.lab))),best=Array(bins.length).fill(Infinity),picked=[];\n for(let step=0;step<Math.max(1,limit);step++){\n  let chosen=-1,score=Infinity;for(let c=0;c<colors.length;c++){if(picked.includes(c))continue;let error=0;for(let i=0;i<bins.length;i++)error+=Math.min(best[i],dist[c][i])*bins[i].n;if(error<score){score=error;chosen=c;}}\n  picked.push(chosen);for(let i=0;i<bins.length;i++)best[i]=Math.min(best[i],dist[chosen][i]);\n }\n return picked.map(i=>colors[i]);\n}\nfunction quantize(samples,width,height,colors,{limit=colors.length,dither='none',inventory=null}={}){\n if(samples.length!==width*height*3||!colors.length)throw Error('Select at least one colour.');\n let palette=colors.map(c=>({...c,rgb:rgb(c.hex),lab:lab(rgb(c.hex))}));\n if(inventory){palette=palette.filter(c=>(inventory[c.id]||0)>0);const available=palette.reduce((n,c)=>n+inventory[c.id],0);if(available<width*height)throw Error(`Your selected stock has ${available.toLocaleString()} pieces. Add ${(width*height-available).toLocaleString()} more or choose a smaller grid.`);}\n else palette=selectPalette(samples,palette,limit||palette.length);\n const result=new Array(width*height),nearest=(p,list)=>{let ix=0,best=Infinity;for(let i=0;i<list.length;i++){const d=delta(p,list[i].lab);if(d<best){best=d;ix=i;}}return list[ix];};\n if(inventory){\n  const input=Array.from({length:width*height},(_,i)=>lab(Array.from(samples.slice(i*3,i*3+3)))),left={...inventory};\n  const order=input.map((p,i)=>{let a=Infinity,b=Infinity;for(const c of palette){const d=delta(p,c.lab);if(d<a){b=a;a=d;}else b=Math.min(b,d);}return {i,priority:Number.isFinite(b)?b-a:0};}).sort((a,b)=>b.priority-a.priority||a.i-b.i);\n  for(const {i}of order){const c=nearest(input[i],palette.filter(c=>left[c.id]>0));result[i]=c.id;left[c.id]--;}\n }else{\n  const work=new Float32Array(samples),bayer=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];\n  for(let y=0;y<height;y++)for(let step=0;step<width;step++){\n   const x=dither==='floyd'&&y%2?width-1-step:step,i=y*width+x,dir=y%2?-1:1;\n   let p=Array.from(work.slice(i*3,i*3+3));if(dither==='ordered')p=p.map(v=>v+(bayer[(y%4)*4+x%4]-7.5)*3);\n   const c=nearest(lab(p),palette);result[i]=c.id;\n   if(dither==='floyd')for(const [dx,dy,weight]of [[dir,0,7\/16],[-dir,1,3\/16],[0,1,5\/16],[dir,1,1\/16]])if(x+dx>=0&&x+dx<width&&y+dy<height)for(let k=0;k<3;k++)work[((y+dy)*width+x+dx)*3+k]+=(p[k]-c.rgb[k])*weight;\n  }\n }\n return result;\n}\nfunction counts(pixels){const m=new Map();for(const id of pixels)m.set(id,(m.get(id)||0)+1);return [...m].sort((a,b)=>b[1]-a[1]||a[0]-b[0]);}\nfunction convertMosaic(samples,width,height,colors,options={},overrides={}){\n const fixed=Object.entries(overrides).map(([i,id])=>[Number(i),id]).filter(([i,id])=>Number.isInteger(i)&&i>=0&&i<width*height&&Number.isInteger(id));\n if(!fixed.length)return quantize(samples,width,height,colors,options);\n if(!options.inventory){const result=quantize(samples,width,height,colors,options);for(const [i,id]of fixed)result[i]=id;return result;}\n const stock={...options.inventory},result=new Array(width*height),locked=new Set();\n for(const [i,id]of fixed){stock[id]=(stock[id]||0)-1;if(stock[id]<0)throw Error('Your painted pieces exceed this stock. Add pieces or reset manual edits.');result[i]=id;locked.add(i);}\n const indices=[];for(let i=0;i<result.length;i++)if(!locked.has(i))indices.push(i);\n if(indices.length){const remaining=new Float32Array(indices.length*3);indices.forEach((i,j)=>remaining.set(samples.subarray(i*3,i*3+3),j*3));const converted=quantize(remaining,indices.length,1,colors,{...options,inventory:stock,dither:'none'});indices.forEach((i,j)=>result[i]=converted[j]);}\n return result;\n}\nfunction region(pixels,width,start,tolerance,colors){\n const color=new Map(colors.map(c=>[c.id,lab(rgb(c.hex))])),target=color.get(pixels[start]),seen=new Uint8Array(pixels.length),queue=[start];seen[start]=1;\n for(let i=0;i<queue.length;i++){const p=queue[i];for(const n of [p%width?p-1:-1,p%width<width-1?p+1:-1,p-width,p+width])if(n>=0&&n<pixels.length&&!seen[n]&&delta(color.get(pixels[n]),target)<=tolerance*tolerance){seen[n]=1;queue.push(n);}}\n return queue;\n}\nfunction fillRegion(pixels,width,start,id){const out=pixels.slice(),old=out[start],queue=[start];if(old===id)return out;out[start]=id;for(let j=0;j<queue.length;j++){const p=queue[j];for(const n of [p%width?p-1:-1,p%width<width-1?p+1:-1,p-width,p+width])if(n>=0&&n<out.length&&out[n]===old){out[n]=id;queue.push(n);}}return out;}\nfunction validateProject(p,data){\n if(!p||p.format!=='design-bricks-mosaic'||![1,2].includes(p.version))throw Error('Choose a Design-Bricks project file.');\n if(!Number.isInteger(p.width)||!Number.isInteger(p.height)||p.width<8||p.height<8||p.width>128||p.height>128)throw Error('Project size must be 8\u2013128 studs per side.');\n const part=data.parts.find(x=>x.id===p.part);if(!part)throw Error('Unsupported piece type.');\n if(!Array.isArray(p.pixels)||p.pixels.length!==p.width*p.height||p.pixels.some(id=>!Number.isInteger(id)||!part.colors.includes(id)))throw Error('The project contains invalid pieces or colours.');\n const selected=Array.isArray(p.selected)?p.selected.filter(id=>part.colors.includes(id)):part.colors.slice(),inventory={};\n for(const id of part.colors){const q=p.inventory?.[id];if(Number.isInteger(q)&&q>=0&&q<=1000000)inventory[id]=q;}\n const stocks={};for(const part of data.parts){const row={};for(const id of part.colors){const q=p.stocks?.[part.id]?.[id];if(Number.isInteger(q)&&q>=0&&q<=1000000)row[id]=q;}stocks[part.id]=row;}\n const source=typeof p.source==='string'&&p.source.length<12000000&&\/^data:image\\\/(png|jpeg|webp);base64,[a-z0-9+\/=]+$\/i.test(p.source)?p.source:null;\n const overrides={};if(p.overrides&&typeof p.overrides==='object')for(const [key,id]of Object.entries(p.overrides)){const i=Number(key);if(Number.isInteger(i)&&i>=0&&i<p.pixels.length&&part.colors.includes(id))overrides[i]=id;}\n return {...p,version:2,title:typeof p.title==='string'?p.title.slice(0,80):'My mosaic',pixels:p.pixels.slice(),selected:selected.length?selected:part.colors.slice(),inventory,stocks,source,overrides};\n}\nfunction bricklinkXML(p){return '<INVENTORY>\\n'+counts(p.pixels).map(([id,q])=>`  <ITEM><ITEMTYPE>P<\/ITEMTYPE><ITEMID>${p.part}<\/ITEMID><COLOR>${id}<\/COLOR><MINQTY>${q}<\/MINQTY><CONDITION>N<\/CONDITION><\/ITEM>`).join('\\n')+'\\n<\/INVENTORY>\\n';}\nfunction csv(p,data){const map=new Map(data.colors.map(c=>[c.id,c]));return 'part_id,colour_id,colour_name,quantity\\r\\n'+counts(p.pixels).map(([id,q])=>`${p.part},${id},\"${map.get(id).name.replace(\/\"\/g,'\"\"')}\",${q}`).join('\\r\\n')+'\\r\\n';}\nfunction ldraw(p,data){const map=new Map(data.colors.map(c=>[c.id,c])),piece=data.parts.find(x=>x.id===p.part);return `0 Design-Bricks Mosaic Studio\\n0 Name: design-bricks-mosaic.ldr\\n0 Author: Design-Bricks\\n0 \/\/ Pixel layer only. Add suitable baseplates and frame.\\n`+p.pixels.map((id,i)=>`1 ${map.get(id).ldraw} ${i%p.width*20} 0 ${Math.floor(i\/p.width)*20} 1 0 0 0 1 0 0 0 1 ${piece.ldraw}`).join('\\n')+'\\n';}\nfunction parseInventory(text,part,data){\n const lines=text.trim().replace(\/^\\uFEFF\/,'').split(\/\\r?\\n\/),parse=line=>{const row=[];let value='',quote=false;for(let i=0;i<line.length;i++){if(line[i]==='\"'){if(quote&&line[i+1]==='\"'){value+='\"';i++;}else quote=!quote;}else if(line[i]===','&&!quote){row.push(value.trim());value='';}else value+=line[i];}if(quote)throw Error('Unclosed CSV quote.');row.push(value.trim());return row;};\n const head=parse(lines.shift()).map(s=>s.toLowerCase()),ci=head.findIndex(s=>['colour_id','color_id'].includes(s)),qi=head.indexOf('quantity'),pi=head.indexOf('part_id');if(ci<0||qi<0)throw Error('CSV columns must include colour_id and quantity.');\n const out={},allowed=data.parts.find(p=>p.id===part).colors;\n for(const line of lines){if(!line.trim())continue;const cells=parse(line);if(pi>=0&&cells[pi]!==part)continue;const id=Number(cells[ci]),q=Number(cells[qi]);if(!allowed.includes(id)||!Number.isInteger(q)||q<0||q>1000000||cells[qi]==='')throw Error('The stock file contains an unavailable colour or invalid quantity.');out[id]=(out[id]||0)+q;}\n if(!Object.keys(out).length)throw Error('No matching pieces found for the selected part.');return out;\n}\n\n\/* A small original vector PDF writer. No external PDF service or library. *\/\nfunction instructionsPDF(project,data){\n const W=595,H=842,pages=[],byId=new Map(data.colors.map(c=>[c.id,c])),used=counts(project.pixels),codes=new Map(used.map(([id],i)=>[id,i+1]));\n let ops=[];\n const number=v=>Number(v.toFixed(3));\n const ascii=s=>String(s).normalize('NFKD').replace(\/[^\\x20-\\x7E]\/g,'').replace(\/[\\\\()]\/g,'\\\\$&');\n const text=(x,y,size,s,bold=false,color=[.08,.08,.08])=>ops.push(`${color.join(' ')} rg BT \/${bold?'F2':'F1'} ${size} Tf ${number(x)} ${number(H-y)} Td (${ascii(s)}) Tj ET`);\n const rect=(x,y,w,h,color,stroke=false)=>{ops.push(`${color.map(number).join(' ')} ${stroke?'RG':'rg'} ${number(x)} ${number(H-y-h)} ${number(w)} ${number(h)} re ${stroke?'S':'f'}`);};\n const circle=(x,y,r,color)=>{const k=.55228475*r,Y=H-y;ops.push(`${color.map(number).join(' ')} rg ${x+r} ${Y} m ${x+r} ${Y+k} ${x+k} ${Y+r} ${x} ${Y+r} c ${x-k} ${Y+r} ${x-r} ${Y+k} ${x-r} ${Y} c ${x-r} ${Y-k} ${x-k} ${Y-r} ${x} ${Y-r} c ${x+k} ${Y-r} ${x+r} ${Y-k} ${x+r} ${Y} c f`);};\n const header=(title,sub)=>{ops=[];rect(0,0,W,9,[.137,.471,.255]);text(36,40,10,'DESIGN-BRICKS \/ MOSAIC STUDIO',true,[.137,.471,.255]);text(36,73,23,title,true);text(36,98,10,sub);};\n const finish=()=>{text(36,812,8,'design-bricks.org | Tile layer only. Baseplates and frame are not included.');text(530,812,8,String(pages.length+1));pages.push(ops.join('\\n'));};\n const piece=(id,x,y,size,label=false)=>{\n  const c=byId.get(id),color=rgb(c.hex).map(v=>v\/255),part=data.parts.find(p=>p.id===project.part);\n  if(part.shape.startsWith('round'))circle(x+size\/2,y+size\/2,size*.46,color);else rect(x+.4,y+.4,size-.8,size-.8,color);\n  if(label){const light=.2126*color[0]+.7152*color[1]+.0722*color[2]>.5,code=String(codes.get(id));text(x+size*.5-code.length*2,y+size*.63,8,code,true,light?[0,0,0]:[1,1,1]);}\n };\n const cols=Math.ceil(project.width\/16),rows=Math.ceil(project.height\/16);\n header('Your mosaic, piece by piece',`${project.width} x ${project.height} studs | ${project.pixels.length} pieces | ${used.length} colours | Part ${project.part}`);\n const scale=Math.min(515\/project.width,500\/project.height),ox=(W-project.width*scale)\/2,oy=123;\n project.pixels.forEach((id,i)=>piece(id,ox+(i%project.width)*scale,oy+Math.floor(i\/project.width)*scale,scale));\n for(let x=0;x<=project.width;x+=16)rect(ox+x*scale,oy,.6,project.height*scale,[.4,.4,.4]);\n for(let y=0;y<=project.height;y+=16)rect(ox,oy+y*scale,project.width*scale,.6,[.4,.4,.4]);\n let ty=oy+project.height*scale+30;\n text(36,ty,12,'Build orientation',true);ty+=21;\n text(36,ty,10,'Row 1 is the top. Column 1 is the left. Keep this orientation for every section.');ty+=18;\n text(36,ty,10,`The design is split into ${cols*rows} sections of up to 16 x 16 studs.`);ty+=18;\n text(36,ty,10,`Finished tile area: ${(project.width*.8).toFixed(1)} x ${(project.height*.8).toFixed(1)} cm.`);ty+=18;\n text(36,ty,10,'Section grids are assembly guides; they do not specify a baseplate arrangement.');finish();\n for(let start=0;start<used.length;start+=25){\n  header('Colour key & parts list',`Part ${project.part} | Colour names and IDs use the BrickLink catalogue.`);\n  text(36,127,9,'KEY',true);text(105,127,9,'COLOUR \/ BRICKLINK ID',true);text(488,127,9,'PIECES',true);\n  used.slice(start,start+25).forEach(([id,q],i)=>{const y=145+i*24,c=byId.get(id);piece(id,67,y-12,17);text(39,y,10,String(codes.get(id)),true);text(105,y,10,`${c.name} (${id})`);text(492,y,10,String(q),true);});\n  text(36,775,9,'Digital colours are approximate. Confirm the selected part and colour before ordering.');finish();\n }\n for(let sy=0;sy<rows;sy++)for(let sx=0;sx<cols;sx++){\n  const x0=sx*16,y0=sy*16,sw=Math.min(16,project.width-x0),sh=Math.min(16,project.height-y0),size=28,ox=75,oy=150;\n  header(`Section ${sy*cols+sx+1} of ${cols*rows}`,`Rows ${y0+1}-${y0+sh} | Columns ${x0+1}-${x0+sw} | Read from top left.`);\n  for(let x=0;x<sw;x++)text(ox+x*size+8,oy-13,8,String(x0+x+1));\n  for(let y=0;y<sh;y++){text(ox-26,oy+y*size+18,8,String(y0+y+1));for(let x=0;x<sw;x++){rect(ox+x*size,oy+y*size,size,size,[.92,.92,.92]);piece(project.pixels[(y0+y)*project.width+x0+x],ox+x*size,oy+y*size,size,true);}}\n  text(36,oy+sh*size+30,10,'Numbers inside pieces match the colour key.');\n  text(36,oy+sh*size+49,10,`Section position: column ${sx+1} of ${cols}, row ${sy+1} of ${rows}.`);finish();\n }\n const objects=[],add=s=>(objects.push(s),objects.length);\n add('<< \/Type \/Catalog \/Pages 2 0 R >>');add('');add('<< \/Type \/Font \/Subtype \/Type1 \/BaseFont \/Helvetica >>');add('<< \/Type \/Font \/Subtype \/Type1 \/BaseFont \/Helvetica-Bold >>');\n const ids=[];for(const content of pages){const stream=add(`<< \/Length ${content.length} >>\\nstream\\n${content}\\nendstream`),page=add(`<< \/Type \/Page \/Parent 2 0 R \/MediaBox [0 0 ${W} ${H}] \/Resources << \/Font << \/F1 3 0 R \/F2 4 0 R >> >> \/Contents ${stream} 0 R >>`);ids.push(page);}\n objects[1]=`<< \/Type \/Pages \/Count ${ids.length} \/Kids [${ids.map(i=>`${i} 0 R`).join(' ')}] >>`;\n let result='%PDF-1.4\\n% Design-Bricks Mosaic Studio\\n',offsets=[0];objects.forEach((obj,i)=>{offsets.push(result.length);result+=`${i+1} 0 obj\\n${obj}\\nendobj\\n`;});\n const xref=result.length;result+=`xref\\n0 ${objects.length+1}\\n0000000000 65535 f \\n`+offsets.slice(1).map(n=>`${String(n).padStart(10,'0')} 00000 n \\n`).join('');result+=`trailer\\n<< \/Size ${objects.length+1} \/Root 1 0 R >>\\nstartxref\\n${xref}\\n%%EOF\\n`;\n return new Uint8Array(Array.from(result,c=>c.charCodeAt(0)));\n}\n\n\/* Original WebGL2 instanced renderer; vertices come from real LDraw parts. *\/\nfunction create3D(canvas,geometry){\n const gl=canvas.getContext('webgl2',{alpha:false,antialias:true,preserveDrawingBuffer:true});if(!gl)throw Error('This browser does not support WebGL 2. The 2D editor remains available.');\n const compile=(type,source)=>{const s=gl.createShader(type);gl.shaderSource(s,source);gl.compileShader(s);if(!gl.getShaderParameter(s,gl.COMPILE_STATUS))throw Error(gl.getShaderInfoLog(s));return s;};\n const program=gl.createProgram();gl.attachShader(program,compile(gl.VERTEX_SHADER,`#version 300 es\n in vec3 position;in vec3 normal;in vec2 offset;in vec3 colour;uniform vec3 right;uniform vec3 up;uniform vec3 eye;uniform float scale;uniform float aspect;uniform float depth;uniform vec2 center;out vec3 vColor;out vec3 vNormal;\n void main(){vec3 p=position+vec3(offset.x-center.x,0.,offset.y-center.y);gl_Position=vec4(dot(p,right)*scale\/aspect,dot(p,up)*scale,-dot(p,eye)\/depth,1.);vColor=colour;vNormal=normal;}`));\n gl.attachShader(program,compile(gl.FRAGMENT_SHADER,`#version 300 es\n precision highp float;in vec3 vColor;in vec3 vNormal;out vec4 result;void main(){float light=.5+.5*abs(dot(normalize(vNormal),normalize(vec3(-.3,1.,.7))));result=vec4(vColor*light,1.);}`));gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS))throw Error('3D renderer could not start.');\n gl.useProgram(program);gl.enable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);\n const locations={};for(const name of ['position','normal','offset','colour'])locations[name]=gl.getAttribLocation(program,name);\n const uniforms={};for(const name of ['right','up','eye','scale','aspect','depth','center'])uniforms[name]=gl.getUniformLocation(program,name);\n const vao=gl.createVertexArray();gl.bindVertexArray(vao);const vertices=gl.createBuffer(),instances=gl.createBuffer();let vertexCount=0,count=0,part=null;\n const normalize=v=>{const n=Math.hypot(...v)||1;return v.map(x=>x\/n);},cross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]];\n function setPart(id){if(part===id)return;part=id;const mesh=geometry[id],raw=atob(mesh.data),bytes=new Uint8Array(raw.length);for(let i=0;i<raw.length;i++)bytes[i]=raw.charCodeAt(i);const view=new DataView(bytes.buffer),p=[];for(let i=0;i<bytes.length;i+=6)p.push([view.getInt16(i,true)\/mesh.scale,-view.getInt16(i+2,true)\/mesh.scale,view.getInt16(i+4,true)\/mesh.scale]);const out=[];for(let i=0;i<p.length;i+=3){const a=p[i],b=p[i+1],c=p[i+2],n=normalize(cross(b.map((x,k)=>x-a[k]),c.map((x,k)=>x-a[k])));for(const v of [a,b,c])out.push(...v,...n);}vertexCount=p.length;gl.bindBuffer(gl.ARRAY_BUFFER,vertices);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(out),gl.STATIC_DRAW);for(const [name,start]of [['position',0],['normal',12]]){gl.enableVertexAttribArray(locations[name]);gl.vertexAttribPointer(locations[name],3,gl.FLOAT,false,24,start);}}\n function update(p,colors){setPart(p.part);const data=new Float32Array(p.pixels.length*5);p.pixels.forEach((id,i)=>{data[i*5]=i%p.width;data[i*5+1]=Math.floor(i\/p.width);const color=colors.get(id).hex.match(\/[\\da-f]{2}\/ig).map(x=>parseInt(x,16)\/255);data.set(color,i*5+2);});count=p.pixels.length;gl.bindBuffer(gl.ARRAY_BUFFER,instances);gl.bufferData(gl.ARRAY_BUFFER,data,gl.DYNAMIC_DRAW);for(const [name,size,start]of [['offset',2,0],['colour',3,8]]){gl.enableVertexAttribArray(locations[name]);gl.vertexAttribPointer(locations[name],size,gl.FLOAT,false,20,start);gl.vertexAttribDivisor(locations[name],1);}}\n function draw(p,{yaw=-.35,pitch=1.05,zoom=1}={}){const dpr=Math.min(devicePixelRatio||1,2),rect=canvas.getBoundingClientRect();canvas.width=Math.max(1,Math.round(rect.width*dpr));canvas.height=Math.max(1,Math.round(rect.height*dpr));gl.viewport(0,0,canvas.width,canvas.height);gl.clearColor(.94,.95,.94,1);gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);gl.useProgram(program);gl.bindVertexArray(vao);const eye=[Math.sin(yaw)*Math.cos(pitch),Math.sin(pitch),Math.cos(yaw)*Math.cos(pitch)],right=normalize(cross([0,1,0],eye)),up=cross(eye,right);gl.uniform3fv(uniforms.eye,eye);gl.uniform3fv(uniforms.right,right);gl.uniform3fv(uniforms.up,up);gl.uniform2f(uniforms.center,(p.width-1)\/2,(p.height-1)\/2);gl.uniform1f(uniforms.aspect,canvas.width\/canvas.height);gl.uniform1f(uniforms.scale,1.55\/Math.max(p.height,p.width\/(canvas.width\/canvas.height))*zoom);gl.uniform1f(uniforms.depth,Math.max(p.width,p.height)*2+5);gl.drawArraysInstanced(gl.TRIANGLES,0,vertexCount,count);}\n return {update,draw};\n}\n\nconst DATA={\"version\":2,\"snapshot\":\"2026-10-31\",\"colors\":[{\"id\":1,\"ldraw\":15,\"name\":\"White\",\"hex\":\"#FEFEFE\"},{\"id\":2,\"ldraw\":19,\"name\":\"Tan\",\"hex\":\"#E3CC9D\"},{\"id\":3,\"ldraw\":14,\"name\":\"Yellow\",\"hex\":\"#F1CC36\"},{\"id\":4,\"ldraw\":25,\"name\":\"Orange\",\"hex\":\"#FD8917\"},{\"id\":5,\"ldraw\":4,\"name\":\"Red\",\"hex\":\"#C81908\"},{\"id\":6,\"ldraw\":2,\"name\":\"Green\",\"hex\":\"#24793D\"},{\"id\":7,\"ldraw\":1,\"name\":\"Blue\",\"hex\":\"#0054BE\"},{\"id\":8,\"ldraw\":6,\"name\":\"Brown\",\"hex\":\"#573826\"},{\"id\":9,\"ldraw\":7,\"name\":\"Light Gray\",\"hex\":\"#9AA09C\"},{\"id\":10,\"ldraw\":8,\"name\":\"Dark Gray\",\"hex\":\"#6C6D5B\"},{\"id\":11,\"ldraw\":0,\"name\":\"Black\",\"hex\":\"#04121C\"},{\"id\":23,\"ldraw\":13,\"name\":\"Pink\",\"hex\":\"#FB96AB\"},{\"id\":24,\"ldraw\":22,\"name\":\"Purple\",\"hex\":\"#80007A\"},{\"id\":28,\"ldraw\":92,\"name\":\"Nougat\",\"hex\":\"#CF9067\"},{\"id\":31,\"ldraw\":462,\"name\":\"Medium Orange\",\"hex\":\"#FEA60A\"},{\"id\":32,\"ldraw\":125,\"name\":\"Light Orange\",\"hex\":\"#F8B960\"},{\"id\":33,\"ldraw\":18,\"name\":\"Light Yellow\",\"hex\":\"#FAE595\"},{\"id\":34,\"ldraw\":27,\"name\":\"Lime\",\"hex\":\"#BAE80A\"},{\"id\":36,\"ldraw\":10,\"name\":\"Bright Green\",\"hex\":\"#4A9E49\"},{\"id\":39,\"ldraw\":3,\"name\":\"Dark Turquoise\",\"hex\":\"#00828E\"},{\"id\":42,\"ldraw\":73,\"name\":\"Medium Blue\",\"hex\":\"#5B9CD0\"},{\"id\":47,\"ldraw\":5,\"name\":\"Dark Pink\",\"hex\":\"#C76F9F\"},{\"id\":48,\"ldraw\":378,\"name\":\"Sand Green\",\"hex\":\"#9FBBAB\"},{\"id\":49,\"ldraw\":503,\"name\":\"Very Light Gray\",\"hex\":\"#E5E2D9\"},{\"id\":55,\"ldraw\":379,\"name\":\"Sand Blue\",\"hex\":\"#587083\"},{\"id\":56,\"ldraw\":77,\"name\":\"Light Pink\",\"hex\":\"#FDCBCE\"},{\"id\":59,\"ldraw\":320,\"name\":\"Dark Red\",\"hex\":\"#710D0E\"},{\"id\":63,\"ldraw\":272,\"name\":\"Dark Blue\",\"hex\":\"#0C315A\"},{\"id\":68,\"ldraw\":484,\"name\":\"Dark Orange\",\"hex\":\"#A85400\"},{\"id\":69,\"ldraw\":28,\"name\":\"Dark Tan\",\"hex\":\"#948972\"},{\"id\":71,\"ldraw\":26,\"name\":\"Magenta\",\"hex\":\"#913877\"},{\"id\":72,\"ldraw\":313,\"name\":\"Maersk Blue\",\"hex\":\"#53A8C7\"},{\"id\":73,\"ldraw\":112,\"name\":\"Medium Violet\",\"hex\":\"#6773C9\"},{\"id\":80,\"ldraw\":288,\"name\":\"Dark Green\",\"hex\":\"#174531\"},{\"id\":85,\"ldraw\":72,\"name\":\"Dark Bluish Gray\",\"hex\":\"#6B6D67\"},{\"id\":86,\"ldraw\":71,\"name\":\"Light Bluish Gray\",\"hex\":\"#9FA4A8\"},{\"id\":88,\"ldraw\":70,\"name\":\"Reddish Brown\",\"hex\":\"#572911\"},{\"id\":89,\"ldraw\":85,\"name\":\"Dark Purple\",\"hex\":\"#3E3590\"},{\"id\":90,\"ldraw\":78,\"name\":\"Light Nougat\",\"hex\":\"#F5D6B2\"},{\"id\":91,\"ldraw\":86,\"name\":\"Light Brown\",\"hex\":\"#7B4F39\"},{\"id\":96,\"ldraw\":68,\"name\":\"Very Light Orange\",\"hex\":\"#F2CE9A\"},{\"id\":99,\"ldraw\":151,\"name\":\"Very Light Bluish Gray\",\"hex\":\"#E5E2DF\"},{\"id\":103,\"ldraw\":226,\"name\":\"Bright Light Yellow\",\"hex\":\"#FEEF39\"},{\"id\":104,\"ldraw\":29,\"name\":\"Bright Pink\",\"hex\":\"#E3ACC7\"},{\"id\":105,\"ldraw\":212,\"name\":\"Bright Light Blue\",\"hex\":\"#85C0E0\"},{\"id\":110,\"ldraw\":191,\"name\":\"Bright Light Orange\",\"hex\":\"#F7BA3C\"},{\"id\":120,\"ldraw\":308,\"name\":\"Dark Brown\",\"hex\":\"#442800\"},{\"id\":150,\"ldraw\":84,\"name\":\"Medium Nougat\",\"hex\":\"#e3a05b\"},{\"id\":152,\"ldraw\":323,\"name\":\"Light Aqua\",\"hex\":\"#BCDBD7\"},{\"id\":153,\"ldraw\":321,\"name\":\"Dark Azure\",\"hex\":\"#1397D6\"},{\"id\":154,\"ldraw\":31,\"name\":\"Lavender\",\"hex\":\"#E0D4EC\"},{\"id\":155,\"ldraw\":330,\"name\":\"Olive Green\",\"hex\":\"#9A9959\"},{\"id\":156,\"ldraw\":322,\"name\":\"Medium Azure\",\"hex\":\"#3DC1DC\"},{\"id\":157,\"ldraw\":30,\"name\":\"Medium Lavender\",\"hex\":\"#AB77B9\"},{\"id\":158,\"ldraw\":326,\"name\":\"Yellowish Green\",\"hex\":\"#DEEDA4\"},{\"id\":167,\"ldraw\":100167,\"name\":\"Reddish Orange\",\"hex\":\"#FF5500\"},{\"id\":220,\"ldraw\":353,\"name\":\"Coral\",\"hex\":\"#ff7575\"},{\"id\":236,\"ldraw\":368,\"name\":\"Neon Yellow\",\"hex\":\"#EDFF21\"}],\"parts\":[{\"id\":\"98138\",\"ldraw\":\"98138.dat\",\"name\":\"Round tile 1 \\u00d7 1\",\"shape\":\"round\",\"colors\":[1,2,3,4,5,6,7,11,28,34,36,39,42,47,55,59,63,68,69,71,80,85,86,88,89,90,103,104,105,110,120,150,152,153,154,155,156,157,158,220,236]},{\"id\":\"4073\",\"ldraw\":\"6141.dat\",\"name\":\"Round plate 1 \\u00d7 1\",\"shape\":\"round-stud\",\"colors\":[1,2,3,4,5,6,7,8,9,10,11,28,34,36,39,42,47,48,55,59,63,68,69,71,72,85,86,88,89,103,104,110,120,150,152,155,156,157,158]},{\"id\":\"3070\",\"ldraw\":\"3070b.dat\",\"name\":\"Square tile 1 \\u00d7 1\",\"shape\":\"square\",\"colors\":[1,2,3,4,5,6,7,8,9,10,11,23,24,28,31,32,34,36,39,42,47,48,55,59,63,68,69,71,72,80,85,86,88,89,90,91,96,103,104,105,110,120,150,152,153,154,156,157,158,167,220,236]},{\"id\":\"3024\",\"ldraw\":\"3024.dat\",\"name\":\"Square plate 1 \\u00d7 1\",\"shape\":\"stud\",\"colors\":[1,2,3,4,5,6,7,8,9,10,11,23,24,28,31,32,33,34,36,39,42,47,48,49,55,56,59,63,68,69,71,72,73,80,85,86,88,89,90,91,96,99,103,104,105,110,120,150,152,153,154,155,156,157,158,167,220,236]}]};\nconst GEOMETRY={\"98138\":{\"vertices\":768,\"scale\":2000,\"data\":\"zv4gA+MCGv8gAyoCAAAgA1gCzv4gA+MCAAAgA1gCAAAgAyADyv0gAzYCWP4gA6gBGv8gAyoCyv0gAzYCGv8gAyoCzv4gA+MCHf0gAzIB1v0gA+YAWP4gA6gBHf0gAzIBWP4gA6gByv0gAzYC4PwgAwAAqP0gAwAA1v0gA+YA4PwgAwAA1v0gA+YAHf0gAzIBHf0gA87+1v0gAxr\/qP0gAwAAHf0gA87+qP0gAwAA4PwgAwAAyv0gA8r9WP4gA1j+1v0gAxr\/yv0gA8r91v0gAxr\/Hf0gA87+zv4gAx39Gv8gA9b9WP4gA1j+zv4gAx39WP4gA1j+yv0gA8r9AAAgA+D8AAAgA6j9Gv8gA9b9AAAgA+D8Gv8gA9b9zv4gAx39MgEgAx395gAgA9b9AAAgA6j9MgEgAx39AAAgA6j9AAAgA+D8NgIgA8r9qAEgA1j+5gAgA9b9NgIgA8r95gAgA9b9MgEgAx394wIgA87+KgIgAxr\/qAEgA1j+4wIgA87+qAEgA1j+NgIgA8r9IAMgAwAAWAIgAwAAKgIgAxr\/IAMgAwAAKgIgAxr\/4wIgA87+4wIgAzIBKgIgA+YAWAIgAwAA4wIgAzIBWAIgAwAAIAMgAwAANgIgAzYCqAEgA6gBKgIgA+YANgIgAzYCKgIgA+YA4wIgAzIBMgEgA+MC5gAgAyoCqAEgA6gBMgEgA+MCqAEgA6gBNgIgAzYCAAAgAyADAAAgA1gC5gAgAyoCAAAgAyAD5gAgAyoCMgEgA+MCAACQAQAAqP2QAQAA1v2QAeYAAACQAQAA1v2QAeYAWP6QAagBAACQAQAAWP6QAagBGv+QASoCAACQAQAAGv+QASoCAACQAVgCAACQAQAAAACQAVgC5gCQASoCAACQAQAA5gCQASoCqAGQAagBAACQAQAAqAGQAagBKgKQAeYAAACQAQAAKgKQAeYAWAKQAQAAAACQAQAAWAKQAQAAKgKQARr\/AACQAQAAKgKQARr\/qAGQAVj+AACQAQAAqAGQAVj+5gCQAdb9AACQAQAA5gCQAdb9AACQAaj9AACQAQAAAACQAaj9Gv+QAdb9AACQAQAAGv+QAdb9WP6QAVj+AACQAQAAWP6QAVj+1v2QARr\/AACQAQAA1v2QARr\/qP2QAQAAqP0gAwAA1v0gA+YA1v2QAeYAqP0gAwAA1v2QAeYAqP2QAQAA1v0gA+YAWP4gA6gBWP6QAagB1v0gA+YAWP6QAagB1v2QAeYAWP4gA6gBGv8gAyoCGv+QASoCWP4gA6gBGv+QASoCWP6QAagBGv8gAyoCAAAgA1gCAACQAVgCGv8gAyoCAACQAVgCGv+QASoCAAAgA1gC5gAgAyoC5gCQASoCAAAgA1gC5gCQASoCAACQAVgC5gAgAyoCqAEgA6gBqAGQAagB5gAgAyoCqAGQAagB5gCQASoCqAEgA6gBKgIgA+YAKgKQAeYAqAEgA6gBKgKQAeYAqAGQAagBKgIgA+YAWAIgAwAAWAKQAQAAKgIgA+YAWAKQAQAAKgKQAeYAWAIgAwAAKgIgAxr\/KgKQARr\/WAIgAwAAKgKQARr\/WAKQAQAAKgIgAxr\/qAEgA1j+qAGQAVj+KgIgAxr\/qAGQAVj+KgKQARr\/qAEgA1j+5gAgA9b95gCQAdb9qAEgA1j+5gCQAdb9qAGQAVj+5gAgA9b9AAAgA6j9AACQAaj95gAgA9b9AACQAaj95gCQAdb9AAAgA6j9Gv8gA9b9Gv+QAdb9AAAgA6j9Gv+QAdb9AACQAaj9Gv8gA9b9WP4gA1j+WP6QAVj+Gv8gA9b9WP6QAVj+Gv+QAdb9WP4gA1j+1v0gAxr\/1v2QARr\/WP4gA1j+1v2QARr\/WP6QAVj+1v0gAxr\/qP0gAwAAqP2QAQAA1v0gAxr\/qP2QAQAA1v2QARr\/fPwgAwAAwPwgA1gBHf0gAzIBfPwgAwAAHf0gAzIB4PwgAwAAwPwgA1gBhP0gA3wCyv0gAzYCwPwgA1gByv0gAzYCHf0gAzIBhP0gA3wCqP4gA0ADzv4gA+MChP0gA3wCzv4gA+MCyv0gAzYCqP4gA0ADAAAgA4QDAAAgAyADqP4gA0ADAAAgAyADzv4gA+MCAAAgA4QDWAEgA0ADMgEgA+MCAAAgA4QDMgEgA+MCAAAgAyADWAEgA0ADfAIgA3wCNgIgAzYCWAEgA0ADNgIgAzYCMgEgA+MCfAIgA3wCQAMgA1gB4wIgAzIBfAIgA3wC4wIgAzIBNgIgAzYCQAMgA1gBhAMgAwAAIAMgAwAAQAMgA1gBIAMgAwAA4wIgAzIBhAMgAwAAQAMgA6j+4wIgA87+hAMgAwAA4wIgA87+IAMgAwAAQAMgA6j+fAIgA4T9NgIgA8r9QAMgA6j+NgIgA8r94wIgA87+fAIgA4T9WAEgA8D8MgEgAx39fAIgA4T9MgEgAx39NgIgA8r9WAEgA8D8AAAgA3z8AAAgA+D8WAEgA8D8AAAgA+D8MgEgAx39AAAgA3z8qP4gA8D8zv4gAx39AAAgA3z8zv4gAx39AAAgA+D8qP4gA8D8hP0gA4T9yv0gA8r9qP4gA8D8yv0gA8r9zv4gAx39hP0gA4T9wPwgA6j+Hf0gA87+hP0gA4T9Hf0gA87+yv0gA8r9wPwgA6j+fPwgAwAA4PwgAwAAwPwgA6j+4PwgAwAAHf0gA87+hAMgAwAAQAMgA1gBQAO8AlgBhAMgAwAAQAO8AlgBhAO8AgAAQAMgA1gBfAIgA3wCfAK8AnwCQAMgA1gBfAK8AnwCQAO8AlgBfAIgA3wCWAEgA0ADWAG8AkADfAIgA3wCWAG8AkADfAK8AnwCWAEgA0ADAAAgA4QDAAC8AoQDWAEgA0ADAAC8AoQDWAG8AkADAAAgA4QDqP4gA0ADqP68AkADAAAgA4QDqP68AkADAAC8AoQDqP4gA0ADhP0gA3wChP28AnwCqP4gA0ADhP28AnwCqP68AkADhP0gA3wCwPwgA1gBwPy8AlgBhP0gA3wCwPy8AlgBhP28AnwCwPwgA1gBfPwgAwAAfPy8AgAAwPwgA1gBfPy8AgAAwPy8AlgBfPwgAwAAwPwgA6j+wPy8Aqj+fPwgAwAAwPy8Aqj+fPy8AgAAwPwgA6j+hP0gA4T9hP28AoT9wPwgA6j+hP28AoT9wPy8Aqj+hP0gA4T9qP4gA8D8qP68AsD8hP0gA4T9qP68AsD8hP28AoT9qP4gA8D8AAAgA3z8AAC8Anz8qP4gA8D8AAC8Anz8qP68AsD8AAAgA3z8WAEgA8D8WAG8AsD8AAAgA3z8WAG8AsD8AAC8Anz8WAEgA8D8fAIgA4T9fAK8AoT9WAEgA8D8fAK8AoT9WAG8AsD8fAIgA4T9QAMgA6j+QAO8Aqj+fAIgA4T9QAO8Aqj+fAK8AoT9QAMgA6j+hAMgAwAAhAO8AgAAQAMgA6j+hAO8AgAAQAO8Aqj+GPy8AgAAZPy8An8BwPy8AlgBGPy8AgAAwPy8AlgBfPy8AgAAZPy8An8BPf28AsMChP28AnwCZPy8An8BhP28AnwCwPy8AlgBPf28AsMCgf68ApwDqP68AkADPf28AsMCqP68AkADhP28AnwCgf68ApwDAAC8AugDAAC8AoQDgf68ApwDAAC8AoQDqP68AkADAAC8AugDfwG8ApwDWAG8AkADAAC8AugDWAG8AkADAAC8AoQDfwG8ApwDwwK8AsMCfAK8AnwCfwG8ApwDfAK8AnwCWAG8AkADwwK8AsMCnAO8An8BQAO8AlgBwwK8AsMCQAO8AlgBfAK8AnwCnAO8An8B6AO8AgAAhAO8AgAAnAO8An8BhAO8AgAAQAO8AlgB6AO8AgAAnAO8AoH+QAO8Aqj+6AO8AgAAQAO8Aqj+hAO8AgAAnAO8AoH+wwK8Aj39fAK8AoT9nAO8AoH+fAK8AoT9QAO8Aqj+wwK8Aj39fwG8AmT8WAG8AsD8wwK8Aj39WAG8AsD8fAK8AoT9fwG8AmT8AAC8Ahj8AAC8Anz8fwG8AmT8AAC8Anz8WAG8AsD8AAC8Ahj8gf68AmT8qP68AsD8AAC8Ahj8qP68AsD8AAC8Anz8gf68AmT8Pf28Aj39hP28AoT9gf68AmT8hP28AoT9qP68AsD8Pf28Aj39ZPy8AoH+wPy8Aqj+Pf28Aj39wPy8Aqj+hP28AoT9ZPy8AoH+GPy8AgAAfPy8AgAAZPy8AoH+fPy8AgAAwPy8Aqj+6AO8AgAAnAO8An8BnAMAAH8B6AO8AgAAnAMAAH8B6AMAAAAAnAO8An8BwwK8AsMCwwIAAMMCnAO8An8BwwIAAMMCnAMAAH8BwwK8AsMCfwG8ApwDfwEAAJwDwwK8AsMCfwEAAJwDwwIAAMMCfwG8ApwDAAC8AugDAAAAAOgDfwG8ApwDAAAAAOgDfwEAAJwDAAC8AugDgf68ApwDgf4AAJwDAAC8AugDgf4AAJwDAAAAAOgDgf68ApwDPf28AsMCPf0AAMMCgf68ApwDPf0AAMMCgf4AAJwDPf28AsMCZPy8An8BZPwAAH8BPf28AsMCZPwAAH8BPf0AAMMCZPy8An8BGPy8AgAAGPwAAAAAZPy8An8BGPwAAAAAZPwAAH8BGPy8AgAAZPy8AoH+ZPwAAIH+GPy8AgAAZPwAAIH+GPwAAAAAZPy8AoH+Pf28Aj39Pf0AAD39ZPy8AoH+Pf0AAD39ZPwAAIH+Pf28Aj39gf68AmT8gf4AAGT8Pf28Aj39gf4AAGT8Pf0AAD39gf68AmT8AAC8Ahj8AAAAABj8gf68AmT8AAAAABj8gf4AAGT8AAC8Ahj8fwG8AmT8fwEAAGT8AAC8Ahj8fwEAAGT8AAAAABj8fwG8AmT8wwK8Aj39wwIAAD39fwG8AmT8wwIAAD39fwEAAGT8wwK8Aj39nAO8AoH+nAMAAIH+wwK8Aj39nAMAAIH+wwIAAD39nAO8AoH+6AO8AgAA6AMAAAAAnAO8AoH+6AMAAAAAnAMAAIH+6AMAAAAAnAMAAH8BQAMAAFgB6AMAAAAAQAMAAFgBhAMAAAAAnAMAAH8BwwIAAMMCfAIAAHwCnAMAAH8BfAIAAHwCQAMAAFgBwwIAAMMCfwEAAJwDWAEAAEADwwIAAMMCWAEAAEADfAIAAHwCfwEAAJwDAAAAAOgDAAAAAIQDfwEAAJwDAAAAAIQDWAEAAEADAAAAAOgDgf4AAJwDqP4AAEADAAAAAOgDqP4AAEADAAAAAIQDgf4AAJwDPf0AAMMChP0AAHwCgf4AAJwDhP0AAHwCqP4AAEADPf0AAMMCZPwAAH8BwPwAAFgBPf0AAMMCwPwAAFgBhP0AAHwCZPwAAH8BGPwAAAAAfPwAAAAAZPwAAH8BfPwAAAAAwPwAAFgBGPwAAAAAZPwAAIH+wPwAAKj+GPwAAAAAwPwAAKj+fPwAAAAAZPwAAIH+Pf0AAD39hP0AAIT9ZPwAAIH+hP0AAIT9wPwAAKj+Pf0AAD39gf4AAGT8qP4AAMD8Pf0AAD39qP4AAMD8hP0AAIT9gf4AAGT8AAAAABj8AAAAAHz8gf4AAGT8AAAAAHz8qP4AAMD8AAAAABj8fwEAAGT8WAEAAMD8AAAAABj8WAEAAMD8AAAAAHz8fwEAAGT8wwIAAD39fAIAAIT9fwEAAGT8fAIAAIT9WAEAAMD8wwIAAD39nAMAAIH+QAMAAKj+wwIAAD39QAMAAKj+fAIAAIT9nAMAAIH+6AMAAAAAhAMAAAAAnAMAAIH+hAMAAAAAQAMAAKj+AAAAAAAAhAMAAAAAQAMAAFgBAAAAAAAAQAMAAFgBfAIAAHwCAAAAAAAAfAIAAHwCWAEAAEADAAAAAAAAWAEAAEADAAAAAIQDAAAAAAAAAAAAAIQDqP4AAEADAAAAAAAAqP4AAEADhP0AAHwCAAAAAAAAhP0AAHwCwPwAAFgBAAAAAAAAwPwAAFgBfPwAAAAAAAAAAAAAfPwAAAAAwPwAAKj+AAAAAAAAwPwAAKj+hP0AAIT9AAAAAAAAhP0AAIT9qP4AAMD8AAAAAAAAqP4AAMD8AAAAAHz8AAAAAAAAAAAAAHz8WAEAAMD8AAAAAAAAWAEAAMD8fAIAAIT9AAAAAAAAfAIAAIT9QAMAAKj+AAAAAAAAQAMAAKj+hAMAAAAA\"},\"4073\":{\"vertices\":864,\"scale\":2000,\"data\":\"qP0sAQAA1v0sAeYA1v0gA+YAqP0sAQAA1v0gA+YAqP0gAwAA1v0sAeYAWP4sAagBWP4gA6gB1v0sAeYAWP4gA6gB1v0gA+YAWP4sAagBGv8sASoCGv8gAyoCWP4sAagBGv8gAyoCWP4gA6gBGv8sASoCAAAsAVgCAAAgA1gCGv8sASoCAAAgA1gCGv8gAyoCAAAsAVgC5gAsASoC5gAgAyoCAAAsAVgC5gAgAyoCAAAgA1gC5gAsASoCqAEsAagBqAEgA6gB5gAsASoCqAEgA6gB5gAgAyoCqAEsAagBKgIsAeYAKgIgA+YAqAEsAagBKgIgA+YAqAEgA6gBKgIsAeYAWAIsAQAAWAIgAwAAKgIsAeYAWAIgAwAAKgIgA+YAWAIsAQAAKgIsARr\/KgIgAxr\/WAIsAQAAKgIgAxr\/WAIgAwAAKgIsARr\/qAEsAVj+qAEgA1j+KgIsARr\/qAEgA1j+KgIgAxr\/qAEsAVj+5gAsAdb95gAgA9b9qAEsAVj+5gAgA9b9qAEgA1j+5gAsAdb9AAAsAaj9AAAgA6j95gAsAdb9AAAgA6j95gAgA9b9AAAsAaj9Gv8sAdb9Gv8gA9b9AAAsAaj9Gv8gA9b9AAAgA6j9Gv8sAdb9WP4sAVj+WP4gA1j+Gv8sAdb9WP4gA1j+Gv8gA9b9WP4sAVj+1v0sARr\/1v0gAxr\/WP4sAVj+1v0gAxr\/WP4gA1j+1v0sARr\/qP0sAQAAqP0gAwAA1v0sARr\/qP0gAwAA1v0gAxr\/4PwsAQAAHf0sATIBHf0gAzIB4PwsAQAAHf0gAzIB4PwgAwAAHf0sATIByv0sATYCyv0gAzYCHf0sATIByv0gAzYCHf0gAzIByv0sATYCzv4sAeMCzv4gA+MCyv0sATYCzv4gA+MCyv0gAzYCzv4sAeMCAAAsASADAAAgAyADzv4sAeMCAAAgAyADzv4gA+MCAAAsASADMgEsAeMCMgEgA+MCAAAsASADMgEgA+MCAAAgAyADMgEsAeMCNgIsATYCNgIgAzYCMgEsAeMCNgIgAzYCMgEgA+MCNgIsATYC4wIsATIB4wIgAzIBNgIsATYC4wIgAzIBNgIgAzYC4wIsATIBIAMsAQAAIAMgAwAA4wIsATIBIAMgAwAA4wIgAzIBIAMsAQAA4wIsAc7+4wIgA87+IAMsAQAA4wIgA87+IAMgAwAA4wIsAc7+NgIsAcr9NgIgA8r94wIsAc7+NgIgA8r94wIgA87+NgIsAcr9MgEsAR39MgEgAx39NgIsAcr9MgEgAx39NgIgA8r9MgEsAR39AAAsAeD8AAAgA+D8MgEsAR39AAAgA+D8MgEgAx39AAAsAeD8zv4sAR39zv4gAx39AAAsAeD8zv4gAx39AAAgA+D8zv4sAR39yv0sAcr9yv0gA8r9zv4sAR39yv0gA8r9zv4gAx39yv0sAcr9Hf0sAc7+Hf0gA87+yv0sAcr9Hf0gA87+yv0gA8r9Hf0sAc7+4PwsAQAA4PwgAwAAHf0sAc7+4PwgAwAAHf0gA87+MgEgA+MC5gAgAyoCAAAgA1gCMgEgA+MCAAAgA1gCAAAgAyADNgIgAzYCqAEgA6gB5gAgAyoCNgIgAzYC5gAgAyoCMgEgA+MC4wIgAzIBKgIgA+YAqAEgA6gB4wIgAzIBqAEgA6gBNgIgAzYCIAMgAwAAWAIgAwAAKgIgA+YAIAMgAwAAKgIgA+YA4wIgAzIB4wIgA87+KgIgAxr\/WAIgAwAA4wIgA87+WAIgAwAAIAMgAwAANgIgA8r9qAEgA1j+KgIgAxr\/NgIgA8r9KgIgAxr\/4wIgA87+MgEgAx395gAgA9b9qAEgA1j+MgEgAx39qAEgA1j+NgIgA8r9AAAgA+D8AAAgA6j95gAgA9b9AAAgA+D85gAgA9b9MgEgAx39zv4gAx39Gv8gA9b9AAAgA6j9zv4gAx39AAAgA6j9AAAgA+D8yv0gA8r9WP4gA1j+Gv8gA9b9yv0gA8r9Gv8gA9b9zv4gAx39Hf0gA87+1v0gAxr\/WP4gA1j+Hf0gA87+WP4gA1j+yv0gA8r94PwgAwAAqP0gAwAA1v0gAxr\/4PwgAwAA1v0gAxr\/Hf0gA87+Hf0gAzIB1v0gA+YAqP0gAwAAHf0gAzIBqP0gAwAA4PwgAwAAyv0gAzYCWP4gA6gB1v0gA+YAyv0gAzYC1v0gA+YAHf0gAzIBzv4gA+MCGv8gAyoCWP4gA6gBzv4gA+MCWP4gA6gByv0gAzYCAAAgAyADAAAgA1gCGv8gAyoCAAAgAyADGv8gAyoCzv4gA+MCAAAsAQAAqP0sAQAA1v0sAeYAAAAsAQAA1v0sAeYAWP4sAagBAAAsAQAAWP4sAagBGv8sASoCAAAsAQAAGv8sASoCAAAsAVgCAAAsAQAAAAAsAVgC5gAsASoCAAAsAQAA5gAsASoCqAEsAagBAAAsAQAAqAEsAagBKgIsAeYAAAAsAQAAKgIsAeYAWAIsAQAAAAAsAQAAWAIsAQAAKgIsARr\/AAAsAQAAKgIsARr\/qAEsAVj+AAAsAQAAqAEsAVj+5gAsAdb9AAAsAQAA5gAsAdb9AAAsAaj9AAAsAQAAAAAsAaj9Gv8sAdb9AAAsAQAAGv8sAdb9WP4sAVj+AAAsAQAAWP4sAVj+1v0sARr\/AAAsAQAA1v0sARr\/qP0sAQAAfwEsAZwDMgEsAeMCAAAsASADfwEsAZwDAAAsASADAAAsAegDwwIsAcMCNgIsATYCMgEsAeMCwwIsAcMCMgEsAeMCfwEsAZwDnAMsAX8B4wIsATIBNgIsATYCnAMsAX8BNgIsATYCwwIsAcMC6AMsAQAAIAMsAQAA4wIsATIB6AMsAQAA4wIsATIBnAMsAX8BnAMsAYH+4wIsAc7+IAMsAQAAnAMsAYH+IAMsAQAA6AMsAQAAwwIsAT39NgIsAcr94wIsAc7+wwIsAT394wIsAc7+nAMsAYH+fwEsAWT8MgEsAR39NgIsAcr9fwEsAWT8NgIsAcr9wwIsAT39AAAsARj8AAAsAeD8MgEsAR39AAAsARj8MgEsAR39fwEsAWT8gf4sAWT8zv4sAR39AAAsAeD8gf4sAWT8AAAsAeD8AAAsARj8Pf0sAT39yv0sAcr9zv4sAR39Pf0sAT39zv4sAR39gf4sAWT8ZPwsAYH+Hf0sAc7+yv0sAcr9ZPwsAYH+yv0sAcr9Pf0sAT39GPwsAQAA4PwsAQAAHf0sAc7+GPwsAQAAHf0sAc7+ZPwsAYH+ZPwsAX8BHf0sATIB4PwsAQAAZPwsAX8B4PwsAQAAGPwsAQAAPf0sAcMCyv0sATYCHf0sATIBPf0sAcMCHf0sATIBZPwsAX8Bgf4sAZwDzv4sAeMCyv0sATYCgf4sAZwDyv0sATYCPf0sAcMCAAAsAegDAAAsASADzv4sAeMCAAAsAegDzv4sAeMCgf4sAZwD6AMsAQAAnAMsAX8BnAMAAH8B6AMsAQAAnAMAAH8B6AMAAAAAnAMsAX8BwwIsAcMCwwIAAMMCnAMsAX8BwwIAAMMCnAMAAH8BwwIsAcMCfwEsAZwDfwEAAJwDwwIsAcMCfwEAAJwDwwIAAMMCfwEsAZwDAAAsAegDAAAAAOgDfwEsAZwDAAAAAOgDfwEAAJwDAAAsAegDgf4sAZwDgf4AAJwDAAAsAegDgf4AAJwDAAAAAOgDgf4sAZwDPf0sAcMCPf0AAMMCgf4sAZwDPf0AAMMCgf4AAJwDPf0sAcMCZPwsAX8BZPwAAH8BPf0sAcMCZPwAAH8BPf0AAMMCZPwsAX8BGPwsAQAAGPwAAAAAZPwsAX8BGPwAAAAAZPwAAH8BGPwsAQAAZPwsAYH+ZPwAAIH+GPwsAQAAZPwAAIH+GPwAAAAAZPwsAYH+Pf0sAT39Pf0AAD39ZPwsAYH+Pf0AAD39ZPwAAIH+Pf0sAT39gf4sAWT8gf4AAGT8Pf0sAT39gf4AAGT8Pf0AAD39gf4sAWT8AAAsARj8AAAAABj8gf4sAWT8AAAAABj8gf4AAGT8AAAsARj8fwEsAWT8fwEAAGT8AAAsARj8fwEAAGT8AAAAABj8fwEsAWT8wwIsAT39wwIAAD39fwEsAWT8wwIAAD39fwEAAGT8wwIsAT39nAMsAYH+nAMAAIH+wwIsAT39nAMAAIH+wwIAAD39nAMsAYH+6AMsAQAA6AMAAAAAnAMsAYH+6AMAAAAAnAMAAIH+gf4AAJwDzv4AAOMCAAAAACADgf4AAJwDAAAAACADAAAAAOgDPf0AAMMCyv0AADYCzv4AAOMCPf0AAMMCzv4AAOMCgf4AAJwDZPwAAH8BHf0AADIByv0AADYCZPwAAH8Byv0AADYCPf0AAMMCGPwAAAAA4PwAAAAAHf0AADIBGPwAAAAAHf0AADIBZPwAAH8BZPwAAIH+Hf0AAM7+4PwAAAAAZPwAAIH+4PwAAAAAGPwAAAAAPf0AAD39yv0AAMr9Hf0AAM7+Pf0AAD39Hf0AAM7+ZPwAAIH+gf4AAGT8zv4AAB39yv0AAMr9gf4AAGT8yv0AAMr9Pf0AAD39AAAAABj8AAAAAOD8zv4AAB39AAAAABj8zv4AAB39gf4AAGT8fwEAAGT8MgEAAB39AAAAAOD8fwEAAGT8AAAAAOD8AAAAABj8wwIAAD39NgIAAMr9MgEAAB39wwIAAD39MgEAAB39fwEAAGT8nAMAAIH+4wIAAM7+NgIAAMr9nAMAAIH+NgIAAMr9wwIAAD396AMAAAAAIAMAAAAA4wIAAM7+6AMAAAAA4wIAAM7+nAMAAIH+nAMAAH8B4wIAADIBIAMAAAAAnAMAAH8BIAMAAAAA6AMAAAAAwwIAAMMCNgIAADYC4wIAADIBwwIAAMMC4wIAADIBnAMAAH8BfwEAAJwDMgEAAOMCNgIAADYCfwEAAJwDNgIAADYCwwIAAMMCAAAAAOgDAAAAACADMgEAAOMCAAAAAOgDMgEAAOMCfwEAAJwDzv4AAOMCGv8AACoCAAAAAFgCzv4AAOMCAAAAAFgCAAAAACADyv0AADYCWP4AAKgBGv8AACoCyv0AADYCGv8AACoCzv4AAOMCHf0AADIB1v0AAOYAWP4AAKgBHf0AADIBWP4AAKgByv0AADYC4PwAAAAAqP0AAAAA1v0AAOYA4PwAAAAA1v0AAOYAHf0AADIBHf0AAM7+1v0AABr\/qP0AAAAAHf0AAM7+qP0AAAAA4PwAAAAAyv0AAMr9WP4AAFj+1v0AABr\/yv0AAMr91v0AABr\/Hf0AAM7+zv4AAB39Gv8AANb9WP4AAFj+zv4AAB39WP4AAFj+yv0AAMr9AAAAAOD8AAAAAKj9Gv8AANb9AAAAAOD8Gv8AANb9zv4AAB39MgEAAB395gAAANb9AAAAAKj9MgEAAB39AAAAAKj9AAAAAOD8NgIAAMr9qAEAAFj+5gAAANb9NgIAAMr95gAAANb9MgEAAB394wIAAM7+KgIAABr\/qAEAAFj+4wIAAM7+qAEAAFj+NgIAAMr9IAMAAAAAWAIAAAAAKgIAABr\/IAMAAAAAKgIAABr\/4wIAAM7+4wIAADIBKgIAAOYAWAIAAAAA4wIAADIBWAIAAAAAIAMAAAAANgIAADYCqAEAAKgBKgIAAOYANgIAADYCKgIAAOYA4wIAADIBMgEAAOMC5gAAACoCqAEAAKgBMgEAAOMCqAEAAKgBNgIAADYCAAAAACADAAAAAFgC5gAAACoCAAAAACAD5gAAACoCMgEAAOMCWAJw\/gAAKgJw\/uYAKgIAAOYAWAJw\/gAAKgIAAOYAWAIAAAAAKgJw\/uYAqAFw\/qgBqAEAAKgBKgJw\/uYAqAEAAKgBKgIAAOYAqAFw\/qgB5gBw\/ioC5gAAACoCqAFw\/qgB5gAAACoCqAEAAKgB5gBw\/ioCAABw\/lgCAAAAAFgC5gBw\/ioCAAAAAFgC5gAAACoCAABw\/lgCGv9w\/ioCGv8AACoCAABw\/lgCGv8AACoCAAAAAFgCGv9w\/ioCWP5w\/qgBWP4AAKgBGv9w\/ioCWP4AAKgBGv8AACoCWP5w\/qgB1v1w\/uYA1v0AAOYAWP5w\/qgB1v0AAOYAWP4AAKgB1v1w\/uYAqP1w\/gAAqP0AAAAA1v1w\/uYAqP0AAAAA1v0AAOYAqP1w\/gAA1v1w\/hr\/1v0AABr\/qP1w\/gAA1v0AABr\/qP0AAAAA1v1w\/hr\/WP5w\/lj+WP4AAFj+1v1w\/hr\/WP4AAFj+1v0AABr\/WP5w\/lj+Gv9w\/tb9Gv8AANb9WP5w\/lj+Gv8AANb9WP4AAFj+Gv9w\/tb9AABw\/qj9AAAAAKj9Gv9w\/tb9AAAAAKj9Gv8AANb9AABw\/qj95gBw\/tb95gAAANb9AABw\/qj95gAAANb9AAAAAKj95gBw\/tb9qAFw\/lj+qAEAAFj+5gBw\/tb9qAEAAFj+5gAAANb9qAFw\/lj+KgJw\/hr\/KgIAABr\/qAFw\/lj+KgIAABr\/qAEAAFj+KgJw\/hr\/WAJw\/gAAWAIAAAAAKgJw\/hr\/WAIAAAAAKgIAABr\/AABw\/gAAWAJw\/gAAKgJw\/uYAAABw\/gAAKgJw\/uYAqAFw\/qgBAABw\/gAAqAFw\/qgB5gBw\/ioCAABw\/gAA5gBw\/ioCAABw\/lgCAABw\/gAAAABw\/lgCGv9w\/ioCAABw\/gAAGv9w\/ioCWP5w\/qgBAABw\/gAAWP5w\/qgB1v1w\/uYAAABw\/gAA1v1w\/uYAqP1w\/gAAAABw\/gAAqP1w\/gAA1v1w\/hr\/AABw\/gAA1v1w\/hr\/WP5w\/lj+AABw\/gAAWP5w\/lj+Gv9w\/tb9AABw\/gAAGv9w\/tb9AABw\/qj9AABw\/gAAAABw\/qj95gBw\/tb9AABw\/gAA5gBw\/tb9qAFw\/lj+AABw\/gAAqAFw\/lj+KgJw\/hr\/AABw\/gAAKgJw\/hr\/WAJw\/gAA\"},\"3070\":{\"vertices\":132,\"scale\":2000,\"data\":\"GPy8AugDGPwAAOgD6AMAAOgDGPy8AugD6AMAAOgD6AO8AugDGPy8Ahj8GPwAABj8GPwAAOgDGPy8Ahj8GPwAAOgDGPy8AugD6AO8Ahj86AMAABj8GPwAABj86AO8Ahj8GPwAABj8GPy8Ahj86AO8AugD6AMAAOgD6AMAABj86AO8AugD6AMAABj86AO8Ahj8qP2QAVgCWAKQAVgCWAKQAaj9qP2QAVgCWAKQAaj9qP2QAaj9qP2QAVgCqP0gA1gCWAIgA1gCqP2QAVgCWAIgA1gCWAKQAVgCqP2QAaj9qP0gA6j9qP0gA1gCqP2QAaj9qP0gA1gCqP2QAVgCWAKQAaj9WAIgA6j9qP0gA6j9WAKQAaj9qP0gA6j9qP2QAaj9WAKQAVgCWAIgA1gCWAIgA6j9WAKQAVgCWAIgA6j9WAKQAaj9fPwgA4QDfPy8AoQDhAO8AoQDfPwgA4QDhAO8AoQDhAMgA4QDfPwgA3z8fPy8Anz8fPy8AoQDfPwgA3z8fPy8AoQDfPwgA4QDhAMgA3z8hAO8Anz8fPy8Anz8hAMgA3z8fPy8Anz8fPwgA3z8hAMgA4QDhAO8AoQDhAO8Anz8hAMgA4QDhAO8Anz8hAMgA3z8hAMgA4QDWAIgA1gCqP0gA1gChAMgA4QDqP0gA1gCfPwgA4QDfPwgA4QDqP0gA1gCqP0gA6j9fPwgA4QDqP0gA6j9fPwgA3z8fPwgA3z8qP0gA6j9WAIgA6j9fPwgA3z8WAIgA6j9hAMgA3z8hAMgA3z8WAIgA6j9WAIgA1gChAMgA3z8WAIgA1gChAMgA4QD6AO8AugDhAO8AoQDfPy8AoQD6AO8AugDfPy8AoQDGPy8AugDGPy8AugDfPy8AoQDfPy8Anz8GPy8AugDfPy8Anz8GPy8Ahj8GPy8Ahj8fPy8Anz8hAO8Anz8GPy8Ahj8hAO8Anz86AO8Ahj86AO8Ahj8hAO8Anz8hAO8AoQD6AO8Ahj8hAO8AoQD6AO8AugDGPwAAOgDGPwAABj86AMAABj8GPwAAOgD6AMAABj86AMAAOgD\"},\"3024\":{\"vertices\":228,\"scale\":2000,\"data\":\"qP2QAVgCWAKQAVgCWAKQAaj9qP2QAVgCWAKQAaj9qP2QAaj9qP2QAVgCqP0gA1gCWAIgA1gCqP2QAVgCWAIgA1gCWAKQAVgCqP2QAaj9qP0gA6j9qP0gA1gCqP2QAaj9qP0gA1gCqP2QAVgCWAKQAaj9WAIgA6j9qP0gA6j9WAKQAaj9qP0gA6j9qP2QAaj9WAKQAVgCWAIgA1gCWAIgA6j9WAKQAVgCWAIgA6j9WAKQAaj96AMgA+gDWAIgA1gCqP0gA1gC6AMgA+gDqP0gA1gCGPwgA+gDGPwgA+gDqP0gA1gCqP0gA6j9GPwgA+gDqP0gA6j9GPwgAxj8GPwgAxj8qP0gA6j9WAIgA6j9GPwgAxj8WAIgA6j96AMgAxj86AMgAxj8WAIgA6j9WAIgA1gC6AMgAxj8WAIgA1gC6AMgA+gDGPwAAOgD6AMAAOgD6AMAABj8GPwAAOgD6AMAABj8GPwAABj8GPwAAOgDGPwgA+gD6AMgA+gDGPwAAOgD6AMgA+gD6AMAAOgDGPwAABj8GPwgAxj8GPwgA+gDGPwAABj8GPwgA+gDGPwAAOgD6AMAABj86AMgAxj8GPwgAxj86AMAABj8GPwgAxj8GPwAABj86AMAAOgD6AMgA+gD6AMgAxj86AMAAOgD6AMgAxj86AMAABj8WAJw\/gAAKgJw\/uYAKgIAAOYAWAJw\/gAAKgIAAOYAWAIAAAAAKgJw\/uYAqAFw\/qgBqAEAAKgBKgJw\/uYAqAEAAKgBKgIAAOYAqAFw\/qgB5gBw\/ioC5gAAACoCqAFw\/qgB5gAAACoCqAEAAKgB5gBw\/ioCAABw\/lgCAAAAAFgC5gBw\/ioCAAAAAFgC5gAAACoCAABw\/lgCGv9w\/ioCGv8AACoCAABw\/lgCGv8AACoCAAAAAFgCGv9w\/ioCWP5w\/qgBWP4AAKgBGv9w\/ioCWP4AAKgBGv8AACoCWP5w\/qgB1v1w\/uYA1v0AAOYAWP5w\/qgB1v0AAOYAWP4AAKgB1v1w\/uYAqP1w\/gAAqP0AAAAA1v1w\/uYAqP0AAAAA1v0AAOYAqP1w\/gAA1v1w\/hr\/1v0AABr\/qP1w\/gAA1v0AABr\/qP0AAAAA1v1w\/hr\/WP5w\/lj+WP4AAFj+1v1w\/hr\/WP4AAFj+1v0AABr\/WP5w\/lj+Gv9w\/tb9Gv8AANb9WP5w\/lj+Gv8AANb9WP4AAFj+Gv9w\/tb9AABw\/qj9AAAAAKj9Gv9w\/tb9AAAAAKj9Gv8AANb9AABw\/qj95gBw\/tb95gAAANb9AABw\/qj95gAAANb9AAAAAKj95gBw\/tb9qAFw\/lj+qAEAAFj+5gBw\/tb9qAEAAFj+5gAAANb9qAFw\/lj+KgJw\/hr\/KgIAABr\/qAFw\/lj+KgIAABr\/qAEAAFj+KgJw\/hr\/WAJw\/gAAWAIAAAAAKgJw\/hr\/WAIAAAAAKgIAABr\/AABw\/gAAWAJw\/gAAKgJw\/uYAAABw\/gAAKgJw\/uYAqAFw\/qgBAABw\/gAAqAFw\/qgB5gBw\/ioCAABw\/gAA5gBw\/ioCAABw\/lgCAABw\/gAAAABw\/lgCGv9w\/ioCAABw\/gAAGv9w\/ioCWP5w\/qgBAABw\/gAAWP5w\/qgB1v1w\/uYAAABw\/gAA1v1w\/uYAqP1w\/gAAAABw\/gAAqP1w\/gAA1v1w\/hr\/AABw\/gAA1v1w\/hr\/WP5w\/lj+AABw\/gAAWP5w\/lj+Gv9w\/tb9AABw\/gAAGv9w\/tb9AABw\/qj9AABw\/gAAAABw\/qj95gBw\/tb9AABw\/gAA5gBw\/tb9qAFw\/lj+AABw\/gAAqAFw\/lj+KgJw\/hr\/AABw\/gAAKgJw\/hr\/WAJw\/gAA\"}};\nconst WORKER_SOURCE=\"\/* Original Design-Bricks Mosaic Studio engine. *\/\\nconst clamp=(x,a,b)=>Math.max(a,Math.min(b,x));\\nconst rgb=hex=>hex.match(\/[a-f\\\\d]{2}\/ig).map(v=>parseInt(v,16));\\nfunction lab(c){\\n const v=c.map(x=>{x=clamp(x,0,255)\/255;return x>.04045?((x+.055)\/1.055)**2.4:x\/12.92;});\\n const f=x=>x>.008856?Math.cbrt(x):7.787*x+16\/116;\\n const x=f((v[0]*.4124564+v[1]*.3575761+v[2]*.1804375)\/.95047),y=f(v[0]*.2126729+v[1]*.7151522+v[2]*.072175),z=f((v[0]*.0193339+v[1]*.119192+v[2]*.9503041)\/1.08883);\\n return [116*y-16,500*(x-y),200*(y-z)];\\n}\\nconst delta=(a,b)=>(a[0]-b[0])**2+(a[1]-b[1])**2+(a[2]-b[2])**2;\\nfunction adjusted(pixels,w,h,opt={}){\\n const {brightness=0,contrast=0,saturation=0,hue=0,gamma=1,sharpen=0,filter='natural'}=opt;\\n const out=new Float32Array(w*h*3),cf=259*(contrast+255)\/(255*(259-contrast)),angle=hue*Math.PI\/180,co=Math.cos(angle),si=Math.sin(angle);\\n for(let i=0,j=0;i<pixels.length;i+=4,j+=3){\\n  const alpha=pixels[i+3]\/255;let [r,g,b]=[0,1,2].map(k=>pixels[i+k]*alpha+255*(1-alpha));\\n  if(hue){const y=.299*r+.587*g+.114*b,I=.596*r-.274*g-.322*b,Q=.211*r-.523*g+.312*b,ii=I*co-Q*si,qq=I*si+Q*co;r=y+.956*ii+.621*qq;g=y-.272*ii-.647*qq;b=y-1.106*ii+1.703*qq;}\\n  if(filter==='warm'){r+=14;g+=3;b-=16;}if(filter==='cool'){r-=10;g+=3;b+=16;}\\n  const l=.2126*r+.7152*g+.0722*b,sat=filter==='mono'?-100:saturation+(filter==='vivid'?25:0);\\n  let c=[r,g,b].map(v=>clamp(cf*(l+(v-l)*(1+sat\/100)-128)+128+brightness,0,255));\\n  if(filter==='poster')c=c.map(v=>Math.round(v\/64)*64);if(filter==='invert')c=c.map(v=>255-v);\\n  for(let k=0;k<3;k++)out[j+k]=255*(clamp(c[k],0,255)\/255)**(1\/Math.max(.1,gamma));\\n }\\n if(sharpen>0||filter==='sketch'){\\n  const copy=out.slice();for(let y=0;y<h;y++)for(let x=0;x<w;x++)for(let k=0;k<3;k++){\\n   const i=(y*w+x)*3+k;let avg=0;for(const [dx,dy]of [[0,-1],[-1,0],[1,0],[0,1]])avg+=copy[(clamp(y+dy,0,h-1)*w+clamp(x+dx,0,w-1))*3+k]\/4;\\n   out[i]=filter==='sketch'?clamp(255-Math.abs(copy[i]-avg)*8,0,255):clamp(copy[i]+(copy[i]-avg)*sharpen,0,255);\\n  }\\n }\\n return out;\\n}\\nfunction selectPalette(samples,colors,limit){\\n if(limit>=colors.length)return colors;\\n \/\/ Greedy weighted error reduction preserves small, distinctive subject colours.\\n const histogram=new Map();for(let i=0;i<samples.length;i+=3){const key=(samples[i]>>5)*64+(samples[i+1]>>5)*8+(samples[i+2]>>5),entry=histogram.get(key);if(entry){entry.n++;for(let k=0;k<3;k++)entry.sum[k]+=samples[i+k];}else histogram.set(key,{n:1,sum:[samples[i],samples[i+1],samples[i+2]]});}\\n const bins=[...histogram.values()].map(v=>({n:v.n,lab:lab(v.sum.map(x=>x\/v.n))}));\\n const dist=colors.map(c=>bins.map(b=>delta(c.lab,b.lab))),best=Array(bins.length).fill(Infinity),picked=[];\\n for(let step=0;step<Math.max(1,limit);step++){\\n  let chosen=-1,score=Infinity;for(let c=0;c<colors.length;c++){if(picked.includes(c))continue;let error=0;for(let i=0;i<bins.length;i++)error+=Math.min(best[i],dist[c][i])*bins[i].n;if(error<score){score=error;chosen=c;}}\\n  picked.push(chosen);for(let i=0;i<bins.length;i++)best[i]=Math.min(best[i],dist[chosen][i]);\\n }\\n return picked.map(i=>colors[i]);\\n}\\nfunction quantize(samples,width,height,colors,{limit=colors.length,dither='none',inventory=null}={}){\\n if(samples.length!==width*height*3||!colors.length)throw Error('Select at least one colour.');\\n let palette=colors.map(c=>({...c,rgb:rgb(c.hex),lab:lab(rgb(c.hex))}));\\n if(inventory){palette=palette.filter(c=>(inventory[c.id]||0)>0);const available=palette.reduce((n,c)=>n+inventory[c.id],0);if(available<width*height)throw Error(`Your selected stock has ${available.toLocaleString()} pieces. Add ${(width*height-available).toLocaleString()} more or choose a smaller grid.`);}\\n else palette=selectPalette(samples,palette,limit||palette.length);\\n const result=new Array(width*height),nearest=(p,list)=>{let ix=0,best=Infinity;for(let i=0;i<list.length;i++){const d=delta(p,list[i].lab);if(d<best){best=d;ix=i;}}return list[ix];};\\n if(inventory){\\n  const input=Array.from({length:width*height},(_,i)=>lab(Array.from(samples.slice(i*3,i*3+3)))),left={...inventory};\\n  const order=input.map((p,i)=>{let a=Infinity,b=Infinity;for(const c of palette){const d=delta(p,c.lab);if(d<a){b=a;a=d;}else b=Math.min(b,d);}return {i,priority:Number.isFinite(b)?b-a:0};}).sort((a,b)=>b.priority-a.priority||a.i-b.i);\\n  for(const {i}of order){const c=nearest(input[i],palette.filter(c=>left[c.id]>0));result[i]=c.id;left[c.id]--;}\\n }else{\\n  const work=new Float32Array(samples),bayer=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];\\n  for(let y=0;y<height;y++)for(let step=0;step<width;step++){\\n   const x=dither==='floyd'&&y%2?width-1-step:step,i=y*width+x,dir=y%2?-1:1;\\n   let p=Array.from(work.slice(i*3,i*3+3));if(dither==='ordered')p=p.map(v=>v+(bayer[(y%4)*4+x%4]-7.5)*3);\\n   const c=nearest(lab(p),palette);result[i]=c.id;\\n   if(dither==='floyd')for(const [dx,dy,weight]of [[dir,0,7\/16],[-dir,1,3\/16],[0,1,5\/16],[dir,1,1\/16]])if(x+dx>=0&&x+dx<width&&y+dy<height)for(let k=0;k<3;k++)work[((y+dy)*width+x+dx)*3+k]+=(p[k]-c.rgb[k])*weight;\\n  }\\n }\\n return result;\\n}\\nfunction counts(pixels){const m=new Map();for(const id of pixels)m.set(id,(m.get(id)||0)+1);return [...m].sort((a,b)=>b[1]-a[1]||a[0]-b[0]);}\\nfunction convertMosaic(samples,width,height,colors,options={},overrides={}){\\n const fixed=Object.entries(overrides).map(([i,id])=>[Number(i),id]).filter(([i,id])=>Number.isInteger(i)&&i>=0&&i<width*height&&Number.isInteger(id));\\n if(!fixed.length)return quantize(samples,width,height,colors,options);\\n if(!options.inventory){const result=quantize(samples,width,height,colors,options);for(const [i,id]of fixed)result[i]=id;return result;}\\n const stock={...options.inventory},result=new Array(width*height),locked=new Set();\\n for(const [i,id]of fixed){stock[id]=(stock[id]||0)-1;if(stock[id]<0)throw Error('Your painted pieces exceed this stock. Add pieces or reset manual edits.');result[i]=id;locked.add(i);}\\n const indices=[];for(let i=0;i<result.length;i++)if(!locked.has(i))indices.push(i);\\n if(indices.length){const remaining=new Float32Array(indices.length*3);indices.forEach((i,j)=>remaining.set(samples.subarray(i*3,i*3+3),j*3));const converted=quantize(remaining,indices.length,1,colors,{...options,inventory:stock,dither:'none'});indices.forEach((i,j)=>result[i]=converted[j]);}\\n return result;\\n}\\nfunction region(pixels,width,start,tolerance,colors){\\n const color=new Map(colors.map(c=>[c.id,lab(rgb(c.hex))])),target=color.get(pixels[start]),seen=new Uint8Array(pixels.length),queue=[start];seen[start]=1;\\n for(let i=0;i<queue.length;i++){const p=queue[i];for(const n of [p%width?p-1:-1,p%width<width-1?p+1:-1,p-width,p+width])if(n>=0&&n<pixels.length&&!seen[n]&&delta(color.get(pixels[n]),target)<=tolerance*tolerance){seen[n]=1;queue.push(n);}}\\n return queue;\\n}\\nfunction fillRegion(pixels,width,start,id){const out=pixels.slice(),old=out[start],queue=[start];if(old===id)return out;out[start]=id;for(let j=0;j<queue.length;j++){const p=queue[j];for(const n of [p%width?p-1:-1,p%width<width-1?p+1:-1,p-width,p+width])if(n>=0&&n<out.length&&out[n]===old){out[n]=id;queue.push(n);}}return out;}\\nfunction validateProject(p,data){\\n if(!p||p.format!=='design-bricks-mosaic'||![1,2].includes(p.version))throw Error('Choose a Design-Bricks project file.');\\n if(!Number.isInteger(p.width)||!Number.isInteger(p.height)||p.width<8||p.height<8||p.width>128||p.height>128)throw Error('Project size must be 8\\u2013128 studs per side.');\\n const part=data.parts.find(x=>x.id===p.part);if(!part)throw Error('Unsupported piece type.');\\n if(!Array.isArray(p.pixels)||p.pixels.length!==p.width*p.height||p.pixels.some(id=>!Number.isInteger(id)||!part.colors.includes(id)))throw Error('The project contains invalid pieces or colours.');\\n const selected=Array.isArray(p.selected)?p.selected.filter(id=>part.colors.includes(id)):part.colors.slice(),inventory={};\\n for(const id of part.colors){const q=p.inventory?.[id];if(Number.isInteger(q)&&q>=0&&q<=1000000)inventory[id]=q;}\\n const stocks={};for(const part of data.parts){const row={};for(const id of part.colors){const q=p.stocks?.[part.id]?.[id];if(Number.isInteger(q)&&q>=0&&q<=1000000)row[id]=q;}stocks[part.id]=row;}\\n const source=typeof p.source==='string'&&p.source.length<12000000&&\/^data:image\\\\\/(png|jpeg|webp);base64,[a-z0-9+\/=]+$\/i.test(p.source)?p.source:null;\\n const overrides={};if(p.overrides&&typeof p.overrides==='object')for(const [key,id]of Object.entries(p.overrides)){const i=Number(key);if(Number.isInteger(i)&&i>=0&&i<p.pixels.length&&part.colors.includes(id))overrides[i]=id;}\\n return {...p,version:2,title:typeof p.title==='string'?p.title.slice(0,80):'My mosaic',pixels:p.pixels.slice(),selected:selected.length?selected:part.colors.slice(),inventory,stocks,source,overrides};\\n}\\nfunction bricklinkXML(p){return '<INVENTORY>\\\\n'+counts(p.pixels).map(([id,q])=>`  <ITEM><ITEMTYPE>P<\/ITEMTYPE><ITEMID>${p.part}<\/ITEMID><COLOR>${id}<\/COLOR><MINQTY>${q}<\/MINQTY><CONDITION>N<\/CONDITION><\/ITEM>`).join('\\\\n')+'\\\\n<\/INVENTORY>\\\\n';}\\nfunction csv(p,data){const map=new Map(data.colors.map(c=>[c.id,c]));return 'part_id,colour_id,colour_name,quantity\\\\r\\\\n'+counts(p.pixels).map(([id,q])=>`${p.part},${id},\\\"${map.get(id).name.replace(\/\\\"\/g,'\\\"\\\"')}\\\",${q}`).join('\\\\r\\\\n')+'\\\\r\\\\n';}\\nfunction ldraw(p,data){const map=new Map(data.colors.map(c=>[c.id,c])),piece=data.parts.find(x=>x.id===p.part);return `0 Design-Bricks Mosaic Studio\\\\n0 Name: design-bricks-mosaic.ldr\\\\n0 Author: Design-Bricks\\\\n0 \/\/ Pixel layer only. Add suitable baseplates and frame.\\\\n`+p.pixels.map((id,i)=>`1 ${map.get(id).ldraw} ${i%p.width*20} 0 ${Math.floor(i\/p.width)*20} 1 0 0 0 1 0 0 0 1 ${piece.ldraw}`).join('\\\\n')+'\\\\n';}\\nfunction parseInventory(text,part,data){\\n const lines=text.trim().replace(\/^\\\\uFEFF\/,'').split(\/\\\\r?\\\\n\/),parse=line=>{const row=[];let value='',quote=false;for(let i=0;i<line.length;i++){if(line[i]==='\\\"'){if(quote&&line[i+1]==='\\\"'){value+='\\\"';i++;}else quote=!quote;}else if(line[i]===','&&!quote){row.push(value.trim());value='';}else value+=line[i];}if(quote)throw Error('Unclosed CSV quote.');row.push(value.trim());return row;};\\n const head=parse(lines.shift()).map(s=>s.toLowerCase()),ci=head.findIndex(s=>['colour_id','color_id'].includes(s)),qi=head.indexOf('quantity'),pi=head.indexOf('part_id');if(ci<0||qi<0)throw Error('CSV columns must include colour_id and quantity.');\\n const out={},allowed=data.parts.find(p=>p.id===part).colors;\\n for(const line of lines){if(!line.trim())continue;const cells=parse(line);if(pi>=0&&cells[pi]!==part)continue;const id=Number(cells[ci]),q=Number(cells[qi]);if(!allowed.includes(id)||!Number.isInteger(q)||q<0||q>1000000||cells[qi]==='')throw Error('The stock file contains an unavailable colour or invalid quantity.');out[id]=(out[id]||0)+q;}\\n if(!Object.keys(out).length)throw Error('No matching pieces found for the selected part.');return out;\\n}\\n\\nonmessage=function(e){const d=e.data;try{postMessage({job:d.job,pixels:convertMosaic(d.samples,d.width,d.height,d.colors,d.options,d.overrides)});}catch(error){postMessage({job:d.job,error:error.message});}};\";\nconst root=document.getElementById('dbstudio');\nif(!root)throw Error('Mosaic Studio markup is missing.');\nconst $=id=>root.querySelector('#ms-'+id),qa=selector=>[...root.querySelectorAll(selector)],byId=new Map(DATA.colors.map(c=>[c.id,c]));\nconst canvas=$('canvas'),ctx=canvas.getContext('2d'),stage=$('stage');\nlet state={width:48,height:48,part:'98138',title:'Sunset study',pixels:[],selected:DATA.parts[0].colors.slice(),inventory:{},stocks:{},overrides:{},opts:{brightness:0,contrast:0,saturation:0,hue:0,gamma:1,sharpen:0,filter:'natural',limit:16,dither:'none',sampling:'area',placement:'cover',cropzoom:1,cropx:0,cropy:0,rotation:0,flip:false,stockMode:false}};\nlet source=null,sourceURL=null,history=[],future=[],lastValid=null,active=5,tool='paint',view='mosaic',panel='palette',zoom=1,pan={x:0,y:0},grid=false,brushSize=1,tolerance=12,mask=null,keyboardCell=0,keyboardFocus=false,drag=null,cropDrag=null,sampleIndex=0,busy=false,job=0,worker=null,convertTimer=null,settingTimer=null,settingGroup=false,detailTimer=null,gl3d=null,glDirty=true,orbit={yaw:-.35,pitch:1.05,zoom:1},section=0,built=new Set(),downloads=new Map(),toastTimer=null,previousBodyOverflow=null;\nconst part=()=>DATA.parts.find(p=>p.id===state.part);\nconst cloneState=()=>({...state,pixels:state.pixels.slice(),selected:state.selected.slice(),inventory:{...state.inventory},overrides:{...state.overrides},stocks:{...Object.fromEntries(Object.entries(state.stocks||{}).map(([k,v])=>[k,{...v}])),[state.part]:{...state.inventory}},opts:{...state.opts}});\nconst snapshot=()=>({state:cloneState(),source,sourceURL});\nfunction checkpoint(){history.push(snapshot());if(history.length>25)history.shift();future=[];historyButtons();}\nfunction historyButtons(){$('undo').disabled=!history.length;$('redo').disabled=!future.length;}\nfunction restore(s){state=s.state;source=s.source;sourceURL=s.sourceURL;job++;clearTimeout(convertTimer);setBusy(false);mask=null;keyboardCell=clamp(keyboardCell,0,state.pixels.length-1);lastValid={width:state.width,height:state.height,part:state.part,pixels:state.pixels.slice(),overrides:{...state.overrides}};glDirty=true;syncControls();refresh();}\nfunction undoRedo(from,to){if(!from.length)return;to.push(snapshot());restore(from.pop());notify('Design restored.');}\nfunction notify(text,error=false){$('message').textContent=text;$('message').classList.toggle('ms-error',error);$('state').lastChild.textContent=' '+(error?'Needs attention':busy?'Updating mosaic':'All changes applied');}\nfunction toast(text){$('toast').textContent=text;$('toast').hidden=false;clearTimeout(toastTimer);toastTimer=setTimeout(()=>$('toast').hidden=true,2300);}\nfunction setBusy(value){busy=value;$('busy').hidden=!value;root.setAttribute('aria-busy',String(value));$('state').innerHTML='<i><\/i>'+ (value?'Updating mosaic':'All changes applied');qa('[data-action=\"export\"],[data-action=\"save\"],[data-action=\"build\"]').forEach(b=>b.disabled=value);}\nfunction beginSetting(){if(!settingGroup){checkpoint();settingGroup=true;}clearTimeout(settingTimer);settingTimer=setTimeout(()=>settingGroup=false,500);}\nfunction createWorker(){try{const url=URL.createObjectURL(new Blob([WORKER_SOURCE],{type:'text\/javascript'}));worker=new Worker(url);URL.revokeObjectURL(url);worker.onmessage=e=>receive(e.data);worker.onerror=()=>{worker.terminate();worker=null;queueConvert(0);};}catch(_){worker=null;}}\nfunction receive(result){if(result.job!==job)return;if(result.error){setBusy(false);if(lastValid&&(lastValid.width!==state.width||lastValid.height!==state.height||lastValid.part!==state.part)){Object.assign(state,{...lastValid,pixels:lastValid.pixels.slice(),overrides:{...lastValid.overrides}});state.selected=state.selected.filter(id=>part().colors.includes(id));if(!state.selected.length)state.selected=part().colors.slice();syncControls();}notify(result.error,true);draw();return;}\n state.pixels=result.pixels;lastValid={width:state.width,height:state.height,part:state.part,pixels:state.pixels.slice(),overrides:{...state.overrides}};setBusy(false);mask=null;glDirty=true;keyboardCell=clamp(keyboardCell,0,state.pixels.length-1);refresh();notify(state.opts.stockMode?'Mosaic updated within your available stock.':'Mosaic updated. Every setting is applied automatically.');}\nfunction transformedCanvas(width,height,sampling=state.opts.sampling){\n const factor=sampling==='area'?4:1,c=document.createElement('canvas');c.width=width*factor;c.height=height*factor;const g=c.getContext('2d');g.fillStyle='#fff';g.fillRect(0,0,c.width,c.height);\n if(source){const o=state.opts,rotated=o.rotation%180!==0,sw=rotated?source.height:source.width,sh=rotated?source.width:source.height,scale=(o.placement==='cover'?Math.max(c.width\/sw,c.height\/sh):Math.min(c.width\/sw,c.height\/sh))*o.cropzoom,excessX=Math.abs(sw*scale-c.width),excessY=Math.abs(sh*scale-c.height);g.translate(c.width\/2+o.cropx\/100*excessX\/2,c.height\/2+o.cropy\/100*excessY\/2);g.rotate(o.rotation*Math.PI\/180);g.scale(o.flip?-scale:scale,scale);g.imageSmoothingEnabled=sampling!=='nearest';g.imageSmoothingQuality='high';g.drawImage(source,-source.width\/2,-source.height\/2);}\n if(factor===1)return c;const input=g.getImageData(0,0,c.width,c.height).data,out=document.createElement('canvas');out.width=width;out.height=height;const og=out.getContext('2d'),image=og.createImageData(width,height);\n for(let y=0;y<height;y++)for(let x=0;x<width;x++){let rr=0,gg=0,bb=0;for(let yy=0;yy<4;yy++)for(let xx=0;xx<4;xx++){const i=((y*4+yy)*c.width+x*4+xx)*4;rr+=input[i];gg+=input[i+1];bb+=input[i+2];}const k=(y*width+x)*4;image.data[k]=rr\/16;image.data[k+1]=gg\/16;image.data[k+2]=bb\/16;image.data[k+3]=255;}og.putImageData(image,0,0);return out;\n}\nfunction queueConvert(delay=100){clearTimeout(convertTimer);job++;setBusy(true);drawThumbnail();if($('crop-dialog').open)drawCrop();convertTimer=setTimeout(convert,delay);}\nfunction convert(){\n const chosen=DATA.colors.filter(c=>state.selected.includes(c.id)&&part().colors.includes(c.id));if(!chosen.length){receive({job,error:'Choose at least one palette colour.'});return;}\n const c=transformedCanvas(state.width,state.height),samples=adjusted(c.getContext('2d').getImageData(0,0,c.width,c.height).data,state.width,state.height,state.opts);\n const options={limit:state.opts.limit,dither:state.opts.dither,inventory:state.opts.stockMode?state.inventory:null};\n if(worker)worker.postMessage({job,samples,width:state.width,height:state.height,colors:chosen,options,overrides:{...state.overrides}},[samples.buffer]);else {const ticket=job,w=state.width,h=state.height;setTimeout(()=>{try{receive({job:ticket,pixels:convertMosaic(samples,w,h,chosen,options,state.overrides)});}catch(e){receive({job:ticket,error:e.message});}},0);}\n}\nfunction readDimension(){const w=Number($('width').value),h=Number($('height').value);if(!Number.isInteger(w)||!Number.isInteger(h)||w<8||h<8||w>128||h>128){notify('Choose whole-number dimensions between 8 and 128 studs.',true);return;}if(w===state.width&&h===state.height)return;beginSetting();state.width=w;state.height=h;state.overrides={};mask=null;zoom=1;pan={x:0,y:0};built.clear();queueConvert();}\nfunction selectPart(id){if(id===state.part)return;checkpoint();state.stocks[state.part]={...state.inventory};state.part=id;state.inventory={...(state.stocks[id]||{})};state.overrides=Object.fromEntries(Object.entries(state.overrides).filter(([,colour])=>part().colors.includes(colour)));state.selected=part().colors.slice();if(!part().colors.includes(active))active=part().colors[0];mask=null;glDirty=true;syncControls();renderPalette();queueConvert(0);}\nfunction syncControls(){\n $('width').value=state.width;$('height').value=state.height;$('part').value=state.part;$('title').textContent=state.title;\n for(const name of ['brightness','contrast','saturation','hue','gamma','sharpen','limit','dither','sampling','placement','cropzoom','cropx','cropy']){const el=$(name);if(el)el.value=state.opts[name];updateOutput(name);}\n $('stock-mode').checked=state.opts.stockMode;$('limit').max=part().colors.length;$('limit').disabled=state.opts.stockMode;$('dither').disabled=state.opts.stockMode;\n $('stock-note').textContent=state.opts.stockMode?'Stock mode uses selected colours with available pieces. Colour cap and dithering are paused.':'Enter quantities in the Pieces panel or import your stock.';\n qa('[data-filter]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.filter===state.opts.filter)));qa('[data-size]').forEach(b=>b.setAttribute('aria-pressed',String(+b.dataset.size===state.width&&state.width===state.height)));\n const shape=state.width===state.height?'square':state.width>state.height?'landscape':'portrait';qa('[data-shape]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.shape===shape)));$('part-note').textContent=`Part ${state.part} \u00b7 ${part().colors.length} documented colours`;\n $('palette-foot').textContent=`${part().name} \u00b7 part ${state.part}. Tick colours to include them. Digital colours are approximate.`;historyButtons();\n}\nfunction updateOutput(name){const out=$(name+'-value');if(!out)return;const v=state.opts[name];out.textContent=name==='hue'?v+'\u00b0':name==='gamma'?Number(v).toFixed(1):name==='cropzoom'?Math.round(v*100)+'%':['cropx','cropy'].includes(name)?Math.round(v):String(v);}\nfunction currentCounts(){return new Map(counts(state.pixels));}\nfunction updateStats(){const used=counts(state.pixels);$('stat-grid').textContent=`${state.width} \u00d7 ${state.height}`;$('stat-pieces').textContent=state.pixels.length.toLocaleString();$('stat-colours').textContent=used.length;$('stat-size').textContent=`${(state.width*.8).toFixed(1)} \u00d7 ${(state.height*.8).toFixed(1)}`;$('count-badge').textContent=used.length;const c=byId.get(active);$('brush-colour').querySelector('i').style.background=c.hex;$('brush-colour').querySelector('b').textContent=c.name;}\nfunction refresh(){syncControls();updateStats();renderPalette();renderPieces();updateSelectionButtons();drawThumbnail();draw();historyButtons();}\nfunction renderPalette(){\n const used=currentCounts(),host=$('palette');host.replaceChildren();\n for(const id of part().colors){const c=byId.get(id),wrap=document.createElement('div');wrap.className='ms-swatch'+(id===active?' ms-active':'')+(state.selected.includes(id)?'':' ms-off');wrap.dataset.search=(c.name+' '+id).toLowerCase();wrap.dataset.id=id;const button=document.createElement('button');button.type='button';button.dataset.colour=id;button.setAttribute('aria-label','Paint with '+c.name);button.title=`${c.name} \u00b7 BrickLink ${id}`;const chip=document.createElement('span');chip.className='ms-chip';chip.style.background=c.hex;const name=document.createElement('span');name.className='ms-colour-name';name.textContent=c.name;button.append(chip,name);const label=document.createElement('label'),input=document.createElement('input');input.type='checkbox';input.checked=state.selected.includes(id);input.dataset.include=id;input.setAttribute('aria-label','Use '+c.name+' in conversion');label.append(input,document.createTextNode('#'+id));const badge=document.createElement('em');badge.textContent=used.get(id)||'';wrap.append(button,label,badge);host.append(wrap);}searchColours();\n}\nfunction selectColour(id){active=id;if(view==='original')setView('mosaic');updateStats();qa('.ms-swatch').forEach(el=>el.classList.toggle('ms-active',+el.dataset.id===id));draw();}\nfunction searchColours(){const q=$('colour-search').value.trim().toLowerCase();qa('[data-search]').forEach(el=>el.hidden=!el.dataset.search.includes(q));}\nfunction renderPieces(){\n const needed=currentCounts(),tbody=$('pieces-body'),focused=document.activeElement?.dataset.stock;\n const ids=part().colors.slice().sort((a,b)=>(needed.get(b)||0)-(needed.get(a)||0)||a-b);\n if(tbody.dataset.part!==state.part){tbody.replaceChildren();tbody.dataset.part=state.part;}\n for(const id of ids){const c=byId.get(id),q=needed.get(id)||0;let row=tbody.querySelector('[data-row=\"'+id+'\"]');if(!row){row=document.createElement('tr');row.dataset.row=id;row.dataset.search=(c.name+' '+id).toLowerCase();const name=document.createElement('td'),inner=document.createElement('span'),chip=document.createElement('i');inner.className='ms-table-colour';chip.style.background=c.hex;inner.append(chip,document.createTextNode(c.name+' #'+id));name.append(inner);const need=document.createElement('td'),have=document.createElement('td'),input=document.createElement('input');input.type='number';input.min='0';input.max='1000000';input.step='1';input.dataset.stock=id;input.setAttribute('aria-label','Stock of '+c.name);have.append(input);row.append(name,need,have);tbody.append(row);}row.children[1].textContent=q;row.children[1].classList.toggle('ms-missing',q>(state.inventory[id]||0));const input=row.querySelector('input');if(document.activeElement!==input)input.value=state.inventory[id]||0;if(!focused)tbody.append(row);}\n const total=part().colors.reduce((sum,id)=>sum+(state.inventory[id]||0),0),missing=[...needed].reduce((sum,[id,q])=>sum+Math.max(0,q-(state.inventory[id]||0)),0);$('stock-summary').textContent=`${total.toLocaleString()} in stock \u00b7 ${missing.toLocaleString()} short for this design`;searchColours();\n}\nfunction setPanel(name){panel=name;qa('[data-panel]').forEach(b=>b.setAttribute('aria-selected',String(b.dataset.panel===name)));$('palette-view').hidden=name!=='palette';$('pieces-view').hidden=name!=='pieces';}\nfunction updateSelectionButtons(){$('selection-fill').hidden=!mask;$('selection-clear').hidden=!mask;}\nfunction setTool(name){tool=name;qa('[data-tool]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.tool===name)));$('tolerance-wrap').hidden=name!=='smart';$('stage-hint').textContent=name==='pan'?'Drag to move \u00b7 scroll to zoom':name==='smart'?'Click a connected colour region, then fill your selection':name==='select'?'Drag a rectangle to select pieces':'Scroll to zoom \u00b7 H to move \u00b7 B to paint';canvas.style.cursor=name==='pan'?'grab':name==='pick'?'crosshair':'crosshair';}\nfunction setView(name){view=name;qa('[data-view]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.view===name)));canvas.hidden=name==='3d';$('3d').hidden=name!=='3d';if(name==='3d'){try{if(!gl3d)gl3d=create3D($('3d'),GEOMETRY);glDirty=true;$('stage-hint').textContent='Drag to orbit \u00b7 scroll to zoom \u00b7 Fit to reset';}catch(e){view='mosaic';canvas.hidden=false;$('3d').hidden=true;qa('[data-view]').forEach(b=>b.setAttribute('aria-pressed',String(b.dataset.view==='mosaic')));notify(e.message,true);}}if(view!=='3d')setTool(tool);draw();}\nfunction viewTransform(){const rect=canvas.getBoundingClientRect(),fit=Math.min((rect.width-60)\/state.width,(rect.height-70)\/state.height),scale=Math.max(.5,fit)*zoom;return {scale,x:(rect.width-state.width*scale)\/2+pan.x,y:(rect.height-state.height*scale)\/2+pan.y,width:rect.width,height:rect.height};}\nfunction drawPieces(g,size=1,numbered=false,pieces=state.pixels,w=state.width){const codes=new Map(counts(state.pixels).map(([id],i)=>[id,i+1])),round=part().shape.startsWith('round'),stud=part().shape.includes('stud');for(let i=0;i<pieces.length;i++){const c=byId.get(pieces[i]);if(!c)continue;const x=i%w*size,y=Math.floor(i\/w)*size;g.fillStyle=c.hex;if(round){g.beginPath();g.arc(x+size\/2,y+size\/2,size*.455,0,Math.PI*2);g.fill();}else g.fillRect(x+size*.025,y+size*.025,size*.95,size*.95);if(stud){g.lineWidth=Math.max(.025*size,.03);g.strokeStyle='#0003';g.beginPath();g.arc(x+size\/2,y+size\/2,size*.29,0,Math.PI*2);g.stroke();g.strokeStyle='#fff4';g.beginPath();g.arc(x+size\/2,y+size\/2,size*.27,Math.PI,Math.PI*1.8);g.stroke();}if(numbered){const vals=rgb(c.hex),light=vals[0]*.2126+vals[1]*.7152+vals[2]*.0722>140;g.fillStyle=light?'#111':'#fff';g.font=`bold ${size*.28}px Arial`;g.textAlign='center';g.textBaseline='middle';g.fillText(codes.get(c.id),x+size\/2,y+size*.54);}}}\nfunction draw(){\n if(!state.pixels.length)return;\n if(view==='3d'&&gl3d){if(glDirty){gl3d.update(state,byId);glDirty=false;}gl3d.draw(state,orbit);$('zoom-value').textContent=Math.round(orbit.zoom*100)+'%';return;}\n const t=viewTransform(),dpr=Math.min(devicePixelRatio||1,2);if(!t.width||!t.height)return;canvas.width=Math.round(t.width*dpr);canvas.height=Math.round(t.height*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,t.width,t.height);ctx.save();ctx.translate(t.x,t.y);ctx.scale(t.scale,t.scale);ctx.fillStyle='#24372b';ctx.fillRect(0,0,state.width,state.height);ctx.imageSmoothingEnabled=false;\n if(view==='original')ctx.drawImage(transformedCanvas(state.width,state.height),0,0,state.width,state.height);else{drawPieces(ctx);if(view==='split'){ctx.save();ctx.beginPath();ctx.rect(0,0,state.width\/2,state.height);ctx.clip();ctx.drawImage(transformedCanvas(state.width,state.height),0,0,state.width,state.height);ctx.restore();ctx.fillStyle='#fff';ctx.fillRect(state.width\/2-.07,0,.14,state.height);}}\n if(grid&&t.scale>=5){ctx.strokeStyle='#0003';ctx.lineWidth=.04;ctx.beginPath();for(let x=0;x<=state.width;x++){ctx.moveTo(x,0);ctx.lineTo(x,state.height);}for(let y=0;y<=state.height;y++){ctx.moveTo(0,y);ctx.lineTo(state.width,y);}ctx.stroke();}\n if(mask){ctx.fillStyle='#ffffff66';for(let i=0;i<mask.length;i++)if(mask[i])ctx.fillRect(i%state.width,Math.floor(i\/state.width),1,1);}\n if(keyboardFocus){ctx.strokeStyle='#fff';ctx.lineWidth=.15;ctx.strokeRect(keyboardCell%state.width+.08,Math.floor(keyboardCell\/state.width)+.08,.84,.84);ctx.strokeStyle='#111';ctx.lineWidth=.05;ctx.strokeRect(keyboardCell%state.width+.14,Math.floor(keyboardCell\/state.width)+.14,.72,.72);}ctx.restore();$('zoom-value').textContent=zoom===1?'Fit':Math.round(zoom*100)+'%';\n}\nfunction drawThumbnail(){const c=$('thumbnail'),g=c.getContext('2d');g.fillStyle='#eef3ef';g.fillRect(0,0,c.width,c.height);if(source){const ratio=Math.max(c.width\/source.width,c.height\/source.height);g.drawImage(source,(c.width-source.width*ratio)\/2,(c.height-source.height*ratio)\/2,source.width*ratio,source.height*ratio);}}\nfunction cellAt(e){const r=canvas.getBoundingClientRect(),t=viewTransform(),x=Math.floor((e.clientX-r.left-t.x)\/t.scale),y=Math.floor((e.clientY-r.top-t.y)\/t.scale);return {x,y,index:y*state.width+x,valid:x>=0&&x<state.width&&y>=0&&y<state.height};}\nfunction commitEdit(next,record=true){if(next.every((id,i)=>id===state.pixels[i]))return false;if(state.opts.stockMode&&counts(next).some(([id,q])=>q>(state.inventory[id]||0))){notify('That edit needs more stock. Add pieces or turn stock limits off.',true);return false;}if(record)checkpoint();for(let i=0;i<next.length;i++)if(next[i]!==state.pixels[i])state.overrides[i]=next[i];state.pixels=next;lastValid={width:state.width,height:state.height,part:state.part,pixels:next.slice(),overrides:{...state.overrides}};glDirty=true;updateStats();draw();clearTimeout(detailTimer);detailTimer=setTimeout(()=>{renderPalette();renderPieces();},80);return true;}\nfunction paintCell(index,record=true){if(busy||index<0||index>=state.pixels.length||view!=='mosaic')return;const id=tool==='erase'?1:active;\n if(tool==='pick'){selectColour(state.pixels[index]);return;}if(tool==='smart'){mask=new Uint8Array(state.pixels.length);for(const i of region(state.pixels,state.width,index,tolerance,DATA.colors))mask[i]=1;updateSelectionButtons();draw();notify(`${mask.reduce((n,v)=>n+v,0)} pieces selected. Choose a colour, then Fill selection.`);return;}\n const next=state.pixels.slice();if(tool==='fill'){for(const i of region(state.pixels,state.width,index,0,DATA.colors))if(!mask||mask[i])next[i]=id;}else if(tool==='replace'){const old=state.pixels[index];for(let i=0;i<next.length;i++)if(next[i]===old&&(!mask||mask[i]))next[i]=id;}else{const x=index%state.width,y=Math.floor(index\/state.width),radius=Math.floor(brushSize\/2);for(let dy=-radius;dy<=radius;dy++)for(let dx=-radius;dx<=radius;dx++){const xx=x+dx,yy=y+dy,i=yy*state.width+xx;if(xx>=0&&xx<state.width&&yy>=0&&yy<state.height&&(!mask||mask[i]))next[i]=id;}}\n commitEdit(next,record);\n}\nfunction pointerDown(e){if(e.button!==0&&e.button!==1)return;e.preventDefault();const target=e.currentTarget;target.setPointerCapture(e.pointerId);target.focus({preventScroll:true});keyboardFocus=false;if(view==='3d'){drag={kind:'orbit',x:e.clientX,y:e.clientY,yaw:orbit.yaw,pitch:orbit.pitch};return;}if(tool==='pan'||e.button===1||e.altKey){drag={kind:'pan',x:e.clientX,y:e.clientY,px:pan.x,py:pan.y};return;}const c=cellAt(e);if(!c.valid||busy)return;if(view!=='mosaic'){notify('Switch to Mosaic to edit pieces.');return;}keyboardCell=c.index;if(tool==='select'){drag={kind:'select',start:c};mask=new Uint8Array(state.pixels.length);mask[c.index]=1;updateSelectionButtons();draw();return;}if(['paint','erase'].includes(tool)){checkpoint();drag={kind:'paint',last:c};paintCell(c.index,false);}else paintCell(c.index);}\nfunction pointerMove(e){if(view==='3d'){if(drag?.kind==='orbit'){orbit.yaw=drag.yaw+(e.clientX-drag.x)*.008;orbit.pitch=clamp(drag.pitch+(e.clientY-drag.y)*.008,.15,1.55);draw();}return;}\n const c=cellAt(e);if(c.valid)$('coordinate').textContent=`R${c.y+1} \u00b7 C${c.x+1} \u00b7 ${byId.get(state.pixels[c.index])?.name||''}`;\n if(drag?.kind==='pan'){pan.x=drag.px+e.clientX-drag.x;pan.y=drag.py+e.clientY-drag.y;draw();}else if(drag?.kind==='select'){const x=clamp(c.x,0,state.width-1),y=clamp(c.y,0,state.height-1),start=drag.start;mask.fill(0);for(let yy=Math.min(y,start.y);yy<=Math.max(y,start.y);yy++)for(let xx=Math.min(x,start.x);xx<=Math.max(x,start.x);xx++)mask[yy*state.width+xx]=1;draw();}else if(drag?.kind==='paint'&&c.valid){const prev=drag.last,steps=Math.max(Math.abs(c.x-prev.x),Math.abs(c.y-prev.y));for(let i=1;i<=steps;i++)paintCell(Math.round(prev.y+(c.y-prev.y)*i\/steps)*state.width+Math.round(prev.x+(c.x-prev.x)*i\/steps),false);drag.last=c;}}\nfunction pointerUp(){if(drag?.kind==='paint'){renderPalette();renderPieces();}drag=null;}\nfunction changeZoom(next,e){if(view==='3d'){orbit.zoom=clamp(next,.4,8);draw();return;}const previous=zoom;zoom=clamp(next,.5,24);if(e){const r=canvas.getBoundingClientRect(),x=e.clientX-r.left-canvas.clientWidth\/2-pan.x,y=e.clientY-r.top-canvas.clientHeight\/2-pan.y;pan.x-=x*(zoom\/previous-1);pan.y-=y*(zoom\/previous-1);}draw();}\nfunction fit(){zoom=1;pan={x:0,y:0};orbit={yaw:-.35,pitch:1.05,zoom:1};draw();}\nfunction demo(index=0){const c=document.createElement('canvas');c.width=900;c.height=800;const g=c.getContext('2d');if(index%3===0){const grad=g.createLinearGradient(0,0,0,800);grad.addColorStop(0,'#254e63');grad.addColorStop(.5,'#efbc82');grad.addColorStop(1,'#f5ddb3');g.fillStyle=grad;g.fillRect(0,0,900,800);g.fillStyle='#f6db85';g.beginPath();g.arc(580,270,125,0,Math.PI*2);g.fill();for(const [y,col,points]of [[400,'#8d7880',[0,430,180,280,400,510,640,380,900,490]],[540,'#4c7779',[0,570,220,440,470,610,730,480,900,550]],[660,'#244e57',[0,690,230,590,460,720,690,580,900,690]]]){g.fillStyle=col;g.beginPath();g.moveTo(points[0],points[1]);for(let i=2;i<points.length;i+=2)g.lineTo(points[i],points[i+1]);g.lineTo(900,800);g.lineTo(0,800);g.fill();}return {canvas:c,title:'Sunset study'};}\n if(index%3===1){g.fillStyle='#f7eed7';g.fillRect(0,0,900,800);const colors=['#237841','#E52828','#0055BF','#e9b63d','#183747'];for(let y=0;y<4;y++)for(let x=0;x<5;x++){g.fillStyle=colors[(x+y*2)%5];const xx=70+x*155,yy=80+y*165;if((x+y)%3===0){g.beginPath();g.arc(xx+68,yy+65,65,0,Math.PI*2);g.fill();}else if((x+y)%3===1){g.fillRect(xx,yy,132,132);}else{g.beginPath();g.moveTo(xx,yy+132);g.lineTo(xx+66,yy);g.lineTo(xx+132,yy+132);g.fill();}}return {canvas:c,title:'Shape & rhythm'};}\n const grad=g.createLinearGradient(0,0,900,0);for(const [i,col]of ['#e15445','#eec767','#7ca888','#55a9bb','#4b5791','#b58db5'].entries())grad.addColorStop(i\/5,col);g.fillStyle=grad;g.fillRect(0,0,900,800);for(let i=0;i<8;i++){g.fillStyle=`rgba(255,255,255,${i\/12})`;g.fillRect(0,i*100,900,100);}return {canvas:c,title:'Colour study'};\n}\nfunction loadSample(){checkpoint();const d=demo(++sampleIndex);source=d.canvas;sourceURL=null;state.title=d.title;resetOptions();state.opts.stockMode=false;syncControls();queueConvert(0);}\nfunction resetOptions(){state.overrides={};Object.assign(state.opts,{brightness:0,contrast:0,saturation:0,hue:0,gamma:1,sharpen:0,filter:'natural',placement:'cover',cropzoom:1,cropx:0,cropy:0,rotation:0,flip:false});}\nfunction blank(){checkpoint();job++;clearTimeout(convertTimer);setBusy(false);state.pixels=Array(state.width*state.height).fill(1);state.title='Untitled mosaic';state.opts.stockMode=false;source=document.createElement('canvas');source.width=state.width;source.height=state.height;const g=source.getContext('2d');g.fillStyle='#fff';g.fillRect(0,0,source.width,source.height);sourceURL=null;resetOptions();mask=null;glDirty=true;lastValid={width:state.width,height:state.height,part:state.part,pixels:state.pixels.slice(),overrides:{...state.overrides}};setView('mosaic');refresh();notify('Blank canvas ready. Pick a colour and start drawing.');}\nasync function openImage(file){if(!file)return;if(!['image\/jpeg','image\/png','image\/webp'].includes(file.type)||file.size>20*1024*1024){notify('Choose a JPG, PNG or WebP photo up to 20 MB.',true);return;}let url;try{url=URL.createObjectURL(file);const image=new Image();image.src=url;await image.decode();if(image.naturalWidth*image.naturalHeight>40000000)throw Error('Use a photo smaller than 40 million pixels.');checkpoint();const scale=Math.min(1,1600\/Math.max(image.naturalWidth,image.naturalHeight));source=document.createElement('canvas');source.width=Math.max(1,Math.round(image.naturalWidth*scale));source.height=Math.max(1,Math.round(image.naturalHeight*scale));source.getContext('2d').drawImage(image,0,0,source.width,source.height);sourceURL=null;state.title=file.name.replace(\/\\.[^.]+$\/,'').slice(0,70);resetOptions();fit();setView('mosaic');syncControls();queueConvert(0);}catch(e){notify('Could not open that photo: '+e.message,true);}finally{if(url)URL.revokeObjectURL(url);}}\nfunction openDialog(name){const d=$(name+'-dialog');if(!d.open)d.showModal();}\nfunction drawCrop(){const c=$('crop-canvas'),g=c.getContext('2d');g.fillStyle='#e7eee8';g.fillRect(0,0,c.width,c.height);const scale=Math.min((c.width-60)\/state.width,(c.height-50)\/state.height),w=state.width*scale,h=state.height*scale,x=(c.width-w)\/2,y=(c.height-h)\/2;g.imageSmoothingEnabled=true;g.drawImage(transformedCanvas(state.width*4,state.height*4,'nearest'),x,y,w,h);g.strokeStyle='#237841';g.lineWidth=2;g.strokeRect(x,y,w,h);g.fillStyle='#237841';g.font='12px Arial';g.fillText(`${state.width} \u00d7 ${state.height} studs`,x,y-8);}\nfunction project(includePhoto=true){if(includePhoto&&source&&!sourceURL)sourceURL=source.toDataURL('image\/png');return {...cloneState(),format:'design-bricks-mosaic',version:2,source:includePhoto?sourceURL:null};}\nfunction filename(extension,tail=''){const slug=state.title.toLowerCase().replace(\/[^a-z0-9]+\/g,'-').replace(\/^-|-$\/g,'').slice(0,45)||'mosaic';return `design-bricks-${slug}${tail}.${extension}`;}\nfunction makeLink(type,blob,name){if(downloads.has(type))URL.revokeObjectURL(downloads.get(type));const url=URL.createObjectURL(blob);downloads.set(type,url);const link=$('download-'+type);if(link){link.href=url;link.download=name;link.hidden=false;}return {url,name};}\nfunction directDownload(blob,name){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;root.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),30000);}\nfunction openExports(){if(busy)return;const p=project();$('export-summary').textContent=`${state.width} \u00d7 ${state.height} studs \u00b7 ${state.pixels.length.toLocaleString()} pieces \u00b7 ${counts(state.pixels).length} colours \u00b7 part ${state.part}`;$('export-message').textContent='';for(const type of ['pdf','png']){$('download-'+type).hidden=true;root.querySelector('[data-export=\"'+type+'\"]').hidden=false;}\n makeLink('csv',new Blob([csv(p,DATA)],{type:'text\/csv;charset=utf-8'}),filename('csv','-parts'));makeLink('xml',new Blob([bricklinkXML(p)],{type:'application\/xml'}),filename('xml','-bricklink'));makeLink('ldr',new Blob([ldraw(p,DATA)],{type:'text\/plain;charset=utf-8'}),filename('ldr'));makeLink('json',new Blob([JSON.stringify(p)],{type:'application\/json'}),filename('json'));openDialog('export');}\nfunction prepareExport(type,button){try{const p=project(false);let blob;if(type==='pdf')blob=new Blob([instructionsPDF(p,DATA)],{type:'application\/pdf'});else{const c=document.createElement('canvas');c.width=state.width*28;c.height=state.height*28;const g=c.getContext('2d');g.fillStyle='#24372b';g.fillRect(0,0,c.width,c.height);drawPieces(g,28);const raw=atob(c.toDataURL('image\/png').split(',')[1]),bytes=new Uint8Array(raw.length);for(let i=0;i<raw.length;i++)bytes[i]=raw.charCodeAt(i);blob=new Blob([bytes],{type:'image\/png'});}makeLink(type,blob,filename(type,type==='pdf'?'-instructions':''));button.hidden=true;$('export-message').textContent=`${type.toUpperCase()} ready. Use the download link to save it.`;$('download-'+type).focus({preventScroll:true});}catch(e){$('export-message').textContent='Export failed: '+e.message;}}\nasync function openProject(file){if(!file)return;try{if(file.size>16*1024*1024)throw Error('Project files must be smaller than 16 MB.');const p=validateProject(JSON.parse(await file.text()),DATA);let imageCanvas=null;if(p.source){const image=new Image();image.src=p.source;await image.decode();if(image.naturalWidth*image.naturalHeight>4000000)throw Error('Saved source image is too large.');imageCanvas=document.createElement('canvas');imageCanvas.width=image.naturalWidth;imageCanvas.height=image.naturalHeight;imageCanvas.getContext('2d').drawImage(image,0,0);}checkpoint();const defaults={...state.opts};Object.assign(state,p,{opts:defaults});if(p.opts){const ranges={brightness:[-100,100],contrast:[-100,100],saturation:[-100,100],hue:[-180,180],gamma:[.4,2.4],sharpen:[0,4],limit:[1,58],cropzoom:[1,3],cropx:[-100,100],cropy:[-100,100],rotation:[0,270]};for(const key of Object.keys(defaults)){const value=p.opts[key];if(ranges[key]&&Number.isFinite(value))state.opts[key]=clamp(value,...ranges[key]);else if(key==='flip'&&typeof value==='boolean')state.opts[key]=value;else if(key==='filter'&&['natural','vivid','warm','cool','mono','poster','sketch','invert'].includes(value))state.opts[key]=value;else if(key==='dither'&&['none','floyd','ordered'].includes(value))state.opts[key]=value;else if(key==='sampling'&&['area','nearest'].includes(value))state.opts[key]=value;else if(key==='placement'&&['cover','contain'].includes(value))state.opts[key]=value;}}state.opts.stockMode=false;source=imageCanvas;sourceURL=p.source;if(!source){source=document.createElement('canvas');source.width=p.width;source.height=p.height;const g=source.getContext('2d');p.pixels.forEach((id,i)=>{g.fillStyle=byId.get(id).hex;g.fillRect(i%p.width,Math.floor(i\/p.width),1,1);});}job++;clearTimeout(convertTimer);setBusy(false);lastValid={width:state.width,height:state.height,part:state.part,pixels:state.pixels.slice(),overrides:{...state.overrides}};mask=null;glDirty=true;fit();setView('mosaic');refresh();notify('Project opened. Your saved mosaic and source image are ready.');}catch(e){notify('Could not open project: '+e.message,true);}}\nasync function importStock(file){if(!file)return;try{if(file.size>2*1024*1024)throw Error('Stock files must be smaller than 2 MB.');const text=await file.text();let stock;if(\/^\\s*<\/.test(text)){const xml=new DOMParser().parseFromString(text,'application\/xml');if(xml.querySelector('parsererror'))throw Error('Invalid XML.');stock={};for(const item of xml.querySelectorAll('ITEM')){if(item.querySelector('ITEMID')?.textContent.trim()!==state.part)continue;const id=Number(item.querySelector('COLOR')?.textContent),q=Number(item.querySelector('QTY,MINQTY')?.textContent);if(!part().colors.includes(id)||!Number.isInteger(q)||q<0||q>1000000)throw Error('Invalid part colour or quantity.');stock[id]=(stock[id]||0)+q;}if(!Object.keys(stock).length)throw Error('No matching pieces for part '+state.part+'.');}else stock=parseInventory(text,state.part,DATA);checkpoint();state.inventory=stock;renderPieces();setPanel('pieces');if(state.opts.stockMode)queueConvert();notify('Stock imported. Enable \u201cUse only my available pieces\u201d to fit the design to these quantities.');}catch(e){notify('Could not import stock: '+e.message,true);}}\nfunction drawSection(){const cols=Math.ceil(state.width\/16),rows=Math.ceil(state.height\/16),total=cols*rows;section=clamp(section,0,total-1);const sx=section%cols,sy=Math.floor(section\/cols),x0=sx*16,y0=sy*16,w=Math.min(16,state.width-x0),h=Math.min(16,state.height-y0),pieces=[];for(let y=0;y<h;y++)for(let x=0;x<w;x++)pieces.push(state.pixels[(y0+y)*state.width+x0+x]);const c=$('build-canvas'),g=c.getContext('2d'),size=34,ox=(640-w*size)\/2,oy=(640-h*size)\/2;g.fillStyle='#f1f5f2';g.fillRect(0,0,640,640);g.save();g.translate(ox,oy);drawPieces(g,size,true,pieces,w);g.restore();g.fillStyle='#405b49';g.font='10px Arial';g.textAlign='center';for(let x=0;x<w;x++)g.fillText(x0+x+1,ox+x*size+size\/2,oy-8);g.textAlign='right';for(let y=0;y<h;y++)g.fillText(y0+y+1,ox-9,oy+y*size+size*.6);$('section-title').textContent=`Section ${section+1} of ${total}`;$('section-coordinates').textContent=`Rows ${y0+1}\u2013${y0+h} \u00b7 Columns ${x0+1}\u2013${x0+w}`;$('section-done').checked=built.has(section);$('build-progress').textContent=`${built.size} of ${total} sections marked as built`;const map=$('section-map');map.style.gridTemplateColumns=`repeat(${cols},1fr)`;map.replaceChildren();for(let i=0;i<total;i++){const b=document.createElement('button');b.type='button';b.dataset.section=i;b.textContent=i+1;b.setAttribute('aria-pressed',String(i===section));b.classList.toggle('ms-done',built.has(i));map.append(b);}const key=$('section-key'),codes=new Map(counts(state.pixels).map(([id],i)=>[id,i+1]));key.replaceChildren();for(const [id,q]of counts(pieces)){const c=byId.get(id),row=document.createElement('div'),chip=document.createElement('i'),label=document.createElement('span');chip.style.background=c.hex;label.textContent=`${codes.get(id)} \u00b7 ${c.name} (${q})`;row.append(chip,label);key.append(row);}}\nfunction toggleExpanded(){const expanded=root.classList.toggle('ms-expanded');const button=root.querySelector('[data-action=fullscreen]');button.setAttribute('aria-pressed',String(expanded));button.innerHTML=button.querySelector('svg').outerHTML+' '+(expanded?'Exit full screen':'Expand');if(expanded){previousBodyOverflow=document.body.style.overflow;document.body.style.overflow='hidden';}else document.body.style.overflow=previousBodyOverflow||'';requestAnimationFrame(draw);}\nconst actions={'reset-edits':()=>{checkpoint();state.overrides={};queueConvert();},sample:loadSample,blank,undo:()=>undoRedo(history,future),redo:()=>undoRedo(future,history),crop:()=>{checkpoint();drawCrop();openDialog('crop');},export:openExports,save:()=>directDownload(new Blob([JSON.stringify(project())],{type:'application\/json'}),filename('json')),build:()=>{section=0;drawSection();openDialog('build');},about:()=>openDialog('about'),fullscreen:toggleExpanded,'zoom-in':()=>changeZoom((view==='3d'?orbit.zoom:zoom)*1.3),'zoom-out':()=>changeZoom((view==='3d'?orbit.zoom:zoom)\/1.3),fit,'reset-adjust':()=>{checkpoint();Object.assign(state.opts,{brightness:0,contrast:0,saturation:0,hue:0,gamma:1,sharpen:0,filter:'natural'});syncControls();queueConvert();},rotate:()=>{state.opts.rotation=(state.opts.rotation+90)%360;queueConvert();},flip:()=>{state.opts.flip=!state.opts.flip;queueConvert();},'reset-crop':()=>{Object.assign(state.opts,{rotation:0,flip:false,cropzoom:1,cropx:0,cropy:0});syncControls();queueConvert();},'selection-fill':()=>{if(mask){const next=state.pixels.map((id,i)=>mask[i]?active:id);commitEdit(next);mask=null;updateSelectionButtons();draw();}},'selection-clear':()=>{mask=null;updateSelectionButtons();draw();},'stock-from-design':()=>{checkpoint();state.inventory=Object.fromEntries(counts(state.pixels));renderPieces();notify('Stock quantities copied from this design.');},'clear-stock':()=>{checkpoint();state.inventory={};renderPieces();if(state.opts.stockMode)queueConvert();},'section-prev':()=>{section--;drawSection();},'section-next':()=>{section++;drawSection();}};\nroot.addEventListener('click',e=>{const el=e.target.closest('button,label,a');if(!el||!root.contains(el))return;if(el.matches('[data-close]')){el.closest('dialog').close();return;}if(el.dataset.action){actions[el.dataset.action]?.();return;}if(el.dataset.tool){setTool(el.dataset.tool);return;}if(el.dataset.view){setView(el.dataset.view);return;}if(el.dataset.panel){setPanel(el.dataset.panel);return;}if(el.dataset.colour){selectColour(+el.dataset.colour);return;}if(el.dataset.filter){checkpoint();state.opts.filter=el.dataset.filter;syncControls();queueConvert();return;}if(el.dataset.palette){checkpoint();const p=el.dataset.palette;state.selected=p==='all'?part().colors.slice():p==='used'?[...new Set(state.pixels)]:part().colors.filter(id=>p==='mono'?[1,11,9,10,85,86,99].includes(id):[1,2,11,28,55,59,68,69,80,85,86,88,90,120,150,155].includes(id));renderPalette();queueConvert();return;}if(el.dataset.size){checkpoint();const n=+el.dataset.size,shape=state.width===state.height?'square':state.width>state.height?'landscape':'portrait';state.width=shape==='portrait'?Math.max(8,Math.round(n*2\/3)):n;state.height=shape==='landscape'?Math.max(8,Math.round(n*2\/3)):n;state.overrides={};fit();built.clear();syncControls();queueConvert();return;}if(el.dataset.shape){checkpoint();const n=Math.max(state.width,state.height),shape=el.dataset.shape;state.width=shape==='portrait'?Math.max(8,Math.round(n*2\/3)):n;state.height=shape==='landscape'?Math.max(8,Math.round(n*2\/3)):n;state.overrides={};fit();built.clear();syncControls();queueConvert();return;}if(el.dataset.export){prepareExport(el.dataset.export,el);return;}if(el.dataset.section!==undefined){section=+el.dataset.section;drawSection();}});\nroot.addEventListener('input',e=>{const el=e.target;if(el.id==='ms-width'||el.id==='ms-height'){readDimension();return;}if(el.id==='ms-title'){state.title=el.textContent.trim().slice(0,80)||'My mosaic';return;}if(el.id==='ms-colour-search'){searchColours();return;}if(el.dataset.stock){const n=Number(el.value);if(Number.isInteger(n)&&n>=0&&n<=1000000){state.inventory[+el.dataset.stock]=n;renderPieces();if(state.opts.stockMode)queueConvert(600);}return;}if(el.dataset.include){beginSetting();const id=+el.dataset.include;state.selected=el.checked?[...new Set([...state.selected,id])]:state.selected.filter(x=>x!==id);el.closest('.ms-swatch').classList.toggle('ms-off',!el.checked);queueConvert();return;}const id=el.id.replace('ms-','');if(['brightness','contrast','saturation','hue','gamma','sharpen','limit','cropzoom','cropx','cropy'].includes(id)){beginSetting();state.opts[id]=Number(el.value);updateOutput(id);queueConvert();}if(id==='brush'){brushSize=+el.value;$('brush-value').textContent=brushSize;}if(id==='tolerance'){tolerance=+el.value;$('tolerance-value').textContent=tolerance;}});\nroot.addEventListener('change',e=>{const el=e.target,id=el.id;if(id==='ms-photo'){openImage(el.files[0]);el.value='';}else if(id==='ms-project-file'){openProject(el.files[0]);el.value='';}else if(id==='ms-stock-file'){importStock(el.files[0]);el.value='';}else if(id==='ms-width'||id==='ms-height')readDimension();else if(id==='ms-part')selectPart(el.value);else if(['ms-dither','ms-sampling','ms-placement'].includes(id)){beginSetting();state.opts[id.slice(3)]=el.value;queueConvert();}else if(id==='ms-stock-mode'){checkpoint();state.opts.stockMode=el.checked;syncControls();if(el.checked)setPanel('pieces');queueConvert();}else if(id==='ms-grid'){grid=el.checked;draw();}else if(id==='ms-section-done'){if(el.checked)built.add(section);else built.delete(section);drawSection();}});\nfor(const c of [canvas,$('3d')]){c.addEventListener('pointerdown',pointerDown);c.addEventListener('pointermove',pointerMove);c.addEventListener('pointerup',pointerUp);c.addEventListener('pointercancel',pointerUp);c.addEventListener('lostpointercapture',pointerUp);c.addEventListener('wheel',e=>{e.preventDefault();changeZoom((view==='3d'?orbit.zoom:zoom)*Math.exp(-e.deltaY*.0015),e);},{passive:false});}\ncanvas.addEventListener('blur',()=>{keyboardFocus=false;draw();});canvas.addEventListener('keydown',e=>{let handled=true;const x=keyboardCell%state.width,y=Math.floor(keyboardCell\/state.width);if(e.key==='ArrowLeft')keyboardCell=y*state.width+Math.max(0,x-1);else if(e.key==='ArrowRight')keyboardCell=y*state.width+Math.min(state.width-1,x+1);else if(e.key==='ArrowUp')keyboardCell=Math.max(0,y-1)*state.width+x;else if(e.key==='ArrowDown')keyboardCell=Math.min(state.height-1,y+1)*state.width+x;else if(e.code==='Space')paintCell(keyboardCell);else handled=false;if(handled){e.preventDefault();keyboardFocus=true;draw();}});\nroot.addEventListener('keydown',e=>{if(e.target.matches('input,select,textarea,[contenteditable]')||root.querySelector('dialog[open]'))return;if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='z'){e.preventDefault();e.shiftKey?undoRedo(future,history):undoRedo(history,future);return;}if(e.key==='Escape'&&root.classList.contains('ms-expanded')){toggleExpanded();return;}if(!e.ctrlKey&&!e.metaKey){const tools={h:'pan',b:'paint',f:'fill',i:'pick',r:'replace',s:'select',w:'smart',e:'erase'};if(tools[e.key.toLowerCase()])setTool(tools[e.key.toLowerCase()]);if(e.key==='+'||e.key==='=')changeZoom(zoom*1.3);if(e.key==='-')changeZoom(zoom\/1.3);if(e.key==='0')fit();}});\nconst cropCanvas=$('crop-canvas');cropCanvas.addEventListener('pointerdown',e=>{cropCanvas.setPointerCapture(e.pointerId);cropDrag={x:e.clientX,y:e.clientY,cx:state.opts.cropx,cy:state.opts.cropy};});cropCanvas.addEventListener('pointermove',e=>{if(!cropDrag)return;state.opts.cropx=clamp(cropDrag.cx+(e.clientX-cropDrag.x)\/cropCanvas.clientWidth*200,-100,100);state.opts.cropy=clamp(cropDrag.cy+(e.clientY-cropDrag.y)\/cropCanvas.clientHeight*200,-100,100);$('cropx').value=state.opts.cropx;$('cropy').value=state.opts.cropy;updateOutput('cropx');updateOutput('cropy');queueConvert();});cropCanvas.addEventListener('pointerup',()=>cropDrag=null);cropCanvas.addEventListener('pointercancel',()=>cropDrag=null);\nstage.addEventListener('dragover',e=>{e.preventDefault();$('drop-overlay').hidden=false;});stage.addEventListener('dragleave',e=>{if(!stage.contains(e.relatedTarget))$('drop-overlay').hidden=true;});stage.addEventListener('drop',e=>{e.preventDefault();$('drop-overlay').hidden=true;openImage(e.dataTransfer.files[0]);});\nqa('dialog').forEach(d=>d.addEventListener('click',e=>{if(e.target===d){const r=d.getBoundingClientRect();if(e.clientX<r.left||e.clientX>r.right||e.clientY<r.top||e.clientY>r.bottom)d.close();}}));\nfor(const p of DATA.parts){const option=document.createElement('option');option.value=p.id;option.textContent=`${p.name} \u00b7 ${p.id}`;$('part').append(option);}\nconst media=matchMedia('(max-width:760px)');function compact(){if(media.matches)$('stage').closest('.ms-layout').querySelector('.ms-controls').open=false;else root.querySelector('.ms-controls').open=true;}media.addEventListener('change',compact);compact();\nnew ResizeObserver(()=>draw()).observe(stage);createWorker();const first=demo();source=first.canvas;syncControls();renderPalette();queueConvert(0);root.dataset.ready='true';\n\n})();<\/script>\n<!-- DESIGN-BRICKS NATIVE STUDIO END --><style id=\"bu-page-slogan-style\">\n.bu-stud-closing.bu-cta-band{position:relative;width:100%;margin-top:24px!important;background:var(--cta-bg)!important;color:var(--cta-ink)!important;min-height:0!important;font-family:Montserrat,Arial,sans-serif;overflow:visible}.bu-stud-closing.bu-cta-band,.bu-stud-closing.bu-cta-band *{box-sizing:border-box}.bu-stud-closing.bu-cta-band:before{content:\"\";position:absolute;left:0;right:0;bottom:100%;height:24px;background:var(--cta-bg)!important;-webkit-mask-image:url(\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAAyCAYAAACqECmXAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA\/\/8AAAKgBAABAAAA9AEAAAOgBAABAAAAMgAAAAAAAAAaX4ZAAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFQmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTAyLTE3PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUhCbkN0NVJacyZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBR0tUNEtfZV9vJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O0JBR0tUNnRGa29ZJnF1b3Q7fTwvQXR0cmliOkRhdGE+CiAgICAgPEF0dHJpYjpFeHRJZD5iYzIyOTU2OS0yMDk5LTRiMWMtOGQ0Zi03NTFlZDM1ODc3MjE8L0F0dHJpYjpFeHRJZD4KICAgICA8QXR0cmliOkZiSWQ+NTI1MjY1OTE0MTc5NTgwPC9BdHRyaWI6RmJJZD4KICAgICA8QXR0cmliOlRvdWNoVHlwZT4yPC9BdHRyaWI6VG91Y2hUeXBlPgogICAgPC9yZGY6bGk+CiAgIDwvcmRmOlNlcT4KICA8L0F0dHJpYjpBZHM+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmRjPSdodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyc+CiAgPGRjOnRpdGxlPgogICA8cmRmOkFsdD4KICAgIDxyZGY6bGkgeG1sOmxhbmc9J3gtZGVmYXVsdCc+VW50aXRsZWQgZGVzaWduIC0gMTwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzp0aXRsZT4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4KICA8cGRmOkF1dGhvcj5FcmVuIENvc2t1bjwvcGRmOkF1dGhvcj4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6eG1wPSdodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvJz4KICA8eG1wOkNyZWF0b3JUb29sPkNhbnZhIGRvYz1EQUhCbkN0NVJacyB1c2VyPVVBR0tUNEtfZV9vIGJyYW5kPUJBR0tUNnRGa29ZPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic\/PtKkNWgAAAbWSURBVHic7ZzNr11jFIevuqVNVKXfEaSVfmkrEf8BIukEiTARUx0ghgbiLzCSiEF9jMpAxEBjgDBgIkLSqlaLjqTaMiFBFb3nZ713v6t73ZMr2vTe867e\/TzJL9sddq31rmefffYxNQUAAAAAAAAAAAAAAAAAAACpkXQNWfy07vPVSOueDSWt+3w10rpnQ0nrPl9VULTJQZ0vHeZyclDry4d6TQZm8zLwQtn1Oss6yxbLjtFodAe58pRa1pqus7+vH687zE+Yy2WWVZZNltuZzQWby52WbZZbLWt8Nlme\/0+sD3tz0ebT9+baUuPxusMY4XHGtOU2y2OW\/ZZPraBf2fWo5Ri5opQaHi41tbxqedyy2QcU5scPrs3hjfbf91iet7xt+cxyJEFfl0LKbH5p+dDyUplNq\/ct4jHnJaFO5JvrmX6tnvHDCfq6FOJ78xN1TipuKo6abt33tNRDWz75PGh513LaDvQ5u84IFppRre1py3uWRyyrxdL8T6w2Gy3P1JvLXy1\/NeveEsbqe8Euv1tOWV633K3uJp\/ZnAd1e7Oc3XKGy1k+Y\/mzlLJJA5c2M2FvFkcVVxVnMZuO+keZpTD7LCfqofYCXqh\/z5Arz1g9fYF+b3nSsjr2ZOiE2bzZ8oLV6kd1i\/JiHZnNhZ\/NWtNS5z8sH1vutyxvPQ+ZCLNZZP605SR7c\/Fns\/7te7O46gm7roo9GSxhKMsd+AOWb+tA\/lMTizoiC5JYU69zoUi93OXz\/dDUnNm8yfKc5Ww4yMzlIs9mWKBlPj+y3NV6JrKgue8ZPWo56bPJfC7+bGru3jyhzl3TsTeDJAxmedngoB\/gcIfugYXlYm3HlsD7lm2xN0NF3aPMay171d3siLmcCPPN5m+WFy3rvDet56Ml6vdmeYnwA9UbIM0VOCw8c2ZTvasOWrbE3gwO9UO5wrLPCvNTKBRLc\/EZH86yDH62PGVZGXs0NNTPZnlb+BV130nO94kHFofx2Sx8Z7lXA39BTv1srlR3VsuZnWFvTozx2RxVd5Wvi1fEHg0K9YO5wfKmupeMxpcmLC7jj5NKD8rb25tij4aG+tncbYf1UK2VfwXEXE6G8aX5i12ftdwQezQ01M9m+dnkO5a\/xd6cNON787zN5xt23RB7NCjUD+ZWyxezVQovHcBE8eEslJ9n7Ig9GhrqPgWW35vfZzP5Q60LMm+Df\/osb76\/bFnjPWo9Jy1Qvze3qzurszUSs9mCmfAE6XPL1tijQeH\/6Pqj\/WNeIDGYLYhC\/8ayJ\/ZoaKj\/\/yE8pO4nKhI3mq3wpVk+BR2w63rvUes5aYF6oe8Se7M1cW+WXuyKPRoUQuiZiIN53HJn7NGQUL8wl6t7g\/hsrQtCb0P8OugtDfmx5tTcr4PU3Xx7jdibk2dc6LtjjwaFEHom4mCWn2EgdISeBZ9NF\/rG2KehIYSeifEnmwgdoacAoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO6oH8ydlqO1KAxmG+JgHrfsiT0aGuXfbZm2m82H7Xqm1gWht8Fn87z144Bd13uPWs9JC4TQMxH35rHy4TT2aFCoH8xtVohDs9UZjS6IwWzBqNa+cMSyM\/ZoaKgT+jLLXsupWheWZhtml6bN5zm77res9R61npMWaO4Hoa9nC8TebMXFvVkdtj32qAX\/AjNYiqbkFMcOAAAAAElFTkSuQmCC\");mask-image:url(\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAAyCAYAAACqECmXAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA\/\/8AAAKgBAABAAAA9AEAAAOgBAABAAAAMgAAAAAAAAAaX4ZAAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFQmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTAyLTE3PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUhCbkN0NVJacyZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBR0tUNEtfZV9vJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O0JBR0tUNnRGa29ZJnF1b3Q7fTwvQXR0cmliOkRhdGE+CiAgICAgPEF0dHJpYjpFeHRJZD5iYzIyOTU2OS0yMDk5LTRiMWMtOGQ0Zi03NTFlZDM1ODc3MjE8L0F0dHJpYjpFeHRJZD4KICAgICA8QXR0cmliOkZiSWQ+NTI1MjY1OTE0MTc5NTgwPC9BdHRyaWI6RmJJZD4KICAgICA8QXR0cmliOlRvdWNoVHlwZT4yPC9BdHRyaWI6VG91Y2hUeXBlPgogICAgPC9yZGY6bGk+CiAgIDwvcmRmOlNlcT4KICA8L0F0dHJpYjpBZHM+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmRjPSdodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyc+CiAgPGRjOnRpdGxlPgogICA8cmRmOkFsdD4KICAgIDxyZGY6bGkgeG1sOmxhbmc9J3gtZGVmYXVsdCc+VW50aXRsZWQgZGVzaWduIC0gMTwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzp0aXRsZT4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4KICA8cGRmOkF1dGhvcj5FcmVuIENvc2t1bjwvcGRmOkF1dGhvcj4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6eG1wPSdodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvJz4KICA8eG1wOkNyZWF0b3JUb29sPkNhbnZhIGRvYz1EQUhCbkN0NVJacyB1c2VyPVVBR0tUNEtfZV9vIGJyYW5kPUJBR0tUNnRGa29ZPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic\/PtKkNWgAAAbWSURBVHic7ZzNr11jFIevuqVNVKXfEaSVfmkrEf8BIukEiTARUx0ghgbiLzCSiEF9jMpAxEBjgDBgIkLSqlaLjqTaMiFBFb3nZ713v6t73ZMr2vTe867e\/TzJL9sddq31rmefffYxNQUAAAAAAAAAAAAAAAAAAACpkXQNWfy07vPVSOueDSWt+3w10rpnQ0nrPl9VULTJQZ0vHeZyclDry4d6TQZm8zLwQtn1Oss6yxbLjtFodAe58pRa1pqus7+vH687zE+Yy2WWVZZNltuZzQWby52WbZZbLWt8Nlme\/0+sD3tz0ebT9+baUuPxusMY4XHGtOU2y2OW\/ZZPraBf2fWo5Ri5opQaHi41tbxqedyy2QcU5scPrs3hjfbf91iet7xt+cxyJEFfl0LKbH5p+dDyUplNq\/ct4jHnJaFO5JvrmX6tnvHDCfq6FOJ78xN1TipuKo6abt33tNRDWz75PGh513LaDvQ5u84IFppRre1py3uWRyyrxdL8T6w2Gy3P1JvLXy1\/NeveEsbqe8Euv1tOWV633K3uJp\/ZnAd1e7Oc3XKGy1k+Y\/mzlLJJA5c2M2FvFkcVVxVnMZuO+keZpTD7LCfqofYCXqh\/z5Arz1g9fYF+b3nSsjr2ZOiE2bzZ8oLV6kd1i\/JiHZnNhZ\/NWtNS5z8sH1vutyxvPQ+ZCLNZZP605SR7c\/Fns\/7te7O46gm7roo9GSxhKMsd+AOWb+tA\/lMTizoiC5JYU69zoUi93OXz\/dDUnNm8yfKc5Ww4yMzlIs9mWKBlPj+y3NV6JrKgue8ZPWo56bPJfC7+bGru3jyhzl3TsTeDJAxmedngoB\/gcIfugYXlYm3HlsD7lm2xN0NF3aPMay171d3siLmcCPPN5m+WFy3rvDet56Ml6vdmeYnwA9UbIM0VOCw8c2ZTvasOWrbE3gwO9UO5wrLPCvNTKBRLc\/EZH86yDH62PGVZGXs0NNTPZnlb+BV130nO94kHFofx2Sx8Z7lXA39BTv1srlR3VsuZnWFvTozx2RxVd5Wvi1fEHg0K9YO5wfKmupeMxpcmLC7jj5NKD8rb25tij4aG+tncbYf1UK2VfwXEXE6G8aX5i12ftdwQezQ01M9m+dnkO5a\/xd6cNON787zN5xt23RB7NCjUD+ZWyxezVQovHcBE8eEslJ9n7Ig9GhrqPgWW35vfZzP5Q60LMm+Df\/osb76\/bFnjPWo9Jy1Qvze3qzurszUSs9mCmfAE6XPL1tijQeH\/6Pqj\/WNeIDGYLYhC\/8ayJ\/ZoaKj\/\/yE8pO4nKhI3mq3wpVk+BR2w63rvUes5aYF6oe8Se7M1cW+WXuyKPRoUQuiZiIN53HJn7NGQUL8wl6t7g\/hsrQtCb0P8OugtDfmx5tTcr4PU3Xx7jdibk2dc6LtjjwaFEHom4mCWn2EgdISeBZ9NF\/rG2KehIYSeifEnmwgdoacAoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO4IoWcCoVeE0LOB0ANC6JlA6I4QeiYQekUIPRsIPSCEngmE7gihZwKhV4TQs4HQA0LomUDojhB6JhB6RQg9Gwg9IISeCYTuCKFnAqFXhNCzgdADQuiZQOiOEHomEHpFCD0bCD0ghJ4JhO6oH8ydlqO1KAxmG+JgHrfsiT0aGuXfbZm2m82H7Xqm1gWht8Fn87z144Bd13uPWs9JC4TQMxH35rHy4TT2aFCoH8xtVohDs9UZjS6IwWzBqNa+cMSyM\/ZoaKgT+jLLXsupWheWZhtml6bN5zm77res9R61npMWaO4Hoa9nC8TebMXFvVkdtj32qAX\/AjNYiqbkFMcOAAAAAElFTkSuQmCC\");-webkit-mask-repeat:repeat-x;mask-repeat:repeat-x;-webkit-mask-size:auto 24px;mask-size:auto 24px;-webkit-mask-position:center bottom;mask-position:center bottom;border-radius:0}.bu-cta-inner{width:82%;margin:0 auto;padding:30px 0 34px;display:flex;align-items:center;justify-content:space-between;gap:36px}.bu-cta-copy{flex:1;min-width:0}.bu-cta-eyebrow{font-weight:900;font-size:13px;line-height:1.4;letter-spacing:.07em;margin:0 0 8px;text-transform:uppercase;color:inherit}.bu-cta-band .bu-cta-title{font-family:Montserrat,Arial,sans-serif;font-weight:900;font-size:clamp(27px,2.6vw,40px);line-height:1.2;letter-spacing:-.02em;color:var(--cta-ink)!important;margin:0 0 10px}.bu-cta-band .bu-cta-desc{font-weight:600;font-size:clamp(14px,1.2vw,17px);line-height:1.7;color:var(--cta-ink)!important;max-width:690px;margin:0}.bu-cta-action{flex-shrink:0;max-width:100%;padding:0 5px 5px 0}.bu-cta-band a.bu-cta-button{min-height:56px;display:inline-flex;align-items:center;justify-content:center;gap:10px;padding:16px 24px;border:3px solid #1D1D1B!important;border-radius:999px;background:#1D1D1B!important;color:#fff!important;font-family:Montserrat,Arial,sans-serif;font-size:15px;font-weight:900;line-height:1.3;text-decoration:none!important;text-align:center;box-shadow:5px 5px 0 var(--cta-accent)!important;transition:transform .2s,box-shadow .2s;max-width:100%;overflow-wrap:anywhere}.bu-cta-band a.bu-cta-button:hover{transform:translate(2px,2px);box-shadow:3px 3px 0 var(--cta-accent)!important}.bu-cta-band a.bu-cta-button:focus-visible{outline:3px solid var(--cta-ink);outline-offset:5px}.bu-cta-button span{font-size:18px;font-weight:900}@media(max-width:992px){.bu-cta-inner{gap:24px}.bu-cta-action{max-width:40%}}@media(max-width:768px){.bu-cta-inner{width:92%;padding:28px 0 30px;flex-direction:column;align-items:flex-start;gap:20px}.bu-cta-action{width:100%;max-width:100%}.bu-cta-band a.bu-cta-button{width:100%}}@media(prefers-reduced-motion:reduce){.bu-cta-band a.bu-cta-button{transition:none}}\n<\/style><section class=\"bu-stud-closing bu-cta-band\" style=\"--cta-bg:#237841;--cta-ink:#FFFFFF;--cta-accent:#E52828;\" aria-labelledby=\"bu-cta-title-11193\" data-bu-slogan-page=\"11193\"><div class=\"bu-cta-inner\"><div class=\"bu-cta-copy\"><p class=\"bu-cta-eyebrow\">KEEP EXPLORING<\/p><h2 class=\"bu-cta-title\" id=\"bu-cta-title-11193\">Give an idea its first brick.<\/h2><p class=\"bu-cta-desc\">Explore small creative starting points and ways to experiment.<\/p><\/div><div class=\"bu-cta-action\"><a class=\"bu-cta-button\" href=\"https:\/\/minifigure-collection.org\/en\/mini-kits\/\">Explore Kits <span aria-hidden=\"true\">\u2192<\/span><\/a><\/div><\/div><\/section><\/div>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Studio DESIGN-BRICKS \u00b7 MAKE IT YOURS Mosaic Studio. From your first pixel to your next build. Upload photo Sample BlankOpen projectSave project Build Export Ready to createPrivate by design \u00b7 your photos stay on your device Expand Image &#038; design settings + 01 Image &#038; canvas Crop &#038; positionSquarePortraitLandscapeWidth\u00d7Heightstuds1632486496128Pixel piece 02 Make it your style&hellip; <a class=\"more-link\" href=\"https:\/\/minifigure-collection.org\/en\/mobile-app\/\">Continue reading <span class=\"screen-reader-text\">Studio<\/span><\/a><\/p>","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-11193","page","type-page","status-publish","hentry","entry"],"_links":{"self":[{"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/pages\/11193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/comments?post=11193"}],"version-history":[{"count":41,"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/pages\/11193\/revisions"}],"predecessor-version":[{"id":12540,"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/pages\/11193\/revisions\/12540"}],"wp:attachment":[{"href":"https:\/\/minifigure-collection.org\/en\/wp-json\/wp\/v2\/media?parent=11193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}