{"product_id":"vag-sync-calculator","title":"VAG Sync Calculator","description":"\u003cp\u003e\u003cmeta charset=\"UTF-8\"\u003e \u003cmeta content=\"width=device-width, initial-scale=1.0\" name=\"viewport\"\u003e\u003clink href=\"https:\/\/fonts.googleapis.com\" rel=\"preconnect\"\u003e \u003clink crossorigin=\"\" href=\"https:\/\/fonts.gstatic.com\" rel=\"preconnect\"\u003e \u003clink rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Ubuntu:wght@400;500;700\u0026amp;display=swap\"\u003e\u003c\/p\u003e\n\u003cstyle\u003e\n        \/* ============================================\n           VAG Sync Tool — keylibrary.co.uk Theme\n           Design tokens from keylibrary.co.uk:\n           - Primary accent: #ef5729 (orange-red)\n           - Secondary accent: #0f9d58 (green)\n           - Background: #ffffff\n           - Text: rgba(51,51,51,0.75)\n           - Card bg: #ffffff, border: rgba(51,51,51,0.1)\n           - Fonts: Playfair Display (headings), Lato (body)\n           - Accent: #F05A28 (orange-red)\n        ============================================ *\/\n        .vag-sync-tool {\n            \/* keylibrary colour tokens *\/\n            --color-bg: #ffffff;\n            --color-surface: #f8f8f8;\n            --color-card: #ffffff;\n            --color-card-border: rgba(51,51,51,0.1);\n            --color-text: rgba(51,51,51,0.75);\n            --color-text-muted: rgba(51,51,51,0.55);\n            --color-text-strong: rgb(51,51,51);\n            --color-accent: #ef5729;\n            --color-accent-hover: #d44a23;\n            --color-accent-glow: rgba(239,87,41,0.25);\n            --color-success: #11ae66;\n            --color-success-glow: rgba(17,174,102,0.2);\n            --color-error: #c0392b;\n            --color-warning-bg: #fff9f0;\n            --color-warning-border: #ef5729;\n            --color-border: rgba(51,51,51,0.15);\n            --color-input-bg: #f5f5f5;\n            --color-input-border: rgba(51,51,51,0.2);\n            --color-input-focus: #ef5729;\n            --shadow-card: 0 2px 8px rgba(51,51,51,0.08), 0 1px 2px rgba(51,51,51,0.04);\n            --shadow-card-hover: 0 4px 16px rgba(51,51,51,0.12), 0 2px 4px rgba(51,51,51,0.06);\n            --radius-sm: 6px;\n            --radius-md: 10px;\n            --radius-lg: 14px;\n            --font-heading: 'Ubuntu', 'Segoe UI', sans-serif;\n            --font-body: 'Ubuntu', 'Segoe UI', sans-serif;\n            --font-mono: 'Ubuntu', monospace;\n            --transition: 0.2s ease;\n        }\n        .vag-sync-tool * { box-sizing: border-box; margin: 0; padding: 0; }\n        .vag-sync-tool body {\n            font-family: var(--font-body);\n            max-width: 700px;\n            margin: 0 auto;\n            padding: 30px 20px 60px;\n            background: var(--color-bg);\n            color: var(--color-text);\n            min-height: 100vh;\n            -webkit-font-smoothing: antialiased;\n        }\n\n        \/* Header *\/\n        .vag-sync-tool .header {\n            text-align: center;\n            margin-bottom: 0;\n            padding-bottom: 0;\n            border-bottom: none;\n        }\n        .vag-sync-tool .header .subtitle {\n            color: rgba(51,51,51,0.7);\n            font-size: 12px;\n            font-weight: 400;\n            text-transform: uppercase;\n            letter-spacing: 3px;\n        }\n\n        \/* Mode toggle *\/\n        .vag-sync-tool .mode-toggle {\n            display: flex;\n            margin-bottom: 24px;\n            background: var(--color-surface);\n            border: 1px solid var(--color-border);\n            border-radius: var(--radius-md);\n            padding: 4px;\n        }\n        .vag-sync-tool .mode-btn {\n            flex: 1;\n            padding: 11px 20px;\n            border: none;\n            background: transparent;\n            color: var(--color-text-muted);\n            cursor: pointer;\n            border-radius: var(--radius-sm);\n            font-family: var(--font-body);\n            font-size: 13px;\n            font-weight: 400;\n            text-transform: uppercase;\n            letter-spacing: 1.2px;\n            transition: all var(--transition);\n        }\n        .vag-sync-tool .mode-btn.active {\n            background: rgb(239,87,41);\n            color: #ffffff;\n        }\n        .vag-sync-tool .mode-btn:hover:not(.active) {\n            color: var(--color-text-strong);\n            background: rgba(51,51,51,0.05);\n        }\n\n        \/* Card *\/\n        .vag-sync-tool .card {\n            background: var(--color-card);\n            border-radius: var(--radius-lg);\n            padding: 24px;\n            margin-bottom: 18px;\n        }\n        .vag-sync-tool .card-title {\n            color: var(--color-accent);\n            font-size: 11px;\n            font-weight: 700;\n            text-transform: uppercase;\n            letter-spacing: 2.5px;\n            margin-bottom: 20px;\n            padding-bottom: 12px;\n            border-bottom: 1px solid var(--color-border);\n        }\n\n        \/* Field *\/\n        .vag-sync-tool .field { margin-bottom: 16px; }\n        .vag-sync-tool label {\n            display: block;\n            margin-bottom: 12px !important;\n            font-weight: 400;\n            color: var(--color-text-muted);\n            font-size: 11px;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n        }\n        .vag-sync-tool input[type=\"text\"] {\n            width: 100%;\n            padding: 13px 15px;\n            border: 0;\n            border-radius: var(--radius-sm);\n            background: #ffffff;\n            color: rgb(51,51,51);\n            font-family: var(--font-mono);\n            font-size: 15px;\n            letter-spacing: 1px;\n            box-shadow: inset 0 1px 3px rgba(51,51,51,0.06);\n            transition: box-shadow var(--transition);\n        }\n        .vag-sync-tool input[type=\"text\"]:focus {\n            outline: .2rem solid rgba(51,51,51,0.5);\n            outline-offset: 3px;\n            box-shadow: inset 0 1px 3px rgba(51,51,51,0.06), 0 0 0 3px rgba(51,51,51,0.1);\n        }\n        .vag-sync-tool input[type=\"text\"]::placeholder { color: rgba(51,51,51,0.3); }\n        .vag-sync-tool input[type=\"text\"]:disabled {\n            background: var(--color-surface);\n            color: rgba(51,51,51,0.35);\n            cursor: not-allowed;\n            box-shadow: none;\n        }\n        .vag-sync-tool input[type=\"text\"][readonly] {\n            background: var(--color-surface);\n            color: var(--color-text-strong);\n            border-color: var(--color-border);\n            cursor: default;\n        }\n        .vag-sync-tool input[type=\"text\"][readonly]:focus {\n            box-shadow: none;\n        }\n        .vag-sync-tool .mono-field {\n            font-family: var(--font-mono) !important;\n            letter-spacing: 1px !important;\n        }\n\n        \/* Checkbox *\/\n        .vag-sync-tool .checkbox-container {\n            display: flex;\n            align-items: center;\n            gap: 12px;\n            margin-bottom: 13px;\n            padding: 13px 15px;\n            background: var(--color-surface);\n            border: 1px solid var(--color-border);\n            border-radius: var(--radius-sm);\n            cursor: pointer;\n            transition: all var(--transition);\n        }\n        .vag-sync-tool .checkbox-container:hover {\n            background: rgba(239,87,41,0.04);\n            border-color: rgba(239,87,41,0.25);\n        }\n        .vag-sync-tool .checkbox-container input[type=\"checkbox\"] {\n            width: 20px;\n            height: 20px;\n            accent-color: var(--color-accent);\n            cursor: pointer;\n            flex-shrink: 0;\n        }\n        .vag-sync-tool .checkbox-container span {\n            margin: 0;\n            color: var(--color-text);\n            font-size: 14px;\n            font-weight: 400;\n            text-transform: none;\n            letter-spacing: 0;\n            cursor: pointer;\n        }\n        .vag-sync-tool .checkbox-container.ev-coming-soon label {\n            color: var(--color-text-muted);\n            cursor: not-allowed;\n            font-style: italic;\n        }\n        .vag-sync-tool .checkbox-container.ev-coming-soon {\n            opacity: 0.7;\n            cursor: default;\n        }\n        .vag-sync-tool .checkbox-container.ev-coming-soon:hover {\n            background: var(--color-surface);\n            border-color: var(--color-border);\n        }\n\n        \/* Buttons *\/\n        .vag-sync-tool .btn {\n            padding: 14px 28px;\n            border: none;\n            border-radius: var(--radius-sm);\n            cursor: pointer;\n            font-family: var(--font-body);\n            font-size: 15px;\n            font-weight: 400;\n            letter-spacing: .1rem;\n            text-transform: none;\n            transition: box-shadow var(--transition);\n            width: 100%;\n        }\n        .vag-sync-tool .btn-primary {\n            background: rgb(239,87,41);\n            color: #ffffff;\n        }\n        .vag-sync-tool .btn-primary:hover {\n            background: #d44a23;\n        }\n        .vag-sync-tool .btn-primary:active {\n            background: #c04020;\n        }\n        .vag-sync-tool .btn-secondary {\n            background: var(--color-surface);\n            color: var(--color-text-muted);\n            border: 1px solid var(--color-border);\n        }\n        .vag-sync-tool .btn-secondary:hover {\n            background: var(--color-card);\n            color: var(--color-text-strong);\n            border-color: var(--color-text-muted);\n        }\n\n        \/* Result *\/\n        .vag-sync-tool .result {\n            margin-top: 18px;\n            padding: 20px;\n            background: var(--color-surface);\n            border-radius: var(--radius-md);\n            border-left: 4px solid var(--color-success);\n        }\n        .vag-sync-tool .result-error {\n            border-left-color: var(--color-error);\n            background: #fff8f8;\n        }\n        .vag-sync-tool .result-title {\n            color: var(--color-accent);\n            font-size: 10px;\n            font-weight: 700;\n            text-transform: uppercase;\n            letter-spacing: 2.5px;\n            margin-bottom: 14px;\n        }\n        .vag-sync-tool .result-item { margin-bottom: 14px; }\n        .vag-sync-tool .result-item:last-child { margin-bottom: 0; }\n        .vag-sync-tool .result-label {\n            color: var(--color-text-muted);\n            font-size: 1rem;\n            font-weight: 400;\n            text-transform: none;\n            letter-spacing: 0;\n            margin-bottom: 5px;\n        }\n        .vag-sync-tool .result-value {\n            color: var(--color-text-strong);\n            font-family: var(--font-mono);\n            font-size: 13px;\n            background: var(--color-card);\n            padding: 10px 13px;\n            border-radius: var(--radius-sm);\n            border: 1px solid var(--color-border);\n            word-break: break-all;\n        }\n        .vag-sync-tool .sync-display {\n            font-family: var(--font-mono);\n            word-break: break-all;\n            background: var(--color-card);\n            padding: 14px;\n            border-radius: var(--radius-sm);\n            margin-top: 10px;\n            font-size: 13px;\n            color: var(--color-accent);\n            letter-spacing: 1.5px;\n            border: 1px solid var(--color-border);\n        }\n        .vag-sync-tool .file-controls {\n            display: flex;\n            gap: 10px;\n            margin-top: 10px;\n        }\n        .vag-sync-tool .file-controls .btn {\n            flex: 1;\n        }\n        .vag-sync-tool .instructions {\n            margin-top: 30px;\n            padding: 20px 25px;\n            background: #252525;\n            border-radius: 12px;\n            font-size: 13px;\n            line-height: 1.8;\n            color: #aaa;\n        }\n        .vag-sync-tool .instructions h3 {\n            color: #F2C232;\n            font-size: 12px;\n            text-transform: uppercase;\n            letter-spacing: 2px;\n            margin: 0 0 15px 0;\n        }\n        .vag-sync-tool .instructions p {\n            margin: 0 0 10px 0;\n        }\n        .vag-sync-tool .instructions ul {\n            margin: 0;\n            padding-left: 20px;\n        }\n        .vag-sync-tool .instructions li {\n            margin-bottom: 5px;\n        }\n        .vag-sync-tool .hidden {\n            display: none;\n        }\n\n        \/* Inline style equivalents for vag-sync-tool *\/\n        .vag-sync-tool .field-margin-top {\n            margin-top: 15px;\n        }\n        .vag-sync-tool .file-input-hidden {\n            display: none;\n        }\n        .vag-sync-tool .field2 {\n            margin-bottom: 16px;\n        }\n        .vag-sync-tool .flex-row {\n            display: flex;\n            gap: 10px;\n            align-items: center;\n            margin-top: 8px;\n        }\n        .vag-sync-tool .flex-row input {\n            flex: 1;\n            min-width: 0;\n        }\n        .vag-sync-tool .flex-row .btn {\n            width: auto;\n            padding: 13px 16px;\n            flex-shrink: 0;\n        }\n        @media (max-width: 600px) {\n            .vag-sync-tool .flex-row {\n                flex-direction: column;\n                align-items: stretch;\n            }\n            .vag-sync-tool .flex-row input,\n            .vag-sync-tool .flex-row .btn {\n                width: 100% !important;\n                flex: none !important;\n            }\n        }\n    \u003c\/style\u003e\n\u003cdiv class=\"vag-sync-tool\"\u003e\n\u003cdiv class=\"header\"\u003e\u003c!-- Logo and branding hosted by parent keylibrary.co.uk --\u003e\u003c\/div\u003e\n\u003cdiv class=\"mode-toggle\"\u003e\n\u003cbutton class=\"mode-btn active\" id=\"btnForward\"\u003eForward Mode\u003c\/button\u003e \u003cbutton class=\"mode-btn\" id=\"btnReverse\"\u003eReverse Mode\u003c\/button\u003e\n\u003c\/div\u003e\n\u003cdiv id=\"forwardMode\"\u003e\n\u003cdiv class=\"card\"\u003e\n\u003cdiv class=\"card-title\"\u003eInput Parameters\u003c\/div\u003e\n\u003cdiv class=\"field2\"\u003e\n\u003clabel for=\"fwdImmo\"\u003eIMMO (Key)\u003c\/label\u003e\n\u003cdiv class=\"flex-row\"\u003e\n\u003cinput type=\"text\" id=\"fwdImmo\" placeholder=\"32 hex characters\" maxlength=\"32\"\u003e \u003cbutton class=\"btn btn-secondary\" id=\"btnLoadFwdImmo\"\u003eLoad .bin\u003c\/button\u003e \u003cinput type=\"file\" id=\"fileFwdImmo\" accept=\".bin\" class=\"file-input-hidden\"\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"field2 hidden\" id=\"fwdVINField\"\u003e\n\u003clabel for=\"fwdVIN\"\u003eVIN (from file)\u003c\/label\u003e \u003cinput type=\"text\" id=\"fwdVIN\" class=\"mono-field\" readonly\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"field2\"\u003e\n\u003clabel for=\"fwdECU\"\u003eECU\u003c\/label\u003e \u003cinput type=\"text\" id=\"fwdECU\" placeholder=\"32 hex characters\" maxlength=\"32\"\u003e\n\u003c\/div\u003e\n\u003clabel class=\"checkbox-container\" for=\"fwdTCUActive\"\u003e \u003cinput type=\"checkbox\" id=\"fwdTCUActive\"\u003e \u003cspan\u003eTCU Active\u003c\/span\u003e \u003c\/label\u003e\n\u003cdiv class=\"field2 hidden\" id=\"fwdTCUField\"\u003e\n\u003clabel for=\"fwdTCU\"\u003eTCU\u003c\/label\u003e \u003cinput type=\"text\" id=\"fwdTCU\" placeholder=\"32 hex characters\" maxlength=\"32\"\u003e\n\u003c\/div\u003e\n\u003clabel class=\"checkbox-container\" for=\"fwdELVActive\"\u003e \u003cinput type=\"checkbox\" id=\"fwdELVActive\"\u003e \u003cspan\u003eELV Active\u003c\/span\u003e \u003c\/label\u003e\n\u003cdiv class=\"field2 hidden\" id=\"fwdELVField\"\u003e\n\u003clabel for=\"fwdELV\"\u003eELV\u003c\/label\u003e \u003cinput type=\"text\" id=\"fwdELV\" placeholder=\"32 hex characters\" maxlength=\"32\"\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"checkbox-container disabled\"\u003e\n\u003cinput type=\"checkbox\" id=\"fwdEVActive\" disabled\u003e \u003clabel for=\"fwdEVActive\"\u003eEV (Coming Soon)\u003c\/label\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cbutton class=\"btn btn-primary\" id=\"btnCalcSync\"\u003eCalculate Sync\u003c\/button\u003e\n\u003c\/div\u003e\n\u003cdiv id=\"reverseMode\" class=\"hidden\"\u003e\n\u003cdiv class=\"card\"\u003e\n\u003cdiv class=\"card-title\"\u003eInput Data\u003c\/div\u003e\n\u003cdiv class=\"field2\"\u003e\n\u003clabel for=\"revSync\"\u003eSync\u003c\/label\u003e\n\u003cdiv class=\"flex-row\"\u003e\n\u003cinput type=\"text\" id=\"revSync\" placeholder=\"64 hex characters\" maxlength=\"64\"\u003e \u003cbutton class=\"btn btn-secondary\" id=\"btnLoadSync\"\u003eLoad Sync .bin\u003c\/button\u003e \u003cinput type=\"file\" id=\"fileSync\" accept=\".bin\" class=\"file-input-hidden\"\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"field2 field-margin-top\"\u003e\n\u003clabel for=\"revImmo\"\u003eIMMO (Key)\u003c\/label\u003e\n\u003cdiv class=\"flex-row\"\u003e\n\u003cinput type=\"text\" id=\"revImmo\" placeholder=\"32 hex characters\" maxlength=\"32\"\u003e \u003cbutton class=\"btn btn-secondary\" id=\"btnLoadRevImmo\"\u003eLoad .bin\u003c\/button\u003e \u003cinput type=\"file\" id=\"fileRevImmo\" accept=\".bin\" class=\"file-input-hidden\"\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"field2 hidden\" id=\"revVINField\"\u003e\n\u003clabel for=\"revVIN\"\u003eVIN (from file)\u003c\/label\u003e \u003cinput type=\"text\" id=\"revVIN\" class=\"mono-field\" readonly\u003e\n\u003c\/div\u003e\n\u003clabel class=\"checkbox-container\" for=\"revTCUActive\"\u003e \u003cinput type=\"checkbox\" id=\"revTCUActive\"\u003e \u003cspan\u003eTCU Active\u003c\/span\u003e \u003c\/label\u003e \u003clabel class=\"checkbox-container\" for=\"revELVActive\"\u003e \u003cinput type=\"checkbox\" id=\"revELVActive\"\u003e \u003cspan\u003eELV Active\u003c\/span\u003e \u003c\/label\u003e\n\u003c\/div\u003e\n\u003cbutton class=\"btn btn-primary\" id=\"btnReverseCalc\"\u003eReverse Calculate\u003c\/button\u003e\n\u003c\/div\u003e\n\u003cdiv id=\"result\" class=\"result hidden\"\u003e\u003c\/div\u003e\n\u003cdiv id=\"syncDownloadSection\" class=\"result hidden\"\u003e\n\u003cdiv class=\"result-title\"\u003eGenerated Sync\u003c\/div\u003e\n\u003cdiv class=\"sync-display\" id=\"syncDisplay\"\u003e\u003c\/div\u003e\n\u003cdiv class=\"file-controls\" style=\"margin-top: 15px;\"\u003e\u003cbutton class=\"btn btn-secondary\" id=\"btnDownloadSync\"\u003eDownload .bin\u003c\/button\u003e\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cdiv class=\"card instructions\"\u003e\n\u003cdiv class=\"card-title\"\u003eHow to use\u003c\/div\u003e\n\u003cp\u003e\u003cstrong\u003eForward Mode:\u003c\/strong\u003e Enter IMMO and ECU, optionally TCU and\/or ELV. Click \"Calculate Sync\" to generate the sync bytes.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eReverse Mode:\u003c\/strong\u003e Enter Sync and IMMO. Click \"Reverse Calculate\" to recover ECU, TCU, ELV values. ⚠️ Experimental — under testing.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eNotes:\u003c\/strong\u003e\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eAll values are 32 hex characters (16 bytes)\u003c\/li\u003e\n\u003cli\u003eTCU must be active to include TCU in calculation\u003c\/li\u003e\n\u003cli\u003eELV must be active to include ELV in calculation\u003c\/li\u003e\n\u003cli\u003eThe sync output is exactly 32 bytes\u003c\/li\u003e\n\u003cli\u003eDownload creates a .bin file with raw bytes\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003cp\u003e\u003cscript src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/crypto-js\/4.1.1\/crypto-js.min.js\"\u003e\u003c\/script\u003e \u003cscript\u003e\n        console.log('made by t0m1o1');\n        document.getElementById('fwdEVActive').addEventListener('change', (e) =\u003e {\n            document.getElementById('fwdEVField').classList.toggle('hidden', !e.target.checked);\n        });\n        function hexToBytes(hex) {\n            hex = hex.replace(\/\\s\/g, '');\n            const bytes = [];\n            for (let i = 0; i \u003c hex.length; i += 2) {\n                bytes.push(parseInt(hex.substr(i, 2), 16));\n            }\n            return new Uint8Array(bytes);\n        }\n\n        function bytesToHex(bytes) {\n            return Array.from(bytes).map(b =\u003e b.toString(16).padStart(2, '0')).join('').toUpperCase();\n        }\n\n        \/\/ W permutation: byte shuffle used by VAG tool (self-inverse: W = W^-1)\n        \/\/ Only applied when a[0] == 0x00 (determined empirically from reference vectors)\n        const W_PERM = [14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1];\n\n        function applyW(block) {\n            const result = new Uint8Array(16);\n            for (let i = 0; i \u003c 16; i++) {\n                result[i] = block[W_PERM[i]];\n            }\n            return result;\n        }\n\n        function aesEncrypt(keyBytes, blockBytes) {\n            const key = CryptoJS.lib.WordArray.create(keyBytes);\n            const plaintext = CryptoJS.lib.WordArray.create(blockBytes);\n            const encrypted = CryptoJS.AES.encrypt(plaintext, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });\n            const cipherWords = encrypted.ciphertext;\n            const result = new Uint8Array(16);\n            for (let i = 0; i \u003c 16; i++) {\n                result[i] = cipherWords.words[Math.floor(i \/ 4)] \u003e\u003e ((3 - (i % 4)) * 8) \u0026 0xFF;\n            }\n            return result;\n        }\n\n        function aesDecrypt(keyBytes, blockBytes) {\n            const key = CryptoJS.lib.WordArray.create(keyBytes);\n            const ciphertext = CryptoJS.lib.WordArray.create(blockBytes);\n            const decrypted = CryptoJS.AES.decrypt({ ciphertext: ciphertext }, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });\n            const words = decrypted.words;\n            const result = new Uint8Array(16);\n            for (let i = 0; i \u003c 16; i++) {\n                result[i] = words[Math.floor(i \/ 4)] \u003e\u003e ((3 - (i % 4)) * 8) \u0026 0xFF;\n            }\n            return result;\n        }\n\n        function calculateSync(immoHex, ecuHex, tcuHex, elvHex, tcuActive, elvActive) {\n            const a = hexToBytes(immoHex);\n            const b = hexToBytes(ecuHex);\n            const c = tcuActive ? hexToBytes(tcuHex) : null;\n            const d = elvActive ? hexToBytes(elvHex) : null;\n            const X = crypto.getRandomValues(new Uint8Array(4));\n            let P1, P2;\n\n            if (elvActive \u0026\u0026 tcuActive) {\n                P1 = aesEncrypt(a, d);\n                P2 = new Uint8Array(16);\n                P2.set(c.slice(0, 4), 0);\n                P2.set(b.slice(4, 8), 4);\n                P2.set(b.slice(8, 12), 8);\n                P2.set(X, 12);\n                P2 = aesEncrypt(a, P2);\n            } else if (tcuActive) {\n                \/\/ ELV not active: P1 = ECU (b), same as ECU-only case\n                \/\/ P1 carries b's structure so reverse recovery works\n                P1 = b.slice();\n                P1 = aesEncrypt(a, P1);\n                P2 = new Uint8Array(16);\n                P2.set(c.slice(0, 4), 0);\n                P2.set(b.slice(4, 8), 4);\n                P2.set(b.slice(8, 12), 8);\n                P2.set(X, 12);\n                P2 = aesEncrypt(a, P2);\n            } else if (elvActive) {\n                P1 = aesEncrypt(a, d);\n                P2 = new Uint8Array(16);\n                P2.set(new Uint8Array(4), 0);\n                P2.set(b.slice(4, 8), 4);\n                P2.set(b.slice(8, 12), 8);\n                P2.set(X, 12);\n                P2 = aesEncrypt(a, P2);\n            } else {\n                P1 = b.slice(); \/\/ b is the ECU value, used as P1 when d is not active\n                P1 = aesEncrypt(a, P1);\n                P2 = new Uint8Array(16);\n                P2.set(new Uint8Array(4), 0);\n                P2.set(b.slice(4, 8), 4);\n                P2.set(b.slice(8, 12), 8);\n                P2.set(X, 12);\n                P2 = aesEncrypt(a, P2);\n            }\n\n            const block1 = (a[0] === 0) ? applyW(P1) : P1;\n            const block2 = (a[0] === 0) ? applyW(P2) : P2;\n            const output = new Uint8Array(32);\n            output.set(block1, 0);\n            output.set(block2, 16);\n            return bytesToHex(output);\n        }\n\n        \/\/ Overlap validation for forward mode\n        \/\/ Validates that overlapping bytes between modules are consistent\n        function checkOverlapWarnings(ecu, tcu, elv, tcuActive, elvActive) {\n            const issues = [];\n            \n            \/\/ ECU and ELV overlap: ECU[0:4] === ELV[0:4] AND ECU[12:16] === ELV[12:16]\n            if (elvActive \u0026\u0026 elv) {\n                const ecu0_4 = ecu.substring(0, 8);  \/\/ 4 bytes = 8 hex chars\n                const elv0_4 = elv.substring(0, 8);\n                const ecu12_16 = ecu.substring(24, 32); \/\/ 4 bytes = 8 hex chars\n                const elv12_16 = elv.substring(24, 32);\n                \n                if (ecu0_4 !== elv0_4) {\n                    issues.push('ECU[0:4] !== ELV[0:4]');\n                }\n                if (ecu12_16 !== elv12_16) {\n                    issues.push('ECU[12:16] !== ELV[12:16]');\n                }\n            }\n            \n            \/\/ ECU and TCU overlap: ECU[8:12] === TCU[8:12]\n            if (tcuActive \u0026\u0026 tcu) {\n                const ecu8_12 = ecu.substring(16, 24); \/\/ 4 bytes = 8 hex chars\n                const tcu8_12 = tcu.substring(16, 24);\n                if (ecu8_12 !== tcu8_12) {\n                    issues.push('ECU[8:12] !== TCU[8:12]');\n                }\n            }\n            \n            \/\/ TCU and ELV overlap: TCU[0:4] === ELV[0:4] AND TCU[4:8] === ELV[4:8]\n            if (tcuActive \u0026\u0026 elvActive \u0026\u0026 tcu \u0026\u0026 elv) {\n                const tcu0_4 = tcu.substring(0, 8);\n                const elv0_4 = elv.substring(0, 8);\n                const tcu4_8 = tcu.substring(8, 16);\n                const elv4_8 = elv.substring(8, 16);\n                \n                if (tcu0_4 !== elv0_4) {\n                    issues.push('TCU[0:4] !== ELV[0:4]');\n                }\n                if (tcu4_8 !== elv4_8) {\n                    issues.push('TCU[4:8] !== ELV[4:8]');\n                }\n            }\n            \n            if (issues.length \u003e 0) {\n                return 'Warning: modules don\\'t appear aligned — computing anyway';\n            }\n            return null;\n        }\n\n        function reverseCalculate(syncHex, immoHex, tcuActive, elvActive) {\n            const a = hexToBytes(immoHex);\n            const output = hexToBytes(syncHex);\n            const block1 = output.slice(0, 16);\n            const block2 = output.slice(16, 32);\n            const p1 = aesDecrypt(a, (a[0] === 0) ? applyW(block1) : block1);\n            const p2 = aesDecrypt(a, (a[0] === 0) ? applyW(block2) : block2);\n\n            \/\/ b formula is always the same\n            const b = new Uint8Array(16);\n            b.set(p1.slice(0, 4), 0);\n            b.set(p2.slice(4, 8), 4);\n            b.set(p2.slice(8, 12), 8);\n            b.set(p1.slice(12, 16), 12);\n\n            \/\/ c formula — universal (same formula regardless of tcuActive)\n            const c = new Uint8Array(16);\n            c.set(p2.slice(0, 4), 0);\n            c.set(p1.slice(4, 8), 4);\n            c.set(p2.slice(8, 12), 8);\n            c.set(p1.slice(12, 16), 12);\n\n            \/\/ c[7] fix when nibbles differ\n            if ((p1[4] \u003e\u003e 4) !== (p2[4] \u003e\u003e 4)) {\n                c[7] = p1[7] ^ p1[4] ^ p2[4];\n            }\n\n            \/\/ elv (d) is only valid when elvActive\n            const elv = elvActive ? bytesToHex(p1) : '(inferred - ELV not active)';\n\n            return {\n                ecu: bytesToHex(b),\n                tcu: bytesToHex(c),\n                elv: elv,\n                p1: bytesToHex(p1),\n                p2: bytesToHex(p2)\n            };\n        }\n\n        document.getElementById('fwdTCUActive').addEventListener('change', (e) =\u003e {\n            document.getElementById('fwdTCUField').classList.toggle('hidden', !e.target.checked);\n        });\n\n        document.getElementById('fwdELVActive').addEventListener('change', (e) =\u003e {\n            document.getElementById('fwdELVField').classList.toggle('hidden', !e.target.checked);\n        });\n\n        document.getElementById('btnForward').addEventListener('click', () =\u003e {\n            document.getElementById('btnForward').classList.add('active');\n            document.getElementById('btnReverse').classList.remove('active');\n            document.getElementById('forwardMode').classList.remove('hidden');\n            document.getElementById('reverseMode').classList.add('hidden');\n            document.getElementById('result').classList.add('hidden');\n            document.getElementById('syncDownloadSection').classList.add('hidden');\n        });\n\n        document.getElementById('btnReverse').addEventListener('click', () =\u003e {\n            document.getElementById('btnReverse').classList.add('active');\n            document.getElementById('btnForward').classList.remove('active');\n            document.getElementById('reverseMode').classList.remove('hidden');\n            document.getElementById('forwardMode').classList.add('hidden');\n            document.getElementById('result').classList.add('hidden');\n            document.getElementById('syncDownloadSection').classList.add('hidden');\n        });\n\n        document.getElementById('btnCalcSync').addEventListener('click', () =\u003e {\n            const immo = document.getElementById('fwdImmo').value.trim();\n            const ecu = document.getElementById('fwdECU').value.trim();\n            const tcu = document.getElementById('fwdTCU').value.trim();\n            const elv = document.getElementById('fwdELV').value.trim();\n            const tcuActive = document.getElementById('fwdTCUActive').checked;\n            const elvActive = document.getElementById('fwdELVActive').checked;\n            \n            const resultDiv = document.getElementById('result');\n\n            if (!immo || !ecu) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eIMMO and ECU are required';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            if (immo.length !== 32 || ecu.length !== 32) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eIMMO and ECU must be 32 hex characters (16 bytes)';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            if (tcuActive \u0026\u0026 (!tcu || tcu.length !== 32)) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eTCU is active but TCU value is missing or invalid';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            if (elvActive \u0026\u0026 (!elv || elv.length !== 32)) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eELV is active but ELV value is missing or invalid';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            \/\/ Check overlap warnings in forward mode\n            const overlapWarning = checkOverlapWarnings(ecu, tcu, elv, tcuActive, elvActive);\n            if (overlapWarning) {\n                alert(overlapWarning);\n            }\n\n            try {\n                const sync = calculateSync(immo, ecu, tcu, elv, tcuActive, elvActive);\n                document.getElementById('syncDisplay').textContent = sync;\n                document.getElementById('syncDownloadSection').classList.remove('hidden');\n                document.getElementById('result').classList.add('hidden');\n                window.lastSyncBytes = hexToBytes(sync);\n            } catch (e) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003e' + e.message;\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n            }\n        });\n\n        document.getElementById('btnDownloadSync').addEventListener('click', () =\u003e {\n            if (window.lastSyncBytes) {\n                const blob = new Blob([window.lastSyncBytes], { type: 'application\/octet-stream' });\n                const url = URL.createObjectURL(blob);\n                const a = document.createElement('a');\n                a.href = url;\n                a.download = 'sync.bin';\n                a.click();\n                URL.revokeObjectURL(url);\n            }\n        });\n\n        document.getElementById('btnLoadSync').addEventListener('click', () =\u003e {\n            document.getElementById('fileSync').click();\n        });\n\n        function loadImmoFile(inputId, buttonId, immoFieldId, vinFieldId) {\n            const fileInput = document.getElementById(inputId);\n            fileInput.click();\n            fileInput.onchange = (e) =\u003e {\n                const file = e.target.files[0];\n                if (!file) return;\n                if (file.size !== 189) {\n                    alert('Invalid file: must be exactly 189 bytes, got ' + file.size + ' bytes');\n                    fileInput.value = '';\n                    return;\n                }\n                const reader = new FileReader();\n                reader.onload = (event) =\u003e {\n                    const bytes = new Uint8Array(event.target.result);\n                    \/\/ Parse VIN at offset 0x17-0x27 (17 bytes)\n                    const vinBytes = bytes.slice(0x17, 0x28);\n                    const vin = String.fromCharCode.apply(null, vinBytes);\n                    \/\/ Validate VIN is alphanumeric\n                    if (!\/^[A-Za-z0-9]+$\/.test(vin)) {\n                        alert('Invalid VIN: contains non-alphanumeric characters');\n                        document.getElementById(vinFieldId).closest('.field').classList.add('hidden');\n                        return;\n                    }\n                    \/\/ Parse IMMO key at offset 0x28-0x37 (16 bytes = 32 hex chars)\n                    const immoBytes = bytes.slice(0x28, 0x38);\n                    const immoHex = Array.from(immoBytes).map(b =\u003e b.toString(16).padStart(2, '0')).join('').toUpperCase();\n                    \/\/ Fill IMMO field\n                    document.getElementById(immoFieldId).value = immoHex;\n                    \/\/ Show VIN field\n                    const vinField = document.getElementById(vinFieldId);\n                    vinField.value = vin;\n                    vinField.closest('.field').classList.remove('hidden');\n                };\n                reader.readAsArrayBuffer(file);\n            };\n        }\n\n        document.getElementById('btnLoadFwdImmo').addEventListener('click', () =\u003e {\n            loadImmoFile('fileFwdImmo', 'btnLoadFwdImmo', 'fwdImmo', 'fwdVIN');\n        });\n\n        document.getElementById('btnLoadRevImmo').addEventListener('click', () =\u003e {\n            loadImmoFile('fileRevImmo', 'btnLoadRevImmo', 'revImmo', 'revVIN');\n        });\n\n        document.getElementById('fileSync').addEventListener('change', (e) =\u003e {\n            const file = e.target.files[0];\n            if (file) {\n                const reader = new FileReader();\n                reader.onload = (event) =\u003e {\n                    const bytes = new Uint8Array(event.target.result);\n                    document.getElementById('revSync').value = bytesToHex(bytes);\n                };\n                reader.readAsArrayBuffer(file);\n            }\n        });\n\n        document.getElementById('btnReverseCalc').addEventListener('click', () =\u003e {\n            const sync = document.getElementById('revSync').value.trim();\n            const immo = document.getElementById('revImmo').value.trim();\n            const tcuActive = document.getElementById('revTCUActive').checked;\n            const elvActive = document.getElementById('revELVActive').checked;\n            const resultDiv = document.getElementById('result');\n\n            if (!sync || !immo) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eSync and IMMO are required';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            if (sync.length !== 64) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eSync must be 64 hex characters (32 bytes)';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            if (immo.length !== 32) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003eIMMO must be 32 hex characters (16 bytes)';\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n                return;\n            }\n\n            try {\n                const result = reverseCalculate(sync, immo, tcuActive, elvActive);\n                resultDiv.innerHTML = `\n                    \u003cdiv class=\"result-title\"\u003eRecovered Values\u003c\/div\u003e\n                    \u003cdiv class=\"result-item\"\u003e\n                        \u003cdiv class=\"result-label\"\u003eECU\u003c\/div\u003e\n                        \u003cdiv class=\"result-value\"\u003e${result.ecu}\u003c\/div\u003e\n                    \u003c\/div\u003e\n                    \u003cdiv class=\"result-item\"\u003e\n                        \u003cdiv class=\"result-label\"\u003eTCU\u003c\/div\u003e\n                        \u003cdiv class=\"result-value\"\u003e${result.tcu}\u003c\/div\u003e\n                    \u003c\/div\u003e\n                    \u003cdiv class=\"result-item\"\u003e\n                        \u003cdiv class=\"result-label\"\u003eELV\u003c\/div\u003e\n                        \u003cdiv class=\"result-value\"\u003e${result.elv}\u003c\/div\u003e\n                    \u003c\/div\u003e\n                `;\n                resultDiv.classList.remove('result-error');\n                resultDiv.classList.remove('hidden');\n            } catch (e) {\n                resultDiv.innerHTML = '\u003cdiv class=\"result-title\"\u003eError\u003c\/div\u003e' + e.message;\n                resultDiv.classList.add('result-error');\n                resultDiv.classList.remove('hidden');\n            }\n        });\n    \u003c\/script\u003e\u003c\/p\u003e","brand":"KEY LIBRARY","offers":[{"title":"Default Title","offer_id":57216311492990,"sku":null,"price":0.0,"currency_code":"GBP","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0569\/2751\/8813\/files\/VAGSYNCCALCULATOR_1.jpg?v=1775519596","url":"https:\/\/www.keylibrary.co.uk\/products\/vag-sync-calculator","provider":"Key Library","version":"1.0","type":"link"}