{"id":466586,"url":"\/logo-tidy\/","layout":"standard","version":"2026-04-24T12:14:30.000000Z","blocks":[{"id":4401883,"type":"textBlock","published":1,"size":{"x":12,"y":0},"order":0,"items":[],"properties":{"html":{"id":106591808,"value":"<p><\/p>"},"borderRadius":{"id":106591809,"value":"#{text.border.radius}"},"textColor":{"id":106591810,"value":"#{text.color}"},"backgroundColour":{"id":106591811,"value":"#0000"},"truncateHeight":{"id":106591812,"value":null},"template":{"id":106591813,"value":"text"},"padding":{"id":106591814,"value":"3"},"fullWidth":{"id":106591815,"value":"0"},"fullBackgroundColor":{"id":106591816,"value":"#{text.fullBackground.color}"},"published":{"id":106591817,"value":"1"},"conditions":{"id":106591818,"value":"[]"}}},{"id":4401884,"type":"textBlock","published":1,"size":{"x":12,"y":0},"order":1,"items":[],"properties":{"html":{"id":106591819,"value":"<h3>Free Logo Upscaler<\/h3><p>At Kitlocker we believe in supporting grass roots sport. And we know how important it is that your club logo pops right off your kit... and how hard it can be to get graphics sorted to a high standard. This tool enables you to upload your club crest or artwork and use the power of Ai to improve it. It's free, and if you want to give back to help us continue to support grass roots sport, check out or range of club kits and custom sports kit once you've had your upscaled art emailed to your inbox. <\/p>"},"borderRadius":{"id":106591820,"value":"#{text.border.radius}"},"textColor":{"id":106591821,"value":"#{text.color}"},"backgroundColour":{"id":106591822,"value":"#0000"},"truncateHeight":{"id":106591823,"value":null},"template":{"id":106591824,"value":"text"},"padding":{"id":106591825,"value":"6"},"fullWidth":{"id":106591826,"value":"0"},"fullBackgroundColor":{"id":106591827,"value":"#{text.fullBackground.color}"},"published":{"id":106591828,"value":"1"},"conditions":{"id":106591829,"value":"[]"}}},{"id":4401885,"type":"html","published":1,"size":{"x":12,"y":0},"order":2,"items":[],"properties":{"html":{"id":106591830,"value":"<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Logo Cleanup & Vectorization<\/title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      background: #fff;\n      color: #333;\n      margin: 0;\n      padding: 0;\n    }\n\n    .logo-widget {\n      display: flex;\n      flex-direction: column;\n      gap: 20px;\n      padding: 30px;\n      max-width: 1000px;\n      margin: 0 auto;\n    }\n\n    @media (min-width: 768px) {\n      .logo-widget {\n        flex-direction: row;\n        align-items: flex-start;\n      }\n    }\n\n    .logo-form,\n    .logo-preview {\n      flex: 1;\n    }\n\n    .logo-form {\n      padding-right: 20px;\n    }\n\n    h1 {\n      text-align: center;\n      margin-bottom: 20px;\n    }\n\n    .form-group {\n      margin-bottom: 15px;\n    }\n\n    label {\n      display: block;\n      margin-bottom: 6px;\n      font-weight: bold;\n    }\n\n    input[type=\"text\"],\n    input[type=\"email\"],\n    select,\n    textarea,\n    input[type=\"file\"] {\n      width: 100%;\n      padding: 10px;\n      border: 1px solid #ccc;\n      border-radius: 5px;\n      box-sizing: border-box;\n    }\n\n    textarea {\n      resize: vertical;\n    }\n\n    button[type=\"submit\"] {\n      background-color: #000;\n      color: #fff;\n      padding: 12px 18px;\n      border: none;\n      border-radius: 3px;\n      cursor: pointer;\n      font-size: 16px;\n      width: 100%;\n    }\n\n    button[type=\"submit\"]:hover {\n      background-color: #333;\n    }\n\n    button[type=\"submit\"]:disabled {\n      background-color: #999;\n      cursor: not-allowed;\n    }\n\n    .privacy {\n      font-size: 0.85em;\n      margin-top: 10px;\n      text-align: center;\n    }\n\n    .privacy a {\n      color: #0077cc;\n      text-decoration: none;\n    }\n\n    .privacy a:hover {\n      text-decoration: underline;\n    }\n\n    .logo-preview {\n      border: 2px dashed #ddd;\n      border-radius: 10px;\n      padding: 20px;\n      text-align: center;\n      min-height: 300px;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      color: #999;\n      position: relative;\n    }\n\n    .spinner {\n      border: 4px solid rgba(0, 0, 0, 0.1);\n      border-radius: 50%;\n      border-top: 4px solid #000;\n      width: 40px;\n      height: 40px;\n      animation: spin 1s linear infinite;\n      position: absolute;\n      top: calc(50% - 20px);\n      left: calc(50% - 20px);\n      display: none;\n    }\n\n    #countdown {\n      position: absolute;\n      top: 50%;\n      left: 50%;\n      transform: translate(-50%, -50%);\n      font-size: 12px;\n      color: #555;\n    }\n\n    @keyframes spin {\n      0% {\n        transform: rotate(0deg);\n      }\n\n      100% {\n        transform: rotate(360deg);\n      }\n    }\n\n    .g-recaptcha {\n      display: flex;\n      justify-content: center;\n      margin-bottom: 10px;\n    }\n  <\/style>\n<\/head>\n\n<body>\n  <div class=\"logo-widget\">\n    <form id=\"vectorForm\" class=\"logo-form\">\n      <h1>Clean & Vectorize Your Logo<\/h1>\n\n      <div class=\"form-group\">\n        <label for=\"clubName\">Club or Organization Name*:<\/label>\n        <input type=\"text\" id=\"clubName\" name=\"clubName\" required>\n      <\/div>\n\n      <div class=\"form-group\">\n        <label for=\"uploadLogo\">Upload Logo*<\/label>\n        <input type=\"file\" id=\"uploadLogo\" name=\"uploadLogo\" accept=\"image\/png\" required>\n      <\/div>\n\n      <div class=\"form-group\">\n        <label for=\"desiredStyle\">Desired Style:<\/label>\n        <select id=\"desiredStyle\" name=\"desiredStyle\">\n          <option value=\"Flat Vector\" selected>Flat Vector<\/option>\n          <option value=\"Minimal Clean\">Minimal Clean<\/option>\n          <option value=\"Detailed Illustration\">Detailed Illustration<\/option>\n          <option value=\"Modern Monochrome\">Modern Monochrome<\/option>\n          <option value=\"\">Upscale Only<\/option>\n        <\/select>\n      <\/div>\n\n      <div class=\"form-group\">\n        <label for=\"notes\">Additional Notes:<\/label>\n        <textarea id=\"notes\" name=\"notes\" rows=\"4\" placeholder=\"Any specific instructions or references\"><\/textarea>\n      <\/div>\n\n      <div class=\"form-group\">\n        <label for=\"userEmail\">Email Address*:<\/label>\n        <input type=\"email\" id=\"userEmail\" name=\"userEmail\" required placeholder=\"your.email@example.com\">\n      <\/div>\n\n      <div id=\"g-recaptcha\" class=\"g-recaptcha\"><\/div>\n\n      <button type=\"submit\" id=\"submitBtn\">Submit for Cleanup<\/button>\n      <div class=\"privacy\">\n        <small>By submitting, you agree to our <a href=\"\/privacy-policy\/\" target=\"_blank\">privacy\n            policy<\/a>.<\/small>\n      <\/div>\n    <\/form>\n\n    <div class=\"logo-preview\" id=\"previewArea\">\n      <div id=\"placeholder\">Your cleaned-up logo will appear here<\/div>\n      <div class=\"spinner\" id=\"spinner\"><\/div>\n      <div id=\"countdown\"><\/div>\n      <img id=\"upscaledLogo\" src=\"#\" alt=\"Upscaled Logo\"\n        style=\"display: none; max-width: 100%; border-radius: 8px; background: #fff;\">\n      <a id=\"downloadLogoBtn\" href=\"#\" style=\"display: none; margin-top: 12px; font-weight: 600; text-decoration: none; color: #0077cc;\">Download<\/a>\n      <p id=\"errorMessage\" style=\"color: red; text-align: center; margin-top: 10px;\"><\/p>\n    <\/div>\n  <\/div>\n\n  <script>\n    const form = document.getElementById('vectorForm');\n    const spinner = document.getElementById('spinner');\n    const countdown = document.getElementById('countdown');\n    const placeholder = document.getElementById('placeholder');\n    const upscaledLogoImg = document.getElementById('upscaledLogo');\n    const errorMessageP = document.getElementById('errorMessage');\n    const submitBtn = document.getElementById('submitBtn');\n    const downloadLogoBtn = document.getElementById('downloadLogoBtn');\n    const recaptchaContainer = document.getElementById('g-recaptcha');\n\n    const API_ENDPOINT = '\/omnis\/v3\/storefront\/artwork-tools\/logo-tidy';\n\n    const RECAPTCHA_V3_SITE_KEY = '6LdwEbsUAAAAAJALG7Pbb_MnjjlOlZM8HXco9W5Z';\n    const RECAPTCHA_V2_SITE_KEY = '6LfFQRkTAAAAAJevgMy8sz6hV3f8ET9qsJiTWwqA';\n\n    let v3Token = null;\n    let v2Rendered = false;\n\n    \/\/ --- reCAPTCHA ---\n\n    function loadRecaptchaScript() {\n      if (window.grecaptcha && window.grecaptcha.execute) return Promise.resolve();\n\n      return new Promise((resolve, reject) => {\n        window.onRecaptchaLoad = resolve;\n        const script = document.createElement('script');\n        script.src = 'https:\/\/www.google.com\/recaptcha\/api.js?onload=onRecaptchaLoad&render=' + RECAPTCHA_V3_SITE_KEY;\n        script.onerror = reject;\n        document.head.appendChild(script);\n      });\n    }\n\n    async function getV3Token() {\n      await loadRecaptchaScript();\n      v3Token = await window.grecaptcha.execute(RECAPTCHA_V3_SITE_KEY, { action: 'storefront\/artwork_tools' });\n      return v3Token;\n    }\n\n    async function verifyV3Score() {\n      if (!v3Token) await getV3Token();\n\n            const res = await jsonFetch('\/omnis\/v3\/recaptcha\/verify\/', {\n                method: 'POST',\n                body: JSON.stringify({ token: v3Token }),\n            });\n      const data = await res.json();\n      return data.score ?? 0;\n    }\n\n    function renderV2Checkbox() {\n      if (v2Rendered || !RECAPTCHA_V2_SITE_KEY) return;\n      window.grecaptcha.render(recaptchaContainer, { sitekey: RECAPTCHA_V2_SITE_KEY });\n      v2Rendered = true;\n    }\n\n    function getV2Token() {\n      if (!v2Rendered) return null;\n      try { return window.grecaptcha.getResponse(); } catch { return null; }\n    }\n\n    async function prepareRecaptcha() {\n      await getV3Token();\n      const score = await verifyV3Score();\n\n      if (score <= 0.3) {\n        renderV2Checkbox();\n        throw new Error('Please complete the reCAPTCHA challenge before submitting.');\n      }\n\n      \/\/ refresh the v3 token so it's fresh for the actual request\n      await getV3Token();\n    }\n\n        \/\/ --- Helpers ---\n\n        function getCsrfToken() {\n            const match = document.cookie.match(\/(?:^|;\\s*)XSRF-TOKEN=([^;]*)\/);\n            return match ? decodeURIComponent(match[1]) : '';\n        }\n\n        function jsonFetch(url, options = {}) {\n            options.headers = Object.assign({\n                'Content-Type': 'application\/json',\n                'X-CSRF-TOKEN': getCsrfToken(),\n            }, options.headers || {});\n            options.credentials = 'same-origin';\n            return fetch(url, options);\n        }\n\n        function getBase64(file) {\n      return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.readAsDataURL(file);\n        reader.onload = () => resolve(reader.result);\n        reader.onerror = error => reject(error);\n      });\n    }\n\n    \/\/ --- Submit ---\n\n    form.addEventListener('submit', async function (e) {\n      e.preventDefault();\n\n      placeholder.style.display = 'none';\n      upscaledLogoImg.style.display = 'none';\n      downloadLogoBtn.style.display = 'none';\n      errorMessageP.textContent = '';\n      spinner.style.display = 'block';\n      countdown.style.display = 'block';\n      submitBtn.disabled = true;\n\n      let timeLeft = 45;\n      countdown.textContent = timeLeft + 's';\n\n      const interval = setInterval(() => {\n        timeLeft--;\n        if (timeLeft <= 0) {\n          clearInterval(interval);\n          countdown.textContent = 'Processing...';\n        } else {\n          countdown.textContent = timeLeft + 's';\n        }\n      }, 1000);\n\n      const clubName = document.getElementById('clubName').value;\n      const userEmail = document.getElementById('userEmail').value;\n      const desiredStyle = document.getElementById('desiredStyle').value;\n      const notes = document.getElementById('notes').value;\n      const logoFile = document.getElementById('uploadLogo').files[0];\n\n      if (!logoFile) {\n        spinner.style.display = 'none';\n        countdown.style.display = 'none';\n        submitBtn.disabled = false;\n        errorMessageP.textContent = 'Please select a logo file to upload.';\n        clearInterval(interval);\n        return;\n      }\n\n      try {\n        \/\/ reCAPTCHA\n        await prepareRecaptcha();\n\n        \/\/ Read file as base64\n        let logo_to_upscale_b64 = await getBase64(logoFile);\n        if (logo_to_upscale_b64.includes(',')) {\n          logo_to_upscale_b64 = logo_to_upscale_b64.split(',')[1];\n        }\n\n        const payload = {\n          clubName: clubName,\n          userEmail: userEmail,\n          desiredStyle: desiredStyle,\n          notes: notes,\n          logo_to_upscale_b64: logo_to_upscale_b64,\n          v3captchaToken: v3Token,\n          v2captchaToken: getV2Token(),\n        };\n\n                const response = await jsonFetch(API_ENDPOINT, {\n                    method: 'POST',\n                    body: JSON.stringify(payload),\n                });\n\n        clearInterval(interval);\n        spinner.style.display = 'none';\n        countdown.style.display = 'none';\n        submitBtn.disabled = false;\n\n        const result = await response.json();\n\n        if (response.ok && result.logo) {\n          const dataUrl = 'data:image\/png;base64,' + result.logo;\n          upscaledLogoImg.src = dataUrl;\n          upscaledLogoImg.style.display = 'block';\n          placeholder.style.display = 'none';\n          const clubName = document.getElementById('clubName').value;\n          const safeName = (clubName || 'logo').trim().replace(\/[^\\w\\s-]\/g, '').replace(\/\\s+\/g, '_');\n          downloadLogoBtn.href = dataUrl;\n          downloadLogoBtn.download = safeName + '_tidied_logo.png';\n          downloadLogoBtn.style.display = 'inline-block';\n        } else {\n          errorMessageP.textContent = result.error || 'Failed to process logo. Please try again.';\n          placeholder.style.display = 'block';\n        }\n\n      } catch (err) {\n        clearInterval(interval);\n        spinner.style.display = 'none';\n        countdown.style.display = 'none';\n        submitBtn.disabled = false;\n        errorMessageP.textContent = 'Error: ' + err.message;\n        placeholder.style.display = 'block';\n        console.error('Logo tidy error:', err);\n      }\n    });\n\n    \/\/ Pre-load reCAPTCHA\n    if (RECAPTCHA_V3_SITE_KEY) {\n      loadRecaptchaScript().catch(() => { });\n    }\n  <\/script>\n<\/body>\n\n<\/html>"},"padding":{"id":106591832,"value":"6"},"fullWidth":{"id":106591833,"value":"1"},"fullBackgroundColor":{"id":106591834,"value":"#fff0"},"published":{"id":106591835,"value":"1"},"conditions":{"id":106591836,"value":"[]"},"template":{"id":128917884,"value":"html"}}}],"properties":{"title":{"id":106591802,"value":"Logo Tidy"},"isStorePage":{"id":106591803,"value":"1"},"description":{"id":106591804,"value":"A tool to upscale and clean up a logo"},"isSearchable":{"id":106591805,"value":"0"},"loginState":{"id":106591806,"value":"everyone"},"ogImage":{"id":106591807,"value":"https:\/\/images.podos.io\/8nmaix9zvtifxm97bvshkdmtvfpb8qb2urbeuvlfu9b5bydm.jpg.jpg?w=1200&h=auto"}},"labels":[],"published":1,"sitemap":1,"divisionId":356097,"edited":true,"keyPhraseCampaignId":null}