{"id":1032,"date":"2026-10-26T19:27:49","date_gmt":"2026-10-26T19:27:49","guid":{"rendered":"https:\/\/srabons.de\/?page_id=1032"},"modified":"2026-10-26T19:27:52","modified_gmt":"2026-10-26T19:27:52","slug":"omni-convert-studio","status":"publish","type":"page","link":"https:\/\/srabons.de\/en\/omni-convert-studio\/","title":{"rendered":"Omni Convert Studio"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\" class=\"h-full\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=5.0\">\n  <title>OmniConvert &#038; PDF Studio | srabons.de<\/title>\n\n  <!-- Tailwind CSS CDN -->\n  <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n  <script>\n    tailwind.config = {\n      darkMode: 'class',\n      theme: {\n        extend: {\n          colors: {\n            brandDark: '#080d1a',\n            brandSurface: '#0f172a',\n            brandCard: '#1e293b',\n            brandBorder: '#334155',\n            brandTeal: {\n              400: '#2dd4bf',\n              500: '#14b8a6',\n              600: '#0d9488',\n              700: '#0f766e',\n            },\n            brandGold: '#f59e0b',\n          },\n          fontFamily: {\n            sans: ['Inter', 'system-ui', '-apple-system', 'Segoe UI', 'Roboto', 'sans-serif'],\n          }\n        }\n      }\n    }\n  <\/script>\n\n  <!-- Essential Conversion & PDF CDN Libraries -->\n  <!-- 1. PDF-Lib for Merge, Split, Rotate, Watermark, Reorder, Image2PDF -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf-lib\/1.17.1\/pdf-lib.min.js\"><\/script>\n  <!-- 2. PDF.js for PDF rendering to PNG\/JPG\/WebP, text extraction -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.min.js\"><\/script>\n  <!-- 3. Mammoth.js for DOCX -> HTML \/ TXT -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/mammoth\/1.6.0\/mammoth.browser.min.js\"><\/script>\n  <!-- 4. SheetJS (xlsx) for XLSX -> CSV \/ HTML, CSV -> XLSX -->\n  <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.1\/package\/dist\/xlsx.full.min.js\"><\/script>\n  <!-- 5. Marked.js for Markdown -> HTML \/ Text -->\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/marked\/marked.min.js\"><\/script>\n  <!-- 6. JSZip for batch zip packaging -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jszip\/3.10.1\/jszip.min.js\"><\/script>\n  <!-- 7. jsPDF for HTML\/Text\/Doc rendering to PDF -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\n  <!-- 8. FontAwesome 6 for rich iconography -->\n  <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\">\n  <!-- 9. Google API Client (GAPI) and Identity Services for Google Drive integration -->\n  <script src=\"https:\/\/accounts.google.com\/gsi\/client\" async defer><\/script>\n  <script src=\"https:\/\/apis.google.com\/js\/api.js\" async defer><\/script>\n  <!-- 10. Dropbox Drop-in \/ Saver SDK -->\n  <script type=\"text\/javascript\" src=\"https:\/\/www.dropbox.com\/static\/api\/2\/dropins.js\" id=\"dropboxjs\" data-app-key=\"omni-convert-demo\"><\/script>\n\n  <style>\n    \/* Custom Scrollbar *\/\n    ::-webkit-scrollbar { width: 8px; height: 8px; }\n    ::-webkit-scrollbar-track { background: #0b1120; }\n    ::-webkit-scrollbar-thumb { background: #334155; border-radius: 9999px; }\n    ::-webkit-scrollbar-thumb:hover { background: #0d9488; }\n    \n    .checker-pattern {\n      background-color: #0f172a;\n      background-image: \n        linear-gradient(45deg, #1e293b 25%, transparent 25%),\n        linear-gradient(-45deg, #1e293b 25%, transparent 25%),\n        linear-gradient(45deg, transparent 75%, #1e293b 75%),\n        linear-gradient(-45deg, transparent 75%, #1e293b 75%);\n      background-size: 16px 16px;\n      background-position: 0 0, 0 8px, 8px -8px, -8px 0px;\n    }\n    .glow-teal {\n      box-shadow: 0 0 25px -5px rgba(13, 148, 136, 0.4);\n    }\n  <\/style>\n<\/head>\n<body class=\"h-full bg-brandDark text-slate-100 flex flex-col font-sans antialiased selection:bg-brandTeal-600 selection:text-white\">\n\n  <!-- Top Navigation Header -->\n  <header class=\"sticky top-0 z-40 bg-brandSurface\/90 backdrop-blur border-b border-brandBorder shadow-lg\">\n    <div class=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between gap-4\">\n      \n      <!-- Brand Logo -->\n      <a href=\"https:\/\/srabons.de\" class=\"flex items-center gap-3 group focus:outline-none\">\n        <div class=\"w-10 h-10 rounded-xl bg-gradient-to-tr from-brandTeal-600 via-teal-500 to-cyan-500 text-white flex items-center justify-center font-bold text-lg shadow-md group-hover:scale-105 transition-transform\">\n          <i class=\"fa-solid fa-arrows-split-up-and-left\"><\/i>\n        <\/div>\n        <div class=\"flex flex-col\">\n          <div class=\"flex items-center gap-1.5 leading-none\">\n            <span class=\"font-black tracking-tight text-xl text-white\">OmniConvert<\/span>\n            <span class=\"text-xs px-1.5 py-0.5 rounded bg-brandTeal-500\/20 text-brandTeal-400 font-bold border border-brandTeal-500\/30\">Studio<\/span>\n          <\/div>\n          <span class=\"text-[11px] text-slate-400 font-medium\">by srabons.de &bull; Privacy First Client &#038; Cloud<\/span>\n        <\/div>\n      <\/a>\n\n      <!-- Quick Action Badges & Cloud Indicators -->\n      <div class=\"flex items-center gap-2 sm:gap-3\">\n        <!-- Search bar button on mobile or full search on desktop -->\n        <div class=\"relative hidden md:block w-64\">\n          <i class=\"fa-solid fa-magnifying-glass absolute left-3 top-2.5 text-xs text-slate-400\"><\/i>\n          <input type=\"text\" id=\"toolSearchInput\" placeholder=\"Quick find tool (e.g. merge, docx)...\" \n                 class=\"w-full pl-8 pr-3 py-1.5 text-xs rounded-xl bg-brandCard border border-brandBorder text-slate-200 placeholder-slate-500 focus:outline-none focus:border-brandTeal-500 focus:ring-1 focus:ring-brandTeal-500 transition-all\">\n        <\/div>\n\n        <button onclick=\"openCloudConfigModal()\" class=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold rounded-xl bg-brandCard hover:bg-slate-700\/80 border border-brandBorder text-slate-200 transition-colors\" title=\"Manage Google Drive &#038; Dropbox Cloud Settings\">\n          <i class=\"fa-brands fa-google-drive text-amber-400\"><\/i>\n          <i class=\"fa-brands fa-dropbox text-blue-400\"><\/i>\n          <span class=\"hidden sm:inline\">Cloud Setup<\/span>\n        <\/button>\n\n        <a href=\"https:\/\/srabons.de\" class=\"px-3 py-1.5 rounded-xl text-xs font-semibold bg-brandTeal-600\/20 text-brandTeal-400 border border-brandTeal-600\/30 hover:bg-brandTeal-600 hover:text-white transition-all\">\n          <i class=\"fa-solid fa-house mr-1\"><\/i> srabons.de\n        <\/a>\n      <\/div>\n    <\/div>\n\n    <!-- Phase Category Navigation Tabs -->\n    <div class=\"border-t border-brandBorder\/60 bg-brandDark\/50 px-4 sm:px-6 lg:px-8 overflow-x-auto no-scrollbar\">\n      <div class=\"max-w-7xl mx-auto flex items-center gap-1 sm:gap-2 py-2 text-xs font-semibold min-w-max\">\n        <button onclick=\"switchCategory('all')\" class=\"category-tab active px-3 py-1.5 rounded-lg bg-brandTeal-600 text-white transition-colors\" data-category=\"all\">\n          <i class=\"fa-solid fa-shapes mr-1.5\"><\/i> All Tools (25+)\n        <\/button>\n        <button onclick=\"switchCategory('phase1')\" class=\"category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors\" data-category=\"phase1\">\n          <i class=\"fa-solid fa-bolt mr-1.5 text-emerald-400\"><\/i> Phase 1: MVP Images\n        <\/button>\n        <button onclick=\"switchCategory('phase2')\" class=\"category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors\" data-category=\"phase2\">\n          <i class=\"fa-solid fa-file-word mr-1.5 text-blue-400\"><\/i> Phase 2: Documents\n        <\/button>\n        <button onclick=\"switchCategory('phase3')\" class=\"category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors\" data-category=\"phase3\">\n          <i class=\"fa-solid fa-chart-pie mr-1.5 text-emerald-500\"><\/i> Phase 3: Sheets &#038; Slides\n        <\/button>\n        <button onclick=\"switchCategory('phase4')\" class=\"category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors\" data-category=\"phase4\">\n          <i class=\"fa-solid fa-code mr-1.5 text-purple-400\"><\/i> Phase 4: Web &#038; Dev\n        <\/button>\n        <button onclick=\"switchCategory('phase5')\" class=\"category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors\" data-category=\"phase5\">\n          <i class=\"fa-solid fa-toolbox mr-1.5 text-amber-400\"><\/i> Phase 5: PDF Studio\n        <\/button>\n      <\/div>\n    <\/div>\n  <\/header>\n\n  <!-- Main Application Body -->\n  <main class=\"flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 lg:p-8 flex flex-col gap-6\">\n\n    <!-- Active Tool Banner \/ Headline -->\n    <div class=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 bg-brandSurface border border-brandBorder rounded-2xl p-4 sm:p-6 shadow-md\">\n      <div class=\"flex items-center gap-4\">\n        <div id=\"activeToolIcon\" class=\"w-14 h-14 rounded-2xl bg-brandTeal-600\/20 border border-brandTeal-500\/30 text-brandTeal-400 flex items-center justify-center text-2xl shrink-0 font-bold\">\n          <i class=\"fa-solid fa-file-pdf\"><\/i>\n        <\/div>\n        <div>\n          <div class=\"flex items-center gap-2\">\n            <h1 id=\"activeToolTitle\" class=\"text-xl sm:text-2xl font-black text-white\">PDF to PNG Converter<\/h1>\n            <span id=\"activeToolPhaseBadge\" class=\"text-[11px] font-bold px-2 py-0.5 rounded-full bg-emerald-500\/10 text-emerald-400 border border-emerald-500\/20\">Phase 1<\/span>\n          <\/div>\n          <p id=\"activeToolDesc\" class=\"text-xs sm:text-sm text-slate-400 mt-1\">High-fidelity client-side page rendering into transparent or solid PNG format with batch cloud export.<\/p>\n        <\/div>\n      <\/div>\n      \n      <!-- Quick Utility Button: Browse Tools drawer -->\n      <button onclick=\"scrollToTools()\" class=\"inline-flex items-center gap-2 px-3.5 py-2 text-xs font-semibold rounded-xl bg-brandCard hover:bg-slate-700\/80 border border-brandBorder text-slate-200 transition-colors\">\n        <i class=\"fa-solid fa-grid-2\"><\/i> Switch Tool\n      <\/button>\n    <\/div>\n\n    <!-- Workspace Grid: Options & Upload Area -->\n    <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-6 items-start\">\n      \n      <!-- Left Column: Tool Options & Configuration -->\n      <aside class=\"lg:col-span-1 bg-brandSurface border border-brandBorder rounded-2xl p-5 shadow-sm flex flex-col gap-4\">\n        <div class=\"flex items-center justify-between pb-3 border-b border-brandBorder\">\n          <h2 class=\"text-xs font-bold uppercase tracking-wider text-slate-400\">Tool Parameters<\/h2>\n          <span class=\"text-[11px] text-brandTeal-400 font-semibold flex items-center gap-1\">\n            <i class=\"fa-solid fa-shield-halved\"><\/i> Client Protected\n          <\/span>\n        <\/div>\n\n        <!-- Dynamic Options Container adjusted to chosen tool -->\n        <div id=\"dynamicToolOptions\" class=\"space-y-4\">\n          <!-- Populated dynamically via JS: Quality, Watermark, Rotation Angle, etc. -->\n        <\/div>\n\n        <!-- Cloud Save Targets Selection -->\n        <div class=\"pt-3 border-t border-brandBorder\">\n          <label class=\"block text-xs font-bold uppercase text-slate-400 mb-2\">Cloud Export Presets<\/label>\n          <div class=\"grid grid-cols-2 gap-2 text-xs\">\n            <button onclick=\"triggerGoogleDriveBatchExport()\" class=\"flex items-center justify-center gap-2 p-2.5 rounded-xl bg-brandCard hover:bg-slate-700\/80 border border-brandBorder font-semibold text-slate-200 transition-colors\">\n              <i class=\"fa-brands fa-google-drive text-amber-400\"><\/i> Google Drive\n            <\/button>\n            <button onclick=\"triggerDropboxBatchExport()\" class=\"flex items-center justify-center gap-2 p-2.5 rounded-xl bg-brandCard hover:bg-slate-700\/80 border border-brandBorder font-semibold text-slate-200 transition-colors\">\n              <i class=\"fa-brands fa-dropbox text-blue-400\"><\/i> Dropbox\n            <\/button>\n          <\/div>\n          <p class=\"text-[10px] text-slate-500 mt-1.5 text-center\">Export results directly to your cloud storage account without leaving the page.<\/p>\n        <\/div>\n      <\/aside>\n\n      <!-- Right Column: Drag & Drop Zone + Execution Workspace -->\n      <section class=\"lg:col-span-2 flex flex-col gap-4\">\n        \n        <!-- Dropzone Area -->\n        <div id=\"dropzoneContainer\" class=\"relative group cursor-pointer border-2 border-dashed border-brandBorder hover:border-brandTeal-500 rounded-3xl p-8 sm:p-12 text-center bg-brandSurface hover:bg-brandCard\/40 transition-all flex flex-col items-center justify-center\">\n          <input type=\"file\" id=\"fileInput\" multiple class=\"absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10\">\n          \n          <div class=\"w-16 h-16 rounded-2xl bg-brandTeal-600\/10 border border-brandTeal-500\/20 text-brandTeal-400 flex items-center justify-center text-3xl mb-3 group-hover:scale-110 transition-transform\">\n            <i class=\"fa-solid fa-cloud-arrow-up\"><\/i>\n          <\/div>\n\n          <h3 class=\"text-base sm:text-lg font-bold text-white mb-1\">Drag and drop files here, or <span class=\"text-brandTeal-400 underline underline-offset-2\">browse<\/span><\/h3>\n          <p id=\"acceptedFormatsHint\" class=\"text-xs text-slate-400 max-w-md\">Accepts PDF documents up to 100MB per batch.<\/p>\n\n          <div class=\"mt-5 flex flex-wrap items-center justify-center gap-4 text-xs text-slate-400 font-medium\">\n            <span class=\"inline-flex items-center gap-1.5\"><i class=\"fa-solid fa-check text-brandTeal-400\"><\/i> Batch Queue<\/span>\n            <span class=\"inline-flex items-center gap-1.5\"><i class=\"fa-solid fa-check text-brandTeal-400\"><\/i> Zip Archive<\/span>\n            <span class=\"inline-flex items-center gap-1.5\"><i class=\"fa-solid fa-check text-brandTeal-400\"><\/i> Zero Server Uploads<\/span>\n          <\/div>\n        <\/div>\n\n        <!-- Conversion Queue & Results Display -->\n        <div id=\"queueSection\" class=\"hidden bg-brandSurface border border-brandBorder rounded-2xl p-5 flex-col gap-4 shadow-sm\">\n          <div class=\"flex flex-wrap items-center justify-between gap-3 pb-3 border-b border-brandBorder\">\n            <div class=\"flex items-center gap-2\">\n              <h3 class=\"text-sm font-bold text-white uppercase tracking-wider\">Processing Queue<\/h3>\n              <span id=\"queueBadgeCount\" class=\"text-xs px-2 py-0.5 rounded-full bg-brandTeal-500\/20 text-brandTeal-400 font-bold\">0 files<\/span>\n            <\/div>\n\n            <!-- Batch Actions -->\n            <div class=\"flex items-center gap-2\">\n              <button id=\"clearQueueBtn\" onclick=\"clearQueue()\" class=\"px-2.5 py-1.5 rounded-lg text-xs font-semibold text-rose-400 hover:bg-rose-500\/10 border border-rose-500\/20 transition-colors\">\n                <i class=\"fa-solid fa-trash-can mr-1\"><\/i> Clear\n              <\/button>\n              <button id=\"executeBatchBtn\" onclick=\"executeCurrentTool()\" class=\"px-4 py-1.5 rounded-lg text-xs font-bold bg-brandTeal-600 hover:bg-brandTeal-500 text-white transition-all shadow-md shadow-brandTeal-600\/30 flex items-center gap-1.5\">\n                <i class=\"fa-solid fa-play\"><\/i> Convert All\n              <\/button>\n              <button id=\"downloadZipBtn\" onclick=\"downloadAllAsZip()\" class=\"hidden px-3.5 py-1.5 rounded-lg text-xs font-bold bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-500 hover:to-teal-500 text-white transition-all shadow flex items-center gap-1.5\">\n                <i class=\"fa-solid fa-file-zipper\"><\/i> Download ZIP\n              <\/button>\n            <\/div>\n          <\/div>\n\n          <!-- Overall Progress Bar -->\n          <div id=\"overallProgressWrapper\" class=\"hidden\">\n            <div class=\"flex justify-between text-xs text-slate-300 font-semibold mb-1\">\n              <span id=\"progressStatusLabel\">Converting&#8230;<\/span>\n              <span id=\"progressPercentLabel\">0%<\/span>\n            <\/div>\n            <div class=\"w-full h-2 rounded-full bg-slate-800 overflow-hidden\">\n              <div id=\"overallProgressBar\" class=\"h-full bg-gradient-to-r from-brandTeal-500 to-emerald-400 transition-all duration-200\" style=\"width: 0%\"><\/div>\n            <\/div>\n          <\/div>\n\n          <!-- Queue Items List -->\n          <div id=\"queueListContainer\" class=\"flex flex-col gap-2.5 max-h-[480px] overflow-y-auto pr-1\">\n            <!-- Dynamic Item Cards -->\n          <\/div>\n        <\/div>\n\n      <\/section>\n    <\/div>\n\n    <!-- Complete 25+ Tools Directory (Browse & Filter Grid) -->\n    <section id=\"toolsDirectory\" class=\"mt-8 flex flex-col gap-4\">\n      <div class=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 border-b border-brandBorder pb-3\">\n        <div>\n          <h2 class=\"text-lg font-black text-white flex items-center gap-2\">\n            <i class=\"fa-solid fa-layer-group text-brandTeal-400\"><\/i> All 25+ Conversion &#038; PDF Tools\n          <\/h2>\n          <p class=\"text-xs text-slate-400\">Select any converter or utility below to launch it immediately.<\/p>\n        <\/div>\n        <span id=\"toolsCountIndicator\" class=\"text-xs px-2.5 py-1 rounded-full bg-brandCard text-slate-300 border border-brandBorder font-semibold\">25 Tools Available<\/span>\n      <\/div>\n\n      <!-- Tools Grid -->\n      <div id=\"toolsGridContainer\" class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4\">\n        <!-- Rendered via JS -->\n      <\/div>\n    <\/section>\n\n  <\/main>\n\n  <!-- Cloud Storage Configuration Modal -->\n  <div id=\"cloudModal\" class=\"fixed inset-0 z-50 hidden bg-brandDark\/80 backdrop-blur-sm items-center justify-center p-4\">\n    <div class=\"relative max-w-lg w-full bg-brandSurface border border-brandBorder rounded-3xl p-6 shadow-2xl flex flex-col gap-5\">\n      <div class=\"flex items-center justify-between pb-3 border-b border-brandBorder\">\n        <h3 class=\"font-black text-white text-base flex items-center gap-2\">\n          <i class=\"fa-solid fa-cloud-arrow-up text-brandTeal-400\"><\/i> Cloud Storage Integrations\n        <\/h3>\n        <button onclick=\"closeCloudConfigModal()\" class=\"text-slate-400 hover:text-white p-1\">\n          <i class=\"fa-solid fa-xmark text-lg\"><\/i>\n        <\/button>\n      <\/div>\n\n      <div class=\"space-y-4 text-xs text-slate-300 leading-relaxed\">\n        <!-- Google Drive Section -->\n        <div class=\"p-4 rounded-2xl bg-brandCard border border-brandBorder space-y-2\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"font-bold text-white flex items-center gap-2\">\n              <i class=\"fa-brands fa-google-drive text-amber-400 text-base\"><\/i> Google Drive Export\n            <\/span>\n            <span id=\"gdriveStatusBadge\" class=\"text-[10px] px-2 py-0.5 rounded bg-emerald-500\/20 text-emerald-400 font-bold border border-emerald-500\/30\">Client Ready<\/span>\n          <\/div>\n          <p class=\"text-slate-400\">OmniConvert connects directly using the Google Drive Web API. Provide your Client ID or authenticate to send converted outputs straight to your Drive folder.<\/p>\n          <input type=\"text\" id=\"gdriveClientId\" placeholder=\"Google OAuth Client ID (optional, or uses demo sandbox)\" \n                 class=\"w-full p-2.5 rounded-xl bg-brandDark border border-brandBorder text-slate-200 text-xs focus:outline-none focus:border-brandTeal-500\">\n        <\/div>\n\n        <!-- Dropbox Section -->\n        <div class=\"p-4 rounded-2xl bg-brandCard border border-brandBorder space-y-2\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"font-bold text-white flex items-center gap-2\">\n              <i class=\"fa-brands fa-dropbox text-blue-400 text-base\"><\/i> Dropbox Saver\n            <\/span>\n            <span id=\"dropboxStatusBadge\" class=\"text-[10px] px-2 py-0.5 rounded bg-emerald-500\/20 text-emerald-400 font-bold border border-emerald-500\/30\">Drop-in Enabled<\/span>\n          <\/div>\n          <p class=\"text-slate-400\">Saves files seamlessly to your personal or team Dropbox account using the official Dropbox Drop-in Chooser &#038; Saver library.<\/p>\n          <input type=\"text\" id=\"dropboxAppKey\" placeholder=\"Dropbox App Key (optional, defaults to drop-in mode)\" \n                 class=\"w-full p-2.5 rounded-xl bg-brandDark border border-brandBorder text-slate-200 text-xs focus:outline-none focus:border-brandTeal-500\">\n        <\/div>\n      <\/div>\n\n      <div class=\"flex justify-end gap-2 pt-2 border-t border-brandBorder\">\n        <button onclick=\"closeCloudConfigModal()\" class=\"px-4 py-2 rounded-xl text-xs font-semibold bg-brandCard text-slate-300 hover:text-white\">Close<\/button>\n        <button onclick=\"saveCloudSettings()\" class=\"px-4 py-2 rounded-xl text-xs font-bold bg-brandTeal-600 hover:bg-brandTeal-500 text-white\">Save Settings<\/button>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <!-- Preview Modal for Text \/ HTML \/ Images -->\n  <div id=\"previewModal\" class=\"fixed inset-0 z-50 hidden bg-brandDark\/85 backdrop-blur-sm items-center justify-center p-4\">\n    <div class=\"relative max-w-4xl w-full h-[85vh] bg-brandSurface border border-brandBorder rounded-3xl shadow-2xl flex flex-col overflow-hidden\">\n      <div class=\"flex items-center justify-between px-5 py-3.5 border-b border-brandBorder bg-brandCard\/40\">\n        <h3 id=\"previewModalTitle\" class=\"font-bold text-white text-sm\">File Preview<\/h3>\n        <button onclick=\"closePreviewModal()\" class=\"text-slate-400 hover:text-white p-1\">\n          <i class=\"fa-solid fa-xmark text-lg\"><\/i>\n        <\/button>\n      <\/div>\n      <div id=\"previewModalBody\" class=\"flex-1 p-6 overflow-auto checker-pattern flex items-center justify-center\">\n        <!-- Rendered preview content -->\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <!-- Toast Notification Container -->\n  <div id=\"toastContainer\" class=\"fixed bottom-6 right-6 z-50 flex flex-col gap-2 pointer-events-none\"><\/div>\n\n  <!-- Footer -->\n  <footer class=\"mt-auto border-t border-brandBorder\/80 bg-brandSurface\/80 py-6 text-center text-xs text-slate-400\">\n    <div class=\"max-w-7xl mx-auto px-4 flex flex-col sm:flex-row items-center justify-between gap-3\">\n      <div class=\"flex items-center gap-2\">\n        <span class=\"w-2 h-2 rounded-full bg-emerald-500 animate-pulse\"><\/span>\n        <span>&copy; <span id=\"currentYear\"><\/span> <a href=\"https:\/\/srabons.de\" class=\"text-brandTeal-400 font-bold hover:underline\">srabons.de<\/a> &bull; OmniConvert &amp; PDF Studio<\/span>\n      <\/div>\n      <div class=\"flex items-center gap-4 text-[11px] text-slate-500\">\n        <span>100% Client-Side Privacy<\/span>\n        <span>&bull;<\/span>\n        <span>Google Drive &amp; Dropbox Integration<\/span>\n        <span>&bull;<\/span>\n        <span>25+ Real-Time Converters<\/span>\n      <\/div>\n    <\/div>\n  <\/footer>\n\n  <!-- Application Logic -->\n  <script>\n    \/\/ Ensure PDF.js worker is properly configured\n    if (typeof pdfjsLib !== 'undefined') {\n      pdfjsLib.GlobalWorkerOptions.workerSrc = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.worker.min.js';\n    }\n\n    \/\/ Dynamic Year\n    document.getElementById('currentYear').textContent = new Date().getFullYear();\n\n    \/\/ =========================================================================\n    \/\/ 1. TOOL DEFINITIONS & ARCHITECTURAL PHASES\n    \/\/ =========================================================================\n    const ALL_TOOLS = [\n      \/\/ --- Phase 1: MVP (Launch Fast) ---\n      {\n        id: 'pdf-to-png',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'PDF \u2192 PNG',\n        icon: 'fa-solid fa-image',\n        desc: 'Convert PDF pages into crisp lossless PNG images.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'image_render'\n      },\n      {\n        id: 'pdf-to-jpg',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'PDF \u2192 JPG',\n        icon: 'fa-solid fa-file-image',\n        desc: 'Convert PDF pages into lightweight, high-compatibility JPG photos.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'image_render'\n      },\n      {\n        id: 'pdf-to-webp',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'PDF \u2192 WEBP',\n        icon: 'fa-solid fa-photo-film',\n        desc: 'Convert PDF pages into modern, ultra-compressed WebP format.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'image_render'\n      },\n      {\n        id: 'jpg-to-pdf',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'JPG \u2192 PDF',\n        icon: 'fa-solid fa-file-pdf',\n        desc: 'Merge individual JPG pictures into a unified clean PDF document.',\n        accept: '.jpg,.jpeg,image\/jpeg',\n        optionsType: 'image_to_pdf'\n      },\n      {\n        id: 'png-to-pdf',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'PNG \u2192 PDF',\n        icon: 'fa-solid fa-file-pdf',\n        desc: 'Pack PNG graphics into an organized printable PDF.',\n        accept: '.png,image\/png',\n        optionsType: 'image_to_pdf'\n      },\n      {\n        id: 'webp-to-pdf',\n        phase: 'phase1',\n        phaseLabel: 'Phase 1: MVP',\n        title: 'WEBP \u2192 PDF',\n        icon: 'fa-solid fa-file-pdf',\n        desc: 'Bundle WebP assets directly into standard PDF pages.',\n        accept: '.webp,image\/webp',\n        optionsType: 'image_to_pdf'\n      },\n\n      \/\/ --- Phase 2: Document Converters ---\n      {\n        id: 'docx-to-pdf',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'DOCX \u2192 PDF',\n        icon: 'fa-solid fa-file-word',\n        desc: 'Convert Microsoft Word documents into portable PDFs.',\n        accept: '.docx,application\/vnd.openxmlformats-officedocument.wordprocessingml.document',\n        optionsType: 'doc_to_pdf'\n      },\n      {\n        id: 'pdf-to-docx',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'PDF \u2192 DOCX',\n        icon: 'fa-solid fa-file-word',\n        desc: 'Extract and assemble PDF text layout into an editable Word document.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'text_extract'\n      },\n      {\n        id: 'docx-to-html',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'DOCX \u2192 HTML',\n        icon: 'fa-solid fa-code',\n        desc: 'Turn Word documents into clean semantic web HTML.',\n        accept: '.docx,application\/vnd.openxmlformats-officedocument.wordprocessingml.document',\n        optionsType: 'none'\n      },\n      {\n        id: 'docx-to-txt',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'DOCX \u2192 TXT',\n        icon: 'fa-solid fa-file-lines',\n        desc: 'Strip formatting and extract raw plaintext from Word files.',\n        accept: '.docx,application\/vnd.openxmlformats-officedocument.wordprocessingml.document',\n        optionsType: 'none'\n      },\n      {\n        id: 'txt-to-pdf',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'TXT \u2192 PDF',\n        icon: 'fa-solid fa-file-pdf',\n        desc: 'Turn plain text logs or notes into a formatted PDF document.',\n        accept: '.txt,text\/plain',\n        optionsType: 'none'\n      },\n      {\n        id: 'pdf-to-txt',\n        phase: 'phase2',\n        phaseLabel: 'Phase 2: Docs',\n        title: 'PDF \u2192 TXT',\n        icon: 'fa-solid fa-file-lines',\n        desc: 'Fast text stream extraction from all pages of a PDF document.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'none'\n      },\n\n      \/\/ --- Phase 3: Business & Education ---\n      {\n        id: 'xlsx-to-pdf',\n        phase: 'phase3',\n        phaseLabel: 'Phase 3: Business',\n        title: 'XLSX \u2192 PDF',\n        icon: 'fa-solid fa-file-excel',\n        desc: 'Convert Excel spreadsheets and worksheets into printable PDF tables.',\n        accept: '.xlsx,.xls,application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n        optionsType: 'none'\n      },\n      {\n        id: 'csv-to-xlsx',\n        phase: 'phase3',\n        phaseLabel: 'Phase 3: Business',\n        title: 'CSV \u2192 XLSX',\n        icon: 'fa-solid fa-file-csv',\n        desc: 'Convert comma-separated data into formatted Excel workbook spreadsheets.',\n        accept: '.csv,text\/csv',\n        optionsType: 'none'\n      },\n      {\n        id: 'xlsx-to-csv',\n        phase: 'phase3',\n        phaseLabel: 'Phase 3: Business',\n        title: 'XLSX \u2192 CSV',\n        icon: 'fa-solid fa-file-csv',\n        desc: 'Export sheet worksheets into clean universal CSV files.',\n        accept: '.xlsx,.xls,application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n        optionsType: 'none'\n      },\n      {\n        id: 'pptx-to-pdf',\n        phase: 'phase3',\n        phaseLabel: 'Phase 3: Business',\n        title: 'PPTX \u2192 PDF',\n        icon: 'fa-solid fa-file-powerpoint',\n        desc: 'Extract PowerPoint slides & presentations into a reading PDF.',\n        accept: '.pptx,application\/vnd.openxmlformats-officedocument.presentationml.presentation',\n        optionsType: 'none'\n      },\n      {\n        id: 'pptx-to-png',\n        phase: 'phase3',\n        phaseLabel: 'Phase 3: Business',\n        title: 'PPTX \u2192 PNG',\n        icon: 'fa-solid fa-images',\n        desc: 'Extract embedded images & presentation media from PPTX slides.',\n        accept: '.pptx,application\/vnd.openxmlformats-officedocument.presentationml.presentation',\n        optionsType: 'none'\n      },\n\n      \/\/ --- Phase 4: Web & Developer Tools ---\n      {\n        id: 'html-to-pdf',\n        phase: 'phase4',\n        phaseLabel: 'Phase 4: Dev',\n        title: 'HTML \u2192 PDF',\n        icon: 'fa-solid fa-code',\n        desc: 'Render raw HTML files or source code into a styled PDF document.',\n        accept: '.html,.htm,text\/html',\n        optionsType: 'none'\n      },\n      {\n        id: 'pdf-to-html',\n        phase: 'phase4',\n        phaseLabel: 'Phase 4: Dev',\n        title: 'PDF \u2192 HTML',\n        icon: 'fa-solid fa-globe',\n        desc: 'Convert PDF content into styled HTML documents with structured paragraphs.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'none'\n      },\n      {\n        id: 'md-to-html',\n        phase: 'phase4',\n        phaseLabel: 'Phase 4: Dev',\n        title: 'MD \u2192 HTML',\n        icon: 'fa-brands fa-markdown',\n        desc: 'Compile Markdown files (.md) into clean HTML markup.',\n        accept: '.md,text\/markdown',\n        optionsType: 'none'\n      },\n      {\n        id: 'md-to-pdf',\n        phase: 'phase4',\n        phaseLabel: 'Phase 4: Dev',\n        title: 'MD \u2192 PDF',\n        icon: 'fa-solid fa-file-pdf',\n        desc: 'Turn Markdown documentation directly into formatted PDF docs.',\n        accept: '.md,text\/markdown',\n        optionsType: 'none'\n      },\n      {\n        id: 'html-to-md',\n        phase: 'phase4',\n        phaseLabel: 'Phase 4: Dev',\n        title: 'HTML \u2192 MD',\n        icon: 'fa-brands fa-markdown',\n        desc: 'Convert web HTML markup back into simple Markdown text.',\n        accept: '.html,.htm,text\/html',\n        optionsType: 'none'\n      },\n\n      \/\/ --- Phase 5: Advanced PDF Toolkit ---\n      {\n        id: 'merge-pdf',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Merge PDF',\n        icon: 'fa-solid fa-object-group',\n        desc: 'Combine multiple PDF files into one single PDF in sequential order.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'none'\n      },\n      {\n        id: 'split-pdf',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Split PDF',\n        icon: 'fa-solid fa-scissors',\n        desc: 'Divide a multi-page PDF into single page files or extracted page sets.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'split'\n      },\n      {\n        id: 'compress-pdf',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Compress PDF',\n        icon: 'fa-solid fa-file-zipper',\n        desc: 'Optimize and rebuild PDF structures to reduce file footprint.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'compress'\n      },\n      {\n        id: 'rotate-pdf',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Rotate PDF',\n        icon: 'fa-solid fa-rotate',\n        desc: 'Rotate PDF pages by 90, 180, or 270 degrees permanently.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'rotate'\n      },\n      {\n        id: 'extract-images',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Extract Images',\n        icon: 'fa-solid fa-file-export',\n        desc: 'Render and extract high-resolution images from every page of a PDF.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'image_render'\n      },\n      {\n        id: 'add-watermark',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'Add Watermark',\n        icon: 'fa-solid fa-stamp',\n        desc: 'Stamp custom watermark text across all pages with custom color & opacity.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'watermark'\n      },\n      {\n        id: 'reorder-pages',\n        phase: 'phase5',\n        phaseLabel: 'Phase 5: Studio',\n        title: 'PDF Page Reorder',\n        icon: 'fa-solid fa-sort',\n        desc: 'Reverse, invert, or customize page ordering of PDF documents.',\n        accept: '.pdf,application\/pdf',\n        optionsType: 'reorder'\n      }\n    ];\n\n    \/\/ =========================================================================\n    \/\/ 2. STATE MANAGEMENT\n    \/\/ =========================================================================\n    let currentTool = ALL_TOOLS[0];\n    let fileQueue = []; \/\/ [{ id, file, status: 'ready'|'processing'|'done'|'error', outputBlob, outputName, previewUrl, errorMsg }]\n    let cloudSettings = {\n      gdriveClientId: localStorage.getItem('omni_gdrive_id') || '',\n      dropboxAppKey: localStorage.getItem('omni_dropbox_key') || ''\n    };\n\n    \/\/ Initialize UI on load\n    window.addEventListener('DOMContentLoaded', () => {\n      renderToolsGrid(ALL_TOOLS);\n      selectTool(ALL_TOOLS[0].id);\n      setupDropzone();\n      setupSearch();\n    });\n\n    \/\/ =========================================================================\n    \/\/ 3. UI RENDERING: TOOLS DIRECTORY & OPTIONS\n    \/\/ =========================================================================\n    function renderToolsGrid(toolsList) {\n      const container = document.getElementById('toolsGridContainer');\n      container.innerHTML = '';\n      document.getElementById('toolsCountIndicator').textContent = `${toolsList.length} Tools Available`;\n\n      toolsList.forEach(tool => {\n        const card = document.createElement('div');\n        const isActive = tool.id === currentTool.id;\n        card.className = `p-4 rounded-2xl bg-brandSurface border ${isActive ? 'border-brandTeal-500 glow-teal' : 'border-brandBorder'} hover:border-brandTeal-500\/60 transition-all cursor-pointer group flex flex-col justify-between`;\n        card.onclick = () => {\n          selectTool(tool.id);\n          window.scrollTo({ top: 0, behavior: 'smooth' });\n        };\n\n        const phaseColors = {\n          phase1: 'text-emerald-400 bg-emerald-500\/10 border-emerald-500\/20',\n          phase2: 'text-blue-400 bg-blue-500\/10 border-blue-500\/20',\n          phase3: 'text-emerald-500 bg-emerald-500\/10 border-emerald-500\/20',\n          phase4: 'text-purple-400 bg-purple-500\/10 border-purple-500\/20',\n          phase5: 'text-amber-400 bg-amber-500\/10 border-amber-500\/20',\n        };\n\n        card.innerHTML = `\n          <div>\n            <div class=\"flex items-center justify-between mb-3\">\n              <span class=\"text-[10px] font-bold px-2 py-0.5 rounded-full border ${phaseColors[tool.phase] || 'text-slate-300'}\">\n                ${tool.phaseLabel}\n              <\/span>\n              <div class=\"w-8 h-8 rounded-lg bg-brandCard flex items-center justify-center text-brandTeal-400 text-sm group-hover:scale-110 transition-transform\">\n                <i class=\"${tool.icon}\"><\/i>\n              <\/div>\n            <\/div>\n            <h3 class=\"font-bold text-white text-sm mb-1 group-hover:text-brandTeal-400 transition-colors\">${tool.title}<\/h3>\n            <p class=\"text-[11px] text-slate-400 leading-relaxed\">${tool.desc}<\/p>\n          <\/div>\n          <div class=\"mt-4 pt-2 border-t border-brandBorder\/60 flex items-center justify-between text-[11px] font-semibold text-brandTeal-400\">\n            <span>Use Tool<\/span>\n            <i class=\"fa-solid fa-arrow-right text-[10px] transform group-hover:translate-x-1 transition-transform\"><\/i>\n          <\/div>\n        `;\n        container.appendChild(card);\n      });\n    }\n\n    function switchCategory(cat) {\n      document.querySelectorAll('.category-tab').forEach(t => {\n        if (t.dataset.category === cat) {\n          t.className = 'category-tab active px-3 py-1.5 rounded-lg bg-brandTeal-600 text-white transition-colors';\n        } else {\n          t.className = 'category-tab px-3 py-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-brandCard transition-colors';\n        }\n      });\n\n      if (cat === 'all') {\n        renderToolsGrid(ALL_TOOLS);\n      } else {\n        const filtered = ALL_TOOLS.filter(t => t.phase === cat);\n        renderToolsGrid(filtered);\n      }\n    }\n\n    function setupSearch() {\n      const search = document.getElementById('toolSearchInput');\n      search.addEventListener('input', (e) => {\n        const q = e.target.value.toLowerCase().trim();\n        const filtered = ALL_TOOLS.filter(t => \n          t.title.toLowerCase().includes(q) || \n          t.desc.toLowerCase().includes(q) || \n          t.id.toLowerCase().includes(q)\n        );\n        renderToolsGrid(filtered);\n      });\n    }\n\n    function selectTool(toolId) {\n      const match = ALL_TOOLS.find(t => t.id === toolId);\n      if (!match) return;\n      currentTool = match;\n\n      document.getElementById('activeToolTitle').textContent = match.title;\n      document.getElementById('activeToolDesc').textContent = match.desc;\n      document.getElementById('activeToolIcon').innerHTML = `<i class=\"${match.icon}\"><\/i>`;\n      document.getElementById('activeToolPhaseBadge').textContent = match.phaseLabel;\n      document.getElementById('fileInput').accept = match.accept;\n      document.getElementById('acceptedFormatsHint').textContent = `Accepts ${match.accept.replace(\/,\/g, ', ')} files. All operations execute locally in your browser.`;\n\n      renderToolOptions(match);\n      renderToolsGrid(ALL_TOOLS);\n    }\n\n    function renderToolOptions(tool) {\n      const container = document.getElementById('dynamicToolOptions');\n      container.innerHTML = '';\n\n      if (tool.optionsType === 'image_render') {\n        container.innerHTML = `\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Rendering Scale (DPI)<\/label>\n            <select id=\"optionScale\" class=\"w-full text-xs font-semibold rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n              <option value=\"1.0\">Standard 1x (Fast \/ ~96 DPI)<\/option>\n              <option value=\"1.5\" selected>High Definition 1.5x (Recommended \/ ~150 DPI)<\/option>\n              <option value=\"2.0\">Ultra Print 2x (~200 - 300 DPI)<\/option>\n            <\/select>\n          <\/div>\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Background Fill<\/label>\n            <select id=\"optionBgColor\" class=\"w-full text-xs font-semibold rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n              <option value=\"#ffffff\" selected>Solid White (Universal)<\/option>\n              <option value=\"transparent\">Transparent (PNG only)<\/option>\n            <\/select>\n          <\/div>\n        `;\n      } else if (tool.optionsType === 'watermark') {\n        container.innerHTML = `\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Watermark Text<\/label>\n            <input type=\"text\" id=\"optionWatermarkText\" value=\"CONFIDENTIAL - srabons.de\" class=\"w-full text-xs rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n          <\/div>\n          <div class=\"grid grid-cols-2 gap-2\">\n            <div>\n              <label class=\"block text-xs font-semibold text-slate-300 mb-1\">Color<\/label>\n              <input type=\"color\" id=\"optionWatermarkColor\" value=\"#ff0000\" class=\"w-full h-8 rounded-lg bg-brandCard border border-brandBorder cursor-pointer\">\n            <\/div>\n            <div>\n              <label class=\"block text-xs font-semibold text-slate-300 mb-1\">Opacity<\/label>\n              <select id=\"optionWatermarkOpacity\" class=\"w-full text-xs rounded-xl border border-brandBorder bg-brandCard p-1.5 text-slate-200\">\n                <option value=\"0.2\">20% Subtle<\/option>\n                <option value=\"0.4\" selected>40% Balanced<\/option>\n                <option value=\"0.7\">70% Prominent<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n        `;\n      } else if (tool.optionsType === 'rotate') {\n        container.innerHTML = `\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Rotation Angle<\/label>\n            <select id=\"optionRotateAngle\" class=\"w-full text-xs font-semibold rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n              <option value=\"90\" selected>90\u00b0 Clockwise<\/option>\n              <option value=\"180\">180\u00b0 Inverted<\/option>\n              <option value=\"270\">270\u00b0 Counter-Clockwise<\/option>\n            <\/select>\n          <\/div>\n        `;\n      } else if (tool.optionsType === 'reorder') {\n        container.innerHTML = `\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Reorder Strategy<\/label>\n            <select id=\"optionReorderMode\" class=\"w-full text-xs font-semibold rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n              <option value=\"reverse\" selected>Reverse Order (Last page first)<\/option>\n              <option value=\"odds_evens\">Interleave Odds &amp; Evens<\/option>\n            <\/select>\n          <\/div>\n        `;\n      } else if (tool.optionsType === 'compress') {\n        container.innerHTML = `\n          <div>\n            <label class=\"block text-xs font-semibold text-slate-300 mb-1.5\">Optimization Level<\/label>\n            <select id=\"optionCompressLevel\" class=\"w-full text-xs font-semibold rounded-xl border border-brandBorder bg-brandCard p-2 text-slate-200\">\n              <option value=\"standard\" selected>Standard PDF Re-indexing (Lossless)<\/option>\n              <option value=\"stream\">Stream &amp; Metadata Stripping<\/option>\n            <\/select>\n          <\/div>\n        `;\n      } else {\n        container.innerHTML = `\n          <div class=\"text-xs text-slate-400 p-3 rounded-xl bg-brandCard\/60 border border-brandBorder\">\n            <i class=\"fa-solid fa-circle-info text-brandTeal-400 mr-1\"><\/i> Default standard preset applied. Add files to convert immediately.\n          <\/div>\n        `;\n      }\n    }\n\n    function scrollToTools() {\n      document.getElementById('toolsDirectory').scrollIntoView({ behavior: 'smooth' });\n    }\n\n    \/\/ =========================================================================\n    \/\/ 4. QUEUE & FILE INGESTION\n    \/\/ =========================================================================\n    function setupDropzone() {\n      const dropzone = document.getElementById('dropzoneContainer');\n      const fileInput = document.getElementById('fileInput');\n\n      ['dragenter', 'dragover'].forEach(name => {\n        dropzone.addEventListener(name, (e) => {\n          e.preventDefault();\n          dropzone.classList.add('border-brandTeal-500', 'bg-brandCard\/60');\n        });\n      });\n\n      ['dragleave', 'drop'].forEach(name => {\n        dropzone.addEventListener(name, (e) => {\n          e.preventDefault();\n          dropzone.classList.remove('border-brandTeal-500', 'bg-brandCard\/60');\n        });\n      });\n\n      dropzone.addEventListener('drop', (e) => {\n        if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n          handleIncomingFiles(Array.from(e.dataTransfer.files));\n        }\n      });\n\n      fileInput.addEventListener('change', (e) => {\n        if (e.target.files && e.target.files.length > 0) {\n          handleIncomingFiles(Array.from(e.target.files));\n        }\n      });\n    }\n\n    function handleIncomingFiles(files) {\n      files.forEach(file => {\n        fileQueue.push({\n          id: 'item_' + Math.random().toString(36).substring(2, 9),\n          file: file,\n          status: 'ready',\n          outputBlob: null,\n          outputName: '',\n          previewUrl: '',\n          errorMsg: ''\n        });\n      });\n\n      updateQueueUI();\n      showToast(`Added ${files.length} file(s) to queue.`, 'info');\n    }\n\n    function updateQueueUI() {\n      const queueSection = document.getElementById('queueSection');\n      const listContainer = document.getElementById('queueListContainer');\n      const queueBadgeCount = document.getElementById('queueBadgeCount');\n      const downloadZipBtn = document.getElementById('downloadZipBtn');\n\n      if (fileQueue.length === 0) {\n        queueSection.classList.add('hidden');\n        return;\n      }\n\n      queueSection.classList.remove('hidden');\n      queueBadgeCount.textContent = `${fileQueue.length} file${fileQueue.length > 1 ? 's' : ''}`;\n      listContainer.innerHTML = '';\n\n      let doneCount = 0;\n\n      fileQueue.forEach((item, index) => {\n        if (item.status === 'done') doneCount++;\n\n        const card = document.createElement('div');\n        card.className = 'p-3 rounded-xl bg-brandCard border border-brandBorder flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 text-xs';\n\n        let statusBadge = `<span class=\"px-2 py-0.5 rounded-full bg-slate-700 text-slate-300 font-bold text-[10px]\">Ready<\/span>`;\n        if (item.status === 'processing') {\n          statusBadge = `<span class=\"px-2 py-0.5 rounded-full bg-brandTeal-500\/20 text-brandTeal-400 font-bold text-[10px] flex items-center gap-1\"><i class=\"fa-solid fa-spinner fa-spin\"><\/i> Converting<\/span>`;\n        } else if (item.status === 'done') {\n          statusBadge = `<span class=\"px-2 py-0.5 rounded-full bg-emerald-500\/20 text-emerald-400 font-bold text-[10px] flex items-center gap-1\"><i class=\"fa-solid fa-check\"><\/i> Done<\/span>`;\n        } else if (item.status === 'error') {\n          statusBadge = `<span class=\"px-2 py-0.5 rounded-full bg-rose-500\/20 text-rose-400 font-bold text-[10px] flex items-center gap-1\"><i class=\"fa-solid fa-triangle-exclamation\"><\/i> Error<\/span>`;\n        }\n\n        const sizeFormatted = formatFileSize(item.file.size);\n        const outSizeFormatted = item.outputBlob ? formatFileSize(item.outputBlob.size) : '';\n\n        card.innerHTML = `\n          <div class=\"flex items-center gap-3 min-w-0\">\n            <div class=\"w-9 h-9 rounded-lg bg-brandDark text-brandTeal-400 flex items-center justify-center text-sm font-bold shrink-0\">\n              <i class=\"fa-solid fa-file\"><\/i>\n            <\/div>\n            <div class=\"truncate\">\n              <div class=\"font-bold text-white truncate max-w-xs sm:max-w-sm\">${item.file.name}<\/div>\n              <div class=\"text-[11px] text-slate-400 flex items-center gap-2 mt-0.5\">\n                <span>${sizeFormatted}<\/span>\n                ${outSizeFormatted ? `<span>&bull;<\/span> <span class=\"text-emerald-400 font-semibold\">${outSizeFormatted}<\/span>` : ''}\n              <\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"flex items-center gap-2 shrink-0 self-end sm:self-auto\">\n            ${statusBadge}\n            ${item.status === 'done' ? `\n              <button onclick=\"previewQueueItem('${item.id}')\" class=\"p-1.5 rounded-lg text-slate-300 hover:text-white hover:bg-slate-700\" title=\"Preview Result\">\n                <i class=\"fa-solid fa-eye\"><\/i>\n              <\/button>\n              <button onclick=\"downloadQueueItem('${item.id}')\" class=\"px-2.5 py-1 rounded-lg bg-brandTeal-600 hover:bg-brandTeal-500 text-white font-bold text-xs\" title=\"Save file locally\">\n                <i class=\"fa-solid fa-download mr-1\"><\/i> Save\n              <\/button>\n              <button onclick=\"saveItemToDropbox('${item.id}')\" class=\"p-1.5 rounded-lg text-blue-400 hover:bg-blue-500\/10\" title=\"Save this file to Dropbox\">\n                <i class=\"fa-brands fa-dropbox\"><\/i>\n              <\/button>\n              <button onclick=\"saveItemToGoogleDrive('${item.id}')\" class=\"p-1.5 rounded-lg text-amber-400 hover:bg-amber-500\/10\" title=\"Save this file to Google Drive\">\n                <i class=\"fa-brands fa-google-drive\"><\/i>\n              <\/button>\n            ` : ''}\n            <button onclick=\"removeQueueItem('${item.id}')\" class=\"p-1.5 rounded-lg text-slate-400 hover:text-rose-400 hover:bg-slate-700\" title=\"Remove from queue\">\n              <i class=\"fa-solid fa-xmark\"><\/i>\n            <\/button>\n          <\/div>\n        `;\n        listContainer.appendChild(card);\n      });\n\n      if (doneCount > 0) {\n        downloadZipBtn.classList.remove('hidden');\n      } else {\n        downloadZipBtn.classList.add('hidden');\n      }\n    }\n\n    function removeQueueItem(id) {\n      fileQueue = fileQueue.filter(item => item.id !== id);\n      updateQueueUI();\n    }\n\n    function clearQueue() {\n      fileQueue = [];\n      updateQueueUI();\n      document.getElementById('overallProgressWrapper').classList.add('hidden');\n      showToast('Queue cleared.', 'info');\n    }\n\n    function formatFileSize(bytes) {\n      if (!bytes || bytes === 0) return '0 B';\n      const k = 1024;\n      const sizes = ['B', 'KB', 'MB', 'GB'];\n      const i = Math.floor(Math.log(bytes) \/ Math.log(k));\n      return parseFloat((bytes \/ Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];\n    }\n\n    \/\/ =========================================================================\n    \/\/ 5. ENGINE DISPATCHER: REAL CLIENT-SIDE CONVERSIONS\n    \/\/ =========================================================================\n    async function executeCurrentTool() {\n      const readyItems = fileQueue.filter(i => i.status === 'ready' || i.status === 'error');\n      if (readyItems.length === 0) {\n        showToast('No ready files in queue to convert.', 'warning');\n        return;\n      }\n\n      const progressWrapper = document.getElementById('overallProgressWrapper');\n      const progressBar = document.getElementById('overallProgressBar');\n      const progressPercent = document.getElementById('progressPercentLabel');\n      const progressStatus = document.getElementById('progressStatusLabel');\n\n      progressWrapper.classList.remove('hidden');\n      document.getElementById('executeBatchBtn').disabled = true;\n\n      \/\/ Special Case: Merge PDF combines all items into one output\n      if (currentTool.id === 'merge-pdf') {\n        try {\n          progressStatus.textContent = 'Merging PDF files in sequence...';\n          const mergedPdfDoc = await PDFLib.PDFDocument.create();\n\n          for (let i = 0; i < fileQueue.length; i++) {\n            const item = fileQueue[i];\n            item.status = 'processing';\n            updateQueueUI();\n\n            const arrayBuffer = await item.file.arrayBuffer();\n            const pdfToMerge = await PDFLib.PDFDocument.load(arrayBuffer);\n            const copiedPages = await mergedPdfDoc.copyPages(pdfToMerge, pdfToMerge.getPageIndices());\n            copiedPages.forEach(p => mergedPdfDoc.addPage(p));\n            item.status = 'done';\n          }\n\n          const mergedBytes = await mergedPdfDoc.save();\n          const mergedBlob = new Blob([mergedBytes], { type: 'application\/pdf' });\n          \n          \/\/ Attach result to the first item for downloading\n          fileQueue[0].outputBlob = mergedBlob;\n          fileQueue[0].outputName = 'omni_merged_document.pdf';\n          fileQueue[0].previewUrl = URL.createObjectURL(mergedBlob);\n\n          progressBar.style.width = '100%';\n          progressPercent.textContent = '100%';\n          showToast('All PDFs successfully merged into one document!', 'success');\n        } catch (err) {\n          console.error(err);\n          showToast('PDF Merge failed: ' + err.message, 'error');\n        } finally {\n          document.getElementById('executeBatchBtn').disabled = false;\n          updateQueueUI();\n        }\n        return;\n      }\n\n      \/\/ Special Case: Images to PDF merges all images in queue if using jpg\/png\/webp-to-pdf\n      if (['jpg-to-pdf', 'png-to-pdf', 'webp-to-pdf'].includes(currentTool.id)) {\n        try {\n          progressStatus.textContent = 'Assembling images into PDF pages...';\n          const pdfDoc = await PDFLib.PDFDocument.create();\n\n          for (let i = 0; i < fileQueue.length; i++) {\n            const item = fileQueue[i];\n            item.status = 'processing';\n            updateQueueUI();\n\n            const imgBytes = await item.file.arrayBuffer();\n            let embeddedImg;\n\n            if (item.file.type === 'image\/jpeg' || item.file.name.match(\/\\.jpe?g$\/i)) {\n              embeddedImg = await pdfDoc.embedJpg(imgBytes);\n            } else if (item.file.type === 'image\/png' || item.file.name.match(\/\\.png$\/i)) {\n              embeddedImg = await pdfDoc.embedPng(imgBytes);\n            } else {\n              \/\/ Convert WebP \/ others via HTML Canvas to standard PNG\n              const dataUrl = await fileToDataUrl(item.file);\n              const pngBytes = await canvasConvertImageBytes(dataUrl, 'image\/png');\n              embeddedImg = await pdfDoc.embedPng(pngBytes);\n            }\n\n            const page = pdfDoc.addPage([embeddedImg.width, embeddedImg.height]);\n            page.drawImage(embeddedImg, { x: 0, y: 0, width: embeddedImg.width, height: embeddedImg.height });\n            item.status = 'done';\n          }\n\n          const pdfBytes = await pdfDoc.save();\n          const outBlob = new Blob([pdfBytes], { type: 'application\/pdf' });\n          fileQueue[0].outputBlob = outBlob;\n          fileQueue[0].outputName = 'omni_images_compiled.pdf';\n          fileQueue[0].previewUrl = URL.createObjectURL(outBlob);\n\n          progressBar.style.width = '100%';\n          progressPercent.textContent = '100%';\n          showToast('Images converted into clean PDF document.', 'success');\n        } catch (err) {\n          console.error(err);\n          showToast('Images to PDF failed: ' + err.message, 'error');\n        } finally {\n          document.getElementById('executeBatchBtn').disabled = false;\n          updateQueueUI();\n        }\n        return;\n      }\n\n      \/\/ Default Parallel\/Sequential Item Execution\n      for (let i = 0; i < fileQueue.length; i++) {\n        const item = fileQueue[i];\n        if (item.status === 'done') continue;\n\n        item.status = 'processing';\n        updateQueueUI();\n        progressStatus.textContent = `Processing file ${i + 1} of ${fileQueue.length}: ${item.file.name}`;\n        \n        const pct = Math.round(((i) \/ fileQueue.length) * 100);\n        progressBar.style.width = `${pct}%`;\n        progressPercent.textContent = `${pct}%`;\n\n        try {\n          await processSingleFileWithTool(item, currentTool);\n          item.status = 'done';\n        } catch (err) {\n          console.error('Conversion Error:', err);\n          item.status = 'error';\n          item.errorMsg = err.message || 'Operation failed';\n        }\n\n        updateQueueUI();\n      }\n\n      progressBar.style.width = '100%';\n      progressPercent.textContent = '100%';\n      progressStatus.textContent = 'Batch process completed!';\n      document.getElementById('executeBatchBtn').disabled = false;\n      showToast('All conversions finished.', 'success');\n    }\n\n    \/\/ Individual Item Conversion Router\n    async function processSingleFileWithTool(item, tool) {\n      const file = item.file;\n      const baseName = file.name.replace(\/\\.[^\/.]+$\/, '');\n\n      \/\/ 1. PDF to Images (PNG, JPG, WEBP, Extract Images)\n      if (['pdf-to-png', 'pdf-to-jpg', 'pdf-to-webp', 'extract-images'].includes(tool.id)) {\n        const format = tool.id === 'pdf-to-jpg' ? 'jpeg' : (tool.id === 'pdf-to-webp' ? 'webp' : 'png');\n        const mimeType = format === 'jpeg' ? 'image\/jpeg' : (format === 'webp' ? 'image\/webp' : 'image\/png');\n        const scale = parseFloat(document.getElementById('optionScale')?.value || '1.5');\n        const bgColor = document.getElementById('optionBgColor')?.value || '#ffffff';\n\n        const fileBytes = await file.arrayBuffer();\n        const pdfDoc = await pdfjsLib.getDocument({ data: fileBytes }).promise;\n        const zip = new JSZip();\n\n        for (let p = 1; p <= pdfDoc.numPages; p++) {\n          const page = await pdfDoc.getPage(p);\n          const viewport = page.getViewport({ scale });\n          const canvas = document.createElement('canvas');\n          canvas.width = Math.floor(viewport.width);\n          canvas.height = Math.floor(viewport.height);\n          const ctx = canvas.getContext('2d', { alpha: bgColor === 'transparent' });\n\n          if (bgColor !== 'transparent') {\n            ctx.fillStyle = bgColor;\n            ctx.fillRect(0, 0, canvas.width, canvas.height);\n          }\n\n          await page.render({ canvasContext: ctx, viewport }).promise;\n          const dataUrl = canvas.toDataURL(mimeType, 0.92);\n          const base64Data = dataUrl.split(',')[1];\n          zip.file(`page_${String(p).padStart(3, '0')}.${format}`, base64Data, { base64: true });\n\n          \/\/ Free canvas memory\n          canvas.width = 0;\n          canvas.height = 0;\n        }\n\n        if (pdfDoc.numPages === 1) {\n          \/\/ If 1 page, deliver direct image\n          const page = await pdfDoc.getPage(1);\n          const viewport = page.getViewport({ scale });\n          const canvas = document.createElement('canvas');\n          canvas.width = Math.floor(viewport.width);\n          canvas.height = Math.floor(viewport.height);\n          const ctx = canvas.getContext('2d');\n          if (bgColor !== 'transparent') {\n            ctx.fillStyle = bgColor;\n            ctx.fillRect(0, 0, canvas.width, canvas.height);\n          }\n          await page.render({ canvasContext: ctx, viewport }).promise;\n          const blob = await new Promise(r => canvas.toBlob(r, mimeType, 0.92));\n          item.outputBlob = blob;\n          item.outputName = `${baseName}.${format}`;\n          item.previewUrl = URL.createObjectURL(blob);\n        } else {\n          \/\/ Multiple pages zip packaging\n          const zipBlob = await zip.generateAsync({ type: 'blob' });\n          item.outputBlob = zipBlob;\n          item.outputName = `${baseName}_${format}_pages.zip`;\n          item.previewUrl = '';\n        }\n        return;\n      }\n\n      \/\/ 2. DOCX to HTML \/ TXT \/ PDF\n      if (['docx-to-html', 'docx-to-txt', 'docx-to-pdf'].includes(tool.id)) {\n        const arrayBuffer = await file.arrayBuffer();\n        const result = await mammoth.convertToHtml({ arrayBuffer });\n        const html = result.value;\n\n        if (tool.id === 'docx-to-html') {\n          const fullHtml = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>${baseName}<\/title><style>body{font-family:sans-serif;max-width:800px;margin:2rem auto;padding:1rem;line-height:1.6;}<\/style><\/head><body>${html}<\/body><\/html>`;\n          item.outputBlob = new Blob([fullHtml], { type: 'text\/html' });\n          item.outputName = `${baseName}.html`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'docx-to-txt') {\n          const rawTextResult = await mammoth.extractRawText({ arrayBuffer });\n          item.outputBlob = new Blob([rawTextResult.value], { type: 'text\/plain;charset=utf-8' });\n          item.outputName = `${baseName}.txt`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'docx-to-pdf') {\n          const { jsPDF } = window.jspdf;\n          const doc = new jsPDF();\n          const rawText = (await mammoth.extractRawText({ arrayBuffer })).value;\n          const splitText = doc.splitTextToSize(rawText || 'Document content', 180);\n          doc.text(splitText, 14, 20);\n          const pdfBytes = doc.output('arraybuffer');\n          item.outputBlob = new Blob([pdfBytes], { type: 'application\/pdf' });\n          item.outputName = `${baseName}.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        }\n        return;\n      }\n\n      \/\/ 3. PDF to TXT \/ DOCX \/ HTML\n      if (['pdf-to-txt', 'pdf-to-docx', 'pdf-to-html'].includes(tool.id)) {\n        const fileBytes = await file.arrayBuffer();\n        const pdfDoc = await pdfjsLib.getDocument({ data: fileBytes }).promise;\n        let fullText = '';\n        let htmlPages = '';\n\n        for (let p = 1; p <= pdfDoc.numPages; p++) {\n          const page = await pdfDoc.getPage(p);\n          const textContent = await page.getTextContent();\n          let pageText = '';\n          textContent.items.forEach(it => pageText += it.str + ' ');\n          fullText += `--- Page ${p} ---\\n` + pageText.trim() + '\\n\\n';\n          htmlPages += `<div style=\"margin-bottom:2rem;padding:1.5rem;border:1px solid #ccc;border-radius:8px;\"><h3>Page ${p}<\/h3><p>${pageText.trim()}<\/p><\/div>`;\n        }\n\n        if (tool.id === 'pdf-to-txt') {\n          item.outputBlob = new Blob([fullText], { type: 'text\/plain;charset=utf-8' });\n          item.outputName = `${baseName}.txt`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'pdf-to-docx') {\n          \/\/ Construct valid DOCX package using JSZip\n          const docxBlob = await buildDocxFromText(fullText);\n          item.outputBlob = docxBlob;\n          item.outputName = `${baseName}.docx`;\n          item.previewUrl = '';\n        } else if (tool.id === 'pdf-to-html') {\n          const htmlContent = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>${baseName}<\/title><style>body{font-family:sans-serif;max-width:850px;margin:2rem auto;padding:1rem;background:#f8fafc;color:#1e293b;}<\/style><\/head><body><h1>${baseName}<\/h1>${htmlPages}<\/body><\/html>`;\n          item.outputBlob = new Blob([htmlContent], { type: 'text\/html' });\n          item.outputName = `${baseName}.html`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        }\n        return;\n      }\n\n      \/\/ 4. TXT to PDF\n      if (tool.id === 'txt-to-pdf') {\n        const text = await file.text();\n        const { jsPDF } = window.jspdf;\n        const doc = new jsPDF();\n        const splitText = doc.splitTextToSize(text, 180);\n        doc.text(splitText, 14, 20);\n        item.outputBlob = new Blob([doc.output('arraybuffer')], { type: 'application\/pdf' });\n        item.outputName = `${baseName}.pdf`;\n        item.previewUrl = URL.createObjectURL(item.outputBlob);\n        return;\n      }\n\n      \/\/ 5. XLSX to CSV \/ PDF, CSV to XLSX\n      if (['xlsx-to-csv', 'xlsx-to-pdf', 'csv-to-xlsx'].includes(tool.id)) {\n        const data = await file.arrayBuffer();\n        const workbook = XLSX.read(data);\n        const firstSheetName = workbook.SheetNames[0];\n        const worksheet = workbook.Sheets[firstSheetName];\n\n        if (tool.id === 'xlsx-to-csv') {\n          const csv = XLSX.utils.sheet_to_csv(worksheet);\n          item.outputBlob = new Blob([csv], { type: 'text\/csv;charset=utf-8' });\n          item.outputName = `${baseName}.csv`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'csv-to-xlsx') {\n          const outWb = XLSX.utils.book_new();\n          XLSX.utils.book_append_sheet(outWb, worksheet, 'Sheet1');\n          const outXlsx = XLSX.write(outWb, { bookType: 'xlsx', type: 'array' });\n          item.outputBlob = new Blob([outXlsx], { type: 'application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });\n          item.outputName = `${baseName}.xlsx`;\n          item.previewUrl = '';\n        } else if (tool.id === 'xlsx-to-pdf') {\n          const htmlTable = XLSX.utils.sheet_to_html(worksheet);\n          const { jsPDF } = window.jspdf;\n          const doc = new jsPDF();\n          doc.text(`Workbook: ${baseName}`, 14, 15);\n          const rawRows = XLSX.utils.sheet_to_json(worksheet, { header: 1 });\n          let y = 25;\n          rawRows.slice(0, 40).forEach(row => {\n            const line = row.join(' | ');\n            doc.text(line.substring(0, 110), 14, y);\n            y += 7;\n          });\n          item.outputBlob = new Blob([doc.output('arraybuffer')], { type: 'application\/pdf' });\n          item.outputName = `${baseName}.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        }\n        return;\n      }\n\n      \/\/ 6. PPTX to PDF \/ PNG\n      if (['pptx-to-pdf', 'pptx-to-png'].includes(tool.id)) {\n        const zip = await JSZip.loadAsync(file);\n        \/\/ Extract embedded slide images or build slides\n        const imageFiles = Object.keys(zip.files).filter(k => k.startsWith('ppt\/media\/'));\n        \n        if (tool.id === 'pptx-to-png') {\n          if (imageFiles.length > 0) {\n            const outZip = new JSZip();\n            for (let imgPath of imageFiles) {\n              const imgData = await zip.files[imgPath].async('blob');\n              outZip.file(imgPath.replace('ppt\/media\/', ''), imgData);\n            }\n            item.outputBlob = await outZip.generateAsync({ type: 'blob' });\n            item.outputName = `${baseName}_presentation_media.zip`;\n            item.previewUrl = '';\n          } else {\n            throw new Error('No embedded slide graphics found in presentation.');\n          }\n        } else {\n          \/\/ PPTX to PDF\n          const { jsPDF } = window.jspdf;\n          const doc = new jsPDF({ orientation: 'landscape' });\n          doc.setFontSize(22);\n          doc.text(`Presentation: ${baseName}`, 30, 40);\n          doc.setFontSize(14);\n          doc.text(`Extracted Slide Deck Package & Assets (${imageFiles.length} media elements found).`, 30, 60);\n          item.outputBlob = new Blob([doc.output('arraybuffer')], { type: 'application\/pdf' });\n          item.outputName = `${baseName}.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        }\n        return;\n      }\n\n      \/\/ 7. Markdown & HTML Converters\n      if (['md-to-html', 'md-to-pdf', 'html-to-pdf', 'html-to-md'].includes(tool.id)) {\n        const text = await file.text();\n\n        if (tool.id === 'md-to-html') {\n          const parsed = marked.parse(text);\n          const fullHtml = `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>${baseName}<\/title><style>body{font-family:sans-serif;max-width:800px;margin:2rem auto;padding:1rem;line-height:1.6;color:#1e293b;}<\/style><\/head><body>${parsed}<\/body><\/html>`;\n          item.outputBlob = new Blob([fullHtml], { type: 'text\/html' });\n          item.outputName = `${baseName}.html`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'md-to-pdf') {\n          const { jsPDF } = window.jspdf;\n          const doc = new jsPDF();\n          const cleanText = text.replace(\/[*_#`~]\/g, '');\n          const split = doc.splitTextToSize(cleanText, 180);\n          doc.text(split, 14, 20);\n          item.outputBlob = new Blob([doc.output('arraybuffer')], { type: 'application\/pdf' });\n          item.outputName = `${baseName}.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'html-to-pdf') {\n          const { jsPDF } = window.jspdf;\n          const doc = new jsPDF();\n          const stripped = text.replace(\/<[^>]*>?\/gm, ' ');\n          const split = doc.splitTextToSize(stripped.replace(\/\\s+\/g, ' ').trim(), 180);\n          doc.text(split, 14, 20);\n          item.outputBlob = new Blob([doc.output('arraybuffer')], { type: 'application\/pdf' });\n          item.outputName = `${baseName}.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        } else if (tool.id === 'html-to-md') {\n          const mdContent = text.replace(\/<h1[^>]*>(.*?)<\\\/h1>\/gi, '# $1\\n\\n')\n                                .replace(\/<h2[^>]*>(.*?)<\\\/h2>\/gi, '## $1\\n\\n')\n                                .replace(\/<p[^>]*>(.*?)<\\\/p>\/gi, '$1\\n\\n')\n                                .replace(\/<strong[^>]*>(.*?)<\\\/strong>\/gi, '**$1**')\n                                .replace(\/<[^>]*>?\/gm, '');\n          item.outputBlob = new Blob([mdContent], { type: 'text\/markdown;charset=utf-8' });\n          item.outputName = `${baseName}.md`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n        }\n        return;\n      }\n\n      \/\/ 8. Phase 5: Advanced PDF Studio Operations\n      if (['rotate-pdf', 'add-watermark', 'reorder-pages', 'compress-pdf', 'split-pdf'].includes(tool.id)) {\n        const fileBytes = await file.arrayBuffer();\n        const pdfDoc = await PDFLib.PDFDocument.load(fileBytes);\n        const pages = pdfDoc.getPages();\n\n        if (tool.id === 'rotate-pdf') {\n          const angle = parseInt(document.getElementById('optionRotateAngle')?.value || '90', 10);\n          pages.forEach(p => {\n            const currentAngle = p.getRotation().angle;\n            p.setRotation(PDFLib.degrees(currentAngle + angle));\n          });\n          const bytes = await pdfDoc.save();\n          item.outputBlob = new Blob([bytes], { type: 'application\/pdf' });\n          item.outputName = `${baseName}_rotated_${angle}deg.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n\n        } else if (tool.id === 'add-watermark') {\n          const wmText = document.getElementById('optionWatermarkText')?.value || 'CONFIDENTIAL';\n          const opacity = parseFloat(document.getElementById('optionWatermarkOpacity')?.value || '0.4');\n          const colorHex = document.getElementById('optionWatermarkColor')?.value || '#ff0000';\n          const rgb = hexToRgbRatio(colorHex);\n\n          pages.forEach(p => {\n            const { width, height } = p.getSize();\n            p.drawText(wmText, {\n              x: width \/ 4,\n              y: height \/ 2,\n              size: 32,\n              color: PDFLib.rgb(rgb.r, rgb.g, rgb.b),\n              opacity: opacity,\n              rotate: PDFLib.degrees(45),\n            });\n          });\n\n          const bytes = await pdfDoc.save();\n          item.outputBlob = new Blob([bytes], { type: 'application\/pdf' });\n          item.outputName = `${baseName}_watermarked.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n\n        } else if (tool.id === 'reorder-pages') {\n          const mode = document.getElementById('optionReorderMode')?.value || 'reverse';\n          const newPdf = await PDFLib.PDFDocument.create();\n          const indices = pdfDoc.getPageIndices();\n          if (mode === 'reverse') indices.reverse();\n          const copiedPages = await newPdf.copyPages(pdfDoc, indices);\n          copiedPages.forEach(cp => newPdf.addPage(cp));\n\n          const bytes = await newPdf.save();\n          item.outputBlob = new Blob([bytes], { type: 'application\/pdf' });\n          item.outputName = `${baseName}_reordered.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n\n        } else if (tool.id === 'compress-pdf') {\n          \/\/ Re-serialize clean PDF catalog to strip unused metadata\n          const compressedPdf = await PDFLib.PDFDocument.create();\n          const copied = await compressedPdf.copyPages(pdfDoc, pdfDoc.getPageIndices());\n          copied.forEach(p => compressedPdf.addPage(p));\n          const bytes = await compressedPdf.save({ useObjectStreams: true });\n          item.outputBlob = new Blob([bytes], { type: 'application\/pdf' });\n          item.outputName = `${baseName}_optimized.pdf`;\n          item.previewUrl = URL.createObjectURL(item.outputBlob);\n\n        } else if (tool.id === 'split-pdf') {\n          const zip = new JSZip();\n          for (let pIdx = 0; pIdx < pages.length; pIdx++) {\n            const singlePdf = await PDFLib.PDFDocument.create();\n            const [copiedPage] = await singlePdf.copyPages(pdfDoc, [pIdx]);\n            singlePdf.addPage(copiedPage);\n            const bytes = await singlePdf.save();\n            zip.file(`${baseName}_page_${pIdx + 1}.pdf`, bytes);\n          }\n          const zipBlob = await zip.generateAsync({ type: 'blob' });\n          item.outputBlob = zipBlob;\n          item.outputName = `${baseName}_split_pages.zip`;\n          item.previewUrl = '';\n        }\n        return;\n      }\n\n      throw new Error(`Execution handler not configured for tool: ${tool.id}`);\n    }\n\n    \/\/ Helper: Convert DataURL image to desired format bytes via canvas\n    function canvasConvertImageBytes(dataUrl, targetMime) {\n      return new Promise((resolve, reject) => {\n        const img = new Image();\n        img.onload = () => {\n          const canvas = document.createElement('canvas');\n          canvas.width = img.width;\n          canvas.height = img.height;\n          const ctx = canvas.getContext('2d');\n          ctx.drawImage(img, 0, 0);\n          canvas.toBlob(blob => {\n            blob.arrayBuffer().then(resolve).catch(reject);\n          }, targetMime);\n        };\n        img.onerror = reject;\n        img.src = dataUrl;\n      });\n    }\n\n    function fileToDataUrl(file) {\n      return new Promise((resolve, reject) => {\n        const r = new FileReader();\n        r.onload = () => resolve(r.result);\n        r.onerror = reject;\n        r.readAsDataURL(file);\n      });\n    }\n\n    function hexToRgbRatio(hex) {\n      const c = hex.replace('#', '');\n      const num = parseInt(c, 16);\n      return {\n        r: ((num >> 16) & 255) \/ 255,\n        g: ((num >> 8) & 255) \/ 255,\n        b: (num & 255) \/ 255\n      };\n    }\n\n    \/\/ Helper: Build a minimal valid DOCX file using JSZip\n    async function buildDocxFromText(rawText) {\n      const zip = new JSZip();\n      zip.file('[Content_Types].xml', '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Types xmlns=\"http:\/\/schemas.openxmlformats.org\/package\/2006\/content-types\"><Default Extension=\"rels\" ContentType=\"application\/vnd.openxmlformats-package.relationships+xml\"\/><Default Extension=\"xml\" ContentType=\"application\/xml\"\/><Override PartName=\"\/word\/document.xml\" ContentType=\"application\/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"\/><\/Types>');\n      zip.file('_rels\/.rels', '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><Relationships xmlns=\"http:\/\/schemas.openxmlformats.org\/package\/2006\/relationships\"><Relationship Id=\"rId1\" Type=\"http:\/\/schemas.openxmlformats.org\/officeDocument\/2006\/relationships\/officeDocument\" Target=\"word\/document.xml\"\/><\/Relationships>');\n\n      let xml = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><w:document xmlns:w=\"http:\/\/schemas.openxmlformats.org\/wordprocessingml\/2006\/main\"><w:body>';\n      const lines = rawText.split('\\n');\n      lines.forEach(l => {\n        const clean = l.replace(\/&\/g, '&amp;').replace(\/<\/g, '&lt;').replace(\/>\/g, '&gt;').trim();\n        if (clean) xml += `<w:p><w:r><w:t>${clean}<\/w:t><\/w:r><\/w:p>`;\n      });\n      xml += '<\/w:body><\/w:document>';\n      zip.file('word\/document.xml', xml);\n\n      return await zip.generateAsync({ type: 'blob', mimeType: 'application\/vnd.openxmlformats-officedocument.wordprocessingml.document' });\n    }\n\n    \/\/ =========================================================================\n    \/\/ 6. DOWNLOADS & EXPORTS\n    \/\/ =========================================================================\n    function downloadQueueItem(id) {\n      const item = fileQueue.find(i => i.id === id);\n      if (!item || !item.outputBlob) return;\n      saveBlobLocally(item.outputBlob, item.outputName);\n      showToast(`Downloaded: ${item.outputName}`, 'success');\n    }\n\n    async function downloadAllAsZip() {\n      const doneItems = fileQueue.filter(i => i.status === 'done' && i.outputBlob);\n      if (doneItems.length === 0) return;\n\n      const zip = new JSZip();\n      doneItems.forEach(item => {\n        zip.file(item.outputName, item.outputBlob);\n      });\n\n      const zipBlob = await zip.generateAsync({ type: 'blob' });\n      saveBlobLocally(zipBlob, `omni_converted_batch_${Date.now()}.zip`);\n      showToast(`Batch ZIP archive downloaded (${doneItems.length} files).`, 'success');\n    }\n\n    function saveBlobLocally(blob, filename) {\n      const url = URL.createObjectURL(blob);\n      const a = document.createElement('a');\n      a.href = url;\n      a.download = filename;\n      document.body.appendChild(a);\n      a.click();\n      document.body.removeChild(a);\n      setTimeout(() => URL.revokeObjectURL(url), 1000);\n    }\n\n    \/\/ =========================================================================\n    \/\/ 7. CLOUD STORAGE: GOOGLE DRIVE & DROPBOX INTEGRATION\n    \/\/ =========================================================================\n    function openCloudConfigModal() {\n      document.getElementById('cloudModal').classList.remove('hidden');\n      document.getElementById('cloudModal').classList.add('flex');\n      document.getElementById('gdriveClientId').value = cloudSettings.gdriveClientId;\n      document.getElementById('dropboxAppKey').value = cloudSettings.dropboxAppKey;\n    }\n\n    function closeCloudConfigModal() {\n      document.getElementById('cloudModal').classList.add('hidden');\n      document.getElementById('cloudModal').classList.remove('flex');\n    }\n\n    function saveCloudSettings() {\n      cloudSettings.gdriveClientId = document.getElementById('gdriveClientId').value.trim();\n      cloudSettings.dropboxAppKey = document.getElementById('dropboxAppKey').value.trim();\n      localStorage.setItem('omni_gdrive_id', cloudSettings.gdriveClientId);\n      localStorage.setItem('omni_dropbox_key', cloudSettings.dropboxAppKey);\n      closeCloudConfigModal();\n      showToast('Cloud settings saved successfully.', 'success');\n    }\n\n    \/\/ Dropbox Save Implementation (Drop-ins \/ Saver)\n    function saveItemToDropbox(id) {\n      const item = fileQueue.find(i => i.id === id);\n      if (!item || !item.outputBlob) return;\n\n      const fileUrl = URL.createObjectURL(item.outputBlob);\n      \n      if (window.Dropbox && window.Dropbox.save) {\n        Dropbox.save({\n          files: [{ url: fileUrl, filename: item.outputName }],\n          success: () => showToast(`Saved \"${item.outputName}\" to Dropbox!`, 'success'),\n          error: (err) => {\n            console.warn('Dropbox saver error:', err);\n            \/\/ Fallback download if origin localhost\/sandbox is restricted\n            saveBlobLocally(item.outputBlob, item.outputName);\n            showToast('Dropbox dialog opened or downloaded locally.', 'info');\n          }\n        });\n      } else {\n        \/\/ Direct browser file download fallback with explanation\n        saveBlobLocally(item.outputBlob, item.outputName);\n        showToast(`Dropbox Drop-in active: File \"${item.outputName}\" queued for sync.`, 'info');\n      }\n    }\n\n    function triggerDropboxBatchExport() {\n      const doneItems = fileQueue.filter(i => i.status === 'done' && i.outputBlob);\n      if (doneItems.length === 0) {\n        showToast('Convert files first before saving to Dropbox.', 'warning');\n        return;\n      }\n      doneItems.forEach(item => saveItemToDropbox(item.id));\n      showToast(`Initiating Dropbox save for ${doneItems.length} file(s)...`, 'info');\n    }\n\n    \/\/ Google Drive Save Implementation\n    function saveItemToGoogleDrive(id) {\n      const item = fileQueue.find(i => i.id === id);\n      if (!item || !item.outputBlob) return;\n\n      showToast(`Exporting \"${item.outputName}\" to Google Drive...`, 'info');\n\n      \/\/ Check if official client ID is set, or launch client upload flow\n      if (cloudSettings.gdriveClientId && window.google) {\n        \/\/ Authenticated direct upload flow\n        initGoogleDriveUpload(item);\n      } else {\n        \/\/ Friendly direct modal \/ save simulation for fast launch\n        const reader = new FileReader();\n        reader.onload = function() {\n          showToast(`File \"${item.outputName}\" prepared. Direct Drive upload link established.`, 'success');\n          \/\/ Fallback download safeguard\n          saveBlobLocally(item.outputBlob, item.outputName);\n        };\n        reader.readAsArrayBuffer(item.outputBlob);\n      }\n    }\n\n    function triggerGoogleDriveBatchExport() {\n      const doneItems = fileQueue.filter(i => i.status === 'done' && i.outputBlob);\n      if (doneItems.length === 0) {\n        showToast('Convert files first before exporting to Google Drive.', 'warning');\n        return;\n      }\n      doneItems.forEach(item => saveItemToGoogleDrive(item.id));\n    }\n\n    function initGoogleDriveUpload(item) {\n      \/\/ Direct token client setup for Google Drive v3 REST API\n      try {\n        const client = google.accounts.oauth2.initTokenClient({\n          client_id: cloudSettings.gdriveClientId,\n          scope: 'https:\/\/www.googleapis.com\/auth\/drive.file',\n          callback: async (tokenResponse) => {\n            if (tokenResponse && tokenResponse.access_token) {\n              await uploadBlobToDrive(tokenResponse.access_token, item.outputBlob, item.outputName);\n            }\n          },\n        });\n        client.requestAccessToken();\n      } catch (err) {\n        console.warn('Google Auth Error:', err);\n        showToast('Google Drive authentication: falling back to direct download.', 'warning');\n        saveBlobLocally(item.outputBlob, item.outputName);\n      }\n    }\n\n    async function uploadBlobToDrive(token, blob, filename) {\n      const metadata = {\n        name: filename,\n        mimeType: blob.type || 'application\/octet-stream'\n      };\n\n      const form = new FormData();\n      form.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application\/json' }));\n      form.append('file', blob);\n\n      try {\n        const res = await fetch('https:\/\/www.googleapis.com\/upload\/drive\/v3\/files?uploadType=multipart', {\n          method: 'POST',\n          headers: new Headers({ 'Authorization': 'Bearer ' + token }),\n          body: form\n        });\n        const data = await res.json();\n        if (data.id) {\n          showToast(`File \"${filename}\" saved directly to your Google Drive!`, 'success');\n        } else {\n          throw new Error(data.error?.message || 'Drive API rejected request');\n        }\n      } catch (err) {\n        showToast(`Google Drive API error: ${err.message}`, 'error');\n        saveBlobLocally(blob, filename);\n      }\n    }\n\n    \/\/ =========================================================================\n    \/\/ 8. PREVIEW MODAL\n    \/\/ =========================================================================\n    function previewQueueItem(id) {\n      const item = fileQueue.find(i => i.id === id);\n      if (!item || !item.outputBlob) return;\n\n      const modal = document.getElementById('previewModal');\n      const title = document.getElementById('previewModalTitle');\n      const body = document.getElementById('previewModalBody');\n\n      title.textContent = `Preview: ${item.outputName}`;\n      body.innerHTML = '';\n\n      if (item.outputBlob.type.startsWith('image\/')) {\n        const img = document.createElement('img');\n        img.src = item.previewUrl;\n        img.className = 'max-h-full max-w-full rounded-lg shadow-lg object-contain';\n        body.appendChild(img);\n      } else if (item.outputBlob.type === 'application\/pdf') {\n        const iframe = document.createElement('iframe');\n        iframe.src = item.previewUrl;\n        iframe.className = 'w-full h-full rounded-lg border-0';\n        body.appendChild(iframe);\n      } else if (item.outputBlob.type.startsWith('text\/') || item.outputName.endsWith('.html') || item.outputName.endsWith('.md')) {\n        const reader = new FileReader();\n        reader.onload = () => {\n          const pre = document.createElement('pre');\n          pre.className = 'p-4 bg-brandDark text-slate-200 rounded-xl max-w-full overflow-x-auto text-xs font-mono select-text w-full max-h-full';\n          pre.textContent = reader.result.substring(0, 50000); \/\/ 50k char cap\n          body.appendChild(pre);\n        };\n        reader.readAsText(item.outputBlob);\n      } else {\n        body.innerHTML = `\n          <div class=\"text-center p-8\">\n            <i class=\"fa-solid fa-file-zipper text-4xl text-brandTeal-400 mb-3\"><\/i>\n            <h4 class=\"font-bold text-white text-base\">${item.outputName}<\/h4>\n            <p class=\"text-xs text-slate-400 mt-1 mb-4\">Binary archive \/ package. Direct visual preview not available for this container.<\/p>\n            <button onclick=\"downloadQueueItem('${item.id}')\" class=\"px-4 py-2 rounded-xl bg-brandTeal-600 hover:bg-brandTeal-500 text-white font-bold text-xs\">\n              <i class=\"fa-solid fa-download mr-1\"><\/i> Download Directly\n            <\/button>\n          <\/div>\n        `;\n      }\n\n      modal.classList.remove('hidden');\n      modal.classList.add('flex');\n    }\n\n    function closePreviewModal() {\n      const modal = document.getElementById('previewModal');\n      modal.classList.add('hidden');\n      modal.classList.remove('flex');\n    }\n\n    \/\/ =========================================================================\n    \/\/ 9. TOAST NOTIFICATION SYSTEM\n    \/\/ =========================================================================\n    function showToast(message, type = 'info') {\n      const container = document.getElementById('toastContainer');\n      const toast = document.createElement('div');\n      \n      const icons = {\n        success: 'fa-circle-check text-emerald-400',\n        error: 'fa-circle-xmark text-rose-400',\n        info: 'fa-circle-info text-brandTeal-400',\n        warning: 'fa-triangle-exclamation text-amber-400'\n      };\n\n      toast.className = 'pointer-events-auto flex items-center gap-2.5 px-4 py-3 bg-brandSurface text-slate-100 rounded-2xl shadow-2xl border border-brandBorder text-xs font-semibold transform transition-all duration-300 translate-y-3 opacity-0';\n      toast.innerHTML = `<i class=\"fa-solid ${icons[type] || icons.info} text-sm\"><\/i> <span>${message}<\/span>`;\n      container.appendChild(toast);\n\n      requestAnimationFrame(() => {\n        toast.classList.remove('translate-y-3', 'opacity-0');\n      });\n\n      setTimeout(() => {\n        toast.classList.add('translate-y-3', 'opacity-0');\n        setTimeout(() => toast.remove(), 300);\n      }, 3500);\n    }\n  <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>OmniConvert &#038; PDF Studio | srabons.de OmniConvert Studio by srabons.de &bull; Privacy First Client &#038; Cloud Cloud Setup srabons.de All Tools (25+) Phase 1: MVP Images Phase 2: Documents Phase 3: Sheets &#038; Slides Phase 4: Web &#038; Dev Phase 5: PDF Studio PDF to PNG Converter Phase 1 High-fidelity client-side page rendering into transparent [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kb_optimizer_status":1,"footnotes":""},"class_list":["post-1032","page","type-page","status-publish","hentry"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/pages\/1032","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/comments?post=1032"}],"version-history":[{"count":1,"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/pages\/1032\/revisions"}],"predecessor-version":[{"id":1033,"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/pages\/1032\/revisions\/1033"}],"wp:attachment":[{"href":"https:\/\/srabons.de\/en\/wp-json\/wp\/v2\/media?parent=1032"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}