WhatsApp Channel Join Now
Telegram Channel Join Now

Blogger पर AI tool website कैसे बनाएं?

blogger-par-ai-tool-website-kaise-banaye
5/5 - (1 vote)

दोस्तों, आज के इस article में मैं आपको Blogger platform पर AI की मदद से tool generator website कैसे बनाते हैं, वह बताऊंगा। वैसे तो मैंने इस topic पर already article लिख रखा है, पर वह PHP HTML script थी।

बहुत से लोग हैं जो hosting server लेना afford नहीं कर सकते, तो उन्हीं के लिए मैं यह लेख लिख रहा हूँ, जिसमें वे free platform यानी Blogger की सहायता से अपनी एक tool website बना सकते हैं।

यह वेबसाइट बनाने के लिए उन्हें कुछ AI के promptHTML code और basic AI को इस्तेमाल करने की जानकारी लगेगी। वह भी मैं विस्तार से इस लेख में बताऊंगा।

जैसा मैंने ऊपर आपको बताया है कि Blogger platform पर आप अपनी tool converter website बना सकते हैं। इसके लिए आपको कोई भी पैसा देने की ज़रूरत नहीं है। इस प्रक्रिया को आप AI के द्वारा पूरी तरह से free में कर सकते हैं।

तो पहले आपको Blogger पर एक blog या blog post बना लेना होगा। आप किसी भी template का इस्तेमाल कर सकते हैं। Basic customization जो भी Blogger पर करनी होती है, वह कर लीजिए और नीचे मैं जो AI prompt दे रहा हूँ, उसे copy कीजिए।

Create a fully functional and responsive multi-tool website called “Multi Tool Hub” using only HTML, CSS, and JavaScript (Vanilla JS) — no backend or external libraries (unless browser-native)
The site should have a modern, premium, and minimalistic design, fully responsive across desktop, tablet, and mobile devices.
________________


🧱 Design Theme & Aesthetic:
* Background Color: #1E1E2F (dark navy)

* Text Color: #EAEAEA (light gray)

* Header Background: #2B2D42 (deep blue)

* Accent Color: #FFD700 (gold)

* Tool Card Background: #3A3D5B (dark grayish-blue)

* Hover Effects:

   * Tool card background becomes #FFD700 and text turns #1E1E2F

   * Button hover color: #E6C200

   * Soft box shadow: rgba(255, 215, 0, 0.2)

      * Use box shadows, smooth transitions, and spacing to make the UI feel modern and luxurious

________________


🖥️ Page Layout:
         * Centered header with the title: "Multi Tool Hub"

         * Under the header, display a responsive grid layout:

            * Desktop: 3-column grid (grid-template-columns: repeat(3, 1fr))

            * Tablet: 2-column grid

            * Mobile: Single-column layout

               * Each tool is presented as a tool card:

                  * h2 title

                  * p description

                  * button opens the tool in a modal or dynamically replaces the main content area

________________


🛠️ List of 20 Fully Functional Tools (Frontend Only):
                     1. Image Converter – Convert between JPG, PNG, and WEBP formats using canvas

                     2. Image Compressor – Compress image file size using canvas and quality settings

                     3. Image Cropper – Upload and crop image with preview and export

                     4. Video Converter – Convert video format (MP4 ↔ WebM) using MediaRecorder or canvas (limited to browser capabilities)

                     5. Audio Converter – Convert between MP3 and WAV formats using Web Audio API

                     6. Audio Trimmer – Upload, trim audio based on start/end time, and export trimmed clip

                     7. Age Calculator – Input date of birth → output age in years, months, and days

                     8. EMI Calculator – Input loan amount, interest rate, and duration → show monthly EMI and total interest

                     9. SIP Calculator – Input monthly investment, interest rate, duration → output future value

                     10. QR Code Generator – Enter text or URL → generate downloadable QR image (use canvas or native API)

                     11. Password Generator – Generate secure password with length, symbols, numbers options

                     12. Word Counter – Live count of words, characters, spaces, and reading time

                     13. Base64 Encoder/Decoder – Convert plain text to base64 and vice versa

                     14. Color Picker Tool – Pick a color and display HEX, RGB, and HSL values

                     15. Text to Speech – Enter text and listen to it using SpeechSynthesis API

                     16. Speech to Text – Use microphone to convert voice into text using Web Speech API

                     17. JSON Formatter – Paste JSON → auto-format and validate with error handling

                     18. Unit Converter – Convert values between units (length, weight, temperature, etc.)

                     19. BMI Calculator – Input weight and height → show BMI category and value

                     20. Timer / Stopwatch Tool – Simple timer and stopwatch with start, stop, reset functionality

________________


🎨 Styling Requirements:
                        * Use CSS variables for theme colors

                        * Buttons should be:

                           * Rounded (border-radius: 5px)

                           * Bold text

                           * Hover effects with color change

                           * Easy to tap on mobile

                              * Responsive grid system with proper media queries

                              * Smooth CSS transitions on hover for cards and buttons

                              * Optional fade-in animations on scroll using IntersectionObserver or CSS animation

________________


⚙️ JavaScript Functionality:
                                 * All tools should be fully functional using only frontend JavaScript

                                 * Use modular code: Each tool's logic in separate functions or sections

                                 * Handle:

                                    * File uploads and conversions (canvas, Web Audio API, MediaRecorder, etc.)

                                    * UI interactions (modal open/close, input validations, dynamic updates)

                                    * Calculations and real-time updates (e.g., age, BMI, SIP, EMI)

                                       * Show alerts like “Processing…” or success states where needed

________________


🧩 Code Structure:
                                          * index.html: Structure with header, tool cards, modals/sections for each tool

                                          * style.css: Theme styles, responsive design, and animations

                                          * script.js: Logic for all tools, input handlers, modals, and utility functions

________________


✅ Final Requirements:
                                             * All 20 tools must be functional and accurate

                                             * Site must look modern, mobile-friendly, and premium

                                             * No external libraries (optional: native APIs only)

                                             * All code must be clean, modular, and maintainable

                                             * The layout must adapt perfectly across all devices

अब इस AI prompt को आप Google AI Studio की website पर जाकर paste करें। वहाँ से आपको Google AI के द्वारा कुछ code मिलेगा, उसका आपको इस्तेमाल करना है। अब इस्तेमाल कैसे करें, वह मैंने मेरे YouTube की video में आपको बताया है।

अब आपको HTML code की ज़रूरत भी पड़ेगी, जिसे आपको template के edit section में डालना होगा। तो वह HTML code की script भी मैं नीचे दे रहा हूँ।

<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <!-- SEO Meta Tags -->
        <title>One Page Tools - Your Ultimate Collection of Free Online Tools</title>
        <meta name="description" content="One Page Tools offers 20+ free, responsive, and easy-to-use online tools including image converters, calculators, text utilities, and more. All client-side, no uploads required for many tools!">
        <meta name="keywords" content="online tools, free tools, image converter, image compressor, image cropper, video converter, audio converter, audio trimmer, age calculator, emi calculator, sip calculator, qr code generator, password generator, word counter, base64, color picker, text to speech, speech to text, json formatter, unit converter, bmi calculator, timer, stopwatch, multi tool, utility website">
        <meta name="author" content="One Page Tools">
        <link rel="canonical" href="YOUR_CANONICAL_URL_HERE"> <!-- Replace with your actual domain -->


        <!-- Open Graph Meta Tags (for Facebook, LinkedIn, etc.) -->
        <meta property="og:title" content="One Page Tools - Free Online Utilities">
        <meta property="og:description" content="Access a suite of 20 powerful online tools for images, audio, video, calculations, and text processing. Fast, free, and secure.">
        <meta property="og:type" content="website">
        <meta property="og:url" content="YOUR_CANONICAL_URL_HERE"> <!-- Replace with your actual domain -->
        <meta property="og:image" content="YOUR_OG_IMAGE_URL_HERE"> <!-- URL to an image representing your site (e.g., logo, screenshot) -->
        <meta property="og:image:width" content="1200">
        <meta property="og:image:height" content="630">
        <meta property="og:site_name" content="One Page Tools">


        <!-- Twitter Card Meta Tags -->
        <meta name="twitter:card" content="summary_large_image">
        <meta name="twitter:title" content="One Page Tools - 20+ Free Online Tools">
        <meta name="twitter:description" content="Your one-stop hub for versatile online tools: converters, calculators, generators, and more. Fully responsive and client-side.">
        <meta name="twitter:image" content="YOUR_TWITTER_IMAGE_URL_HERE"> <!-- URL to an image for Twitter card -->
        <!-- <meta name="twitter:site" content="@YourTwitterHandle"> --> <!-- Optional: Your Twitter handle -->


        <!-- Favicon (replace with your actual favicon links) -->
        <link rel="icon" href="/favicon.ico" sizes="any">
        <link rel="icon" href="/favicon.svg" type="image/svg+xml">
        <link rel="apple-touch-icon" href="/apple-touch-icon.png">




        <!-- Schema.org Markup (JSON-LD) -->
        <script type="application/ld+json">
        {
          "@context": "https://schema.org",
          "@type": "WebSite",
          "name": "One Page Tools",
          "url": "YOUR_CANONICAL_URL_HERE", // Replace
          "description": "A comprehensive collection of free online tools for various tasks including image manipulation, calculations, text utilities, and media conversion. All tools are client-side and designed for ease of use.",
          "potentialAction": {
            "@type": "SearchAction",
            "target": "YOUR_CANONICAL_URL_HERE#search?q={search_term_string}", // Replace if you implement site search
            "query-input": "required name=search_term_string"
          },
          "publisher": {
            "@type": "Organization",
            "name": "One Page Tools",
            "logo": {
              "@type": "ImageObject",
              "url": "YOUR_LOGO_URL_HERE", // Replace
              "width": 600, // Adjust
              "height": 60 // Adjust
            }
          },
          "mainEntity": {
            "@type": "ItemList",
            "name": "Available Tools",
            "description": "List of tools available on One Page Tools.",
            "itemListElement": [
              // Tools will be listed here. I'll add a few examples.
              // In a full implementation, you'd list all 20.
              { "@type": "ListItem", "position": 1, "item": { "@type": "Service", "name": "Image Converter", "description": "Convert images between JPG, PNG, and WEBP formats." } },
              { "@type": "ListItem", "position": 2, "item": { "@type": "Service", "name": "Age Calculator", "description": "Calculate age in years, months, and days from a birth date." } },
              { "@type": "ListItem", "position": 3, "item": { "@type": "Service", "name": "QR Code Generator", "description": "Generate QR codes from text or URLs." } }
              // ... (add all 20 tools here similarly)
            ]
          }
        }
        </script>
        <!-- Example HowTo Schema for one tool (you'd ideally create one for each) -->
        <script type="application/ld+json">
        {
          "@context": "https://schema.org",
          "@type": "HowTo",
          "name": "How to use the Image Converter on One Page Tools",
          "description": "A step-by-step guide to convert image formats (JPG, PNG, WEBP) using the One Page Tools's Image Converter.",
          "step": [
            {
              "@type": "HowToStep",
              "name": "Open Tool",
              "text": "Navigate to the One Page Tools and click on the 'Image Converter' tool card.",
              "url": "YOUR_CANONICAL_URL_HERE#imageConverter" // Link to the tool if possible
            },
            {
              "@type": "HowToStep",
              "name": "Upload Image",
              "text": "Click the 'Choose File' button and select the image you want to convert from your device."
            },
            {
              "@type": "HowToStep",
              "name": "Select Output Format",
              "text": "Choose your desired output format (JPG, PNG, or WEBP) from the dropdown menu."
            },
            {
              "@type": "HowToStep",
              "name": "Convert and Download",
              "text": "Click the 'Convert & Download' button. The converted image will be processed and downloaded automatically."
            }
          ],
          "estimatedCost": {
            "@type": "MonetaryAmount",
            "currency": "USD",
            "value": "0"
          },
          "supply": [
            { "@type": "HowToSupply", "name": "A digital image file (JPG, PNG, or initial WEBP)" },
            { "@type": "HowToSupply", "name": "A web browser" }
          ],
          "tool": [
            { "@type": "HowToTool", "name": "Web browser with JavaScript enabled" },
            { "@type": "HowToTool", "name": "One Page Tools Image Converter" }
          ],
          "totalTime": "PT1M" // Estimated time: 1 minute
        }
        </script>
    
        <!-- Placeholder for Analytics (e.g., Google Analytics) -->
        <!--
        <script async src="https://www.googletagmanager.com/gtag/js?id=YOUR_GA_TRACKING_ID"></script>
        <script>
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'YOUR_GA_TRACKING_ID');
        </script>
        -->


        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">


        <style>
            :root {
                --background-color: #1E1E2F;
                --text-color: #EAEAEA;
                --header-background: #2B2D42;
                --accent-color: #FFD700;
                --tool-card-background: #3A3D5B;
                --button-hover-color: #E6C200;
                --soft-box-shadow: 0 8px 16px rgba(255, 215, 0, 0.1);
                --card-hover-box-shadow: 0 12px 24px rgba(255, 215, 0, 0.2);
                --font-family: 'Poppins', sans-serif;
                --ad-placeholder-bg: #4a4e69;
                --ad-placeholder-border: #6c757d;
            }


            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }


            body {
                font-family: var(--font-family);
                background-color: var(--background-color);
                color: var(--text-color);
                line-height: 1.6;
                padding-bottom: 50px;
            }


            header {
                background-color: var(--header-background);
                color: var(--accent-color);
                padding: 1.5rem 1rem;
                text-align: center;
                box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
                margin-bottom: 1rem; /* Reduced margin for ad space */
            }


            header h1 {
                font-weight: 700;
                font-size: 2.5rem;
            }


            .ad-placeholder {
                background-color: var(--ad-placeholder-bg);
                border: 1px dashed var(--ad-placeholder-border);
                color: var(--text-color);
                text-align: center;
                padding: 1rem;
                margin: 1rem auto; /* Centered */
                max-width: 728px; /* Common ad width */
                min-height: 90px; /* Common ad height */
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 0.9rem;
                border-radius: 5px;
            }
            .ad-placeholder-sidebar { /* Example for a different type of ad */
                max-width: 300px;
                min-height: 250px;
            }




            main {
                max-width: 1200px;
                margin: 0 auto;
                padding: 0 1rem;
            }


            .tool-grid {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 1.5rem;
            }


            .tool-card {
                background-color: var(--tool-card-background);
                padding: 1.5rem;
                border-radius: 10px;
                box-shadow: var(--soft-box-shadow);
                transition: transform 0.3s ease, background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                opacity: 0; /* For fade-in animation */
                transform: translateY(20px); /* For fade-in animation */
                animation: fadeInUp 0.5s ease-out forwards;
            }


            .tool-card:hover {
                transform: translateY(-5px);
                background-color: var(--accent-color);
                color: var(--background-color);
                box-shadow: var(--card-hover-box-shadow);
            }


            .tool-card h2 {
                font-size: 1.5rem;
                margin-bottom: 0.75rem;
                color: var(--text-color);
                transition: color 0.3s ease;
            }


            .tool-card:hover h2 {
                color: var(--background-color);
            }


            .tool-card p {
                font-size: 0.9rem;
                margin-bottom: 1rem;
                flex-grow: 1;
                opacity: 0.9;
            }


            .tool-card:hover p {
                color: var(--background-color);
                opacity: 1;
            }


            .tool-button {
                background-color: var(--accent-color);
                color: var(--background-color);
                border: none;
                padding: 0.75rem 1.25rem;
                border-radius: 5px;
                font-weight: bold;
                cursor: pointer;
                text-align: center;
                transition: background-color 0.3s ease, color 0.3s ease;
                align-self: flex-start;
            }


            .tool-card:hover .tool-button {
                background-color: var(--background-color);
                color: var(--accent-color);
            }


            .tool-button:hover {
                background-color: var(--button-hover-color) !important;
                color: var(--background-color) !important;
            }


            .modal {
                display: none;
                position: fixed;
                z-index: 1000;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                overflow: auto;
                background-color: rgba(0, 0, 0, 0.6);
                animation: fadeIn 0.3s ease-in-out;
            }


            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }


            .modal-content {
                background-color: var(--header-background);
                margin: 5% auto;
                padding: 25px;
                border-radius: 10px;
                width: 90%;
                max-width: 700px;
                box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
                position: relative;
                animation: slideIn 0.3s ease-in-out;
            }
            
            /* Modal Ad Placeholder - example, might be too intrusive */
            /* .modal-ad-placeholder { margin-top: 1rem; } */




            @keyframes slideIn {
                from { transform: translateY(-50px); opacity: 0; }
                to { transform: translateY(0); opacity: 1; }
            }


            .close-button {
                color: var(--text-color);
                float: right;
                font-size: 28px;
                font-weight: bold;
                transition: color 0.2s ease;
            }


            .close-button:hover,
            .close-button:focus {
                color: var(--accent-color);
                text-decoration: none;
                cursor: pointer;
            }


            .modal-body {
                margin-top: 1.5rem;
                color: var(--text-color);
            }


            .modal-body h3 {
                color: var(--accent-color);
                margin-bottom: 1rem;
            }


            .modal-body label {
                display: block;
                margin-bottom: 0.5rem;
                font-weight: 600;
            }


            .modal-body input[type="text"],
            .modal-body input[type="number"],
            .modal-body input[type="date"],
            .modal-body input[type="file"],
            .modal-body textarea,
            .modal-body select {
                width: 100%;
                padding: 0.75rem;
                margin-bottom: 1rem;
                border-radius: 5px;
                border: 1px solid var(--tool-card-background);
                background-color: var(--background-color);
                color: var(--text-color);
                font-family: var(--font-family);
                font-size: 1rem;
            }


            .modal-body input[type="file"] { padding: 0.5rem; }
            .modal-body textarea { min-height: 100px; resize: vertical; }


            .modal-body button, .tool-interface button {
                background-color: var(--accent-color);
                color: var(--background-color);
                border: none;
                padding: 0.8rem 1.5rem;
                border-radius: 5px;
                font-weight: bold;
                cursor: pointer;
                transition: background-color 0.3s ease;
                margin-top: 0.5rem;
                margin-right: 0.5rem;
            }


            .modal-body button:hover, .tool-interface button:hover {
                background-color: var(--button-hover-color);
            }


            .modal-body .result-area, .tool-interface .result-area {
                margin-top: 1rem;
                padding: 1rem;
                background-color: var(--background-color);
                border-radius: 5px;
                border: 1px solid var(--tool-card-background);
                white-space: pre-wrap;
                word-wrap: break-word;
            }


            .modal-body .option-group {
                margin-bottom: 1rem;
                padding: 0.75rem;
                border: 1px solid var(--tool-card-background);
                border-radius: 5px;
            }
            .modal-body .option-group label { display: inline-block; margin-right: 10px; }


            .modal-alert {
                margin-top: 1rem;
                padding: 0.75rem;
                border-radius: 5px;
                font-weight: bold;
            }
            .modal-alert.success { background-color: #28a745; color: white; }
            .modal-alert.error { background-color: #dc3545; color: white; }
            .modal-alert.info { background-color: #17a2b8; color: white; }


            #qrCodeContainer canvas {
                display: block;
                margin: 1rem auto;
                border: 5px solid var(--accent-color);
            }


            #imagePreview, #croppedImagePreview, #imagePreviewComp {
                max-width: 100%;
                max-height: 300px;
                margin-top: 1rem;
                border: 1px solid var(--accent-color);
                background-color: var(--background-color); /* Ensure preview area has background */
            }
            
            #cropCanvas { /* Style for the cropper canvas */
                border:1px solid var(--accent-color);
                max-width:100%;
                display:block; /* Important for canvas */
                background-color: var(--background-color);
                margin-top: 10px;
            }




            .color-picker-display { display: flex; align-items: center; margin-top: 1rem; }
            .color-picker-display .color-preview { width: 50px; height: 50px; border-radius: 5px; margin-right: 1rem; border: 1px solid var(--text-color); }
            .color-picker-display .color-values p { margin: 0.25rem 0; }


            .timer-display, .stopwatch-display { font-size: 2.5rem; font-weight: bold; text-align: center; margin-bottom: 1rem; color: var(--accent-color); }
            .laps-list { max-height: 150px; overflow-y: auto; border: 1px solid var(--tool-card-background); padding: 0.5rem; margin-top: 1rem; }
            .laps-list li { list-style: none; padding: 0.25rem 0; border-bottom: 1px dashed var(--tool-card-background); }
            .laps-list li:last-child { border-bottom: none; }
            
            .tabs { display:flex; margin-bottom:1rem; border-bottom:1px solid var(--tool-card-background); }
            .tab-button { background:none; border:none; color: var(--text-color); padding: 0.75rem 1rem; cursor:pointer; font-size: 1rem; font-family: var(--font-family); transition: background-color 0.3s, color 0.3s; }
            .tab-button.active { background-color: var(--accent-color); color: var(--background-color); border-bottom: 2px solid var(--accent-color); font-weight: bold; }
            .tab-button:not(.active):hover { background-color: var(--tool-card-background); }


            #toolsArticle {
                margin-top: 3rem;
                padding: 1.5rem;
                background-color: var(--tool-card-background);
                border-radius: 8px;
            }
            #toolsArticle h2 {
                color: var(--accent-color);
                margin-bottom: 1rem;
                text-align: center;
                font-size: 2rem;
            }
            #toolsArticle h3 {
                color: var(--accent-color);
                opacity: 0.9;
                margin-top: 1.5rem;
                margin-bottom: 0.5rem;
                border-bottom: 1px solid var(--header-background);
                padding-bottom: 0.3rem;
            }
            #toolsArticle p, #toolsArticle ul {
                margin-bottom: 1rem;
                font-size: 0.95rem;
                opacity: 0.9;
            }
            #toolsArticle li {
                margin-left: 1.5rem;
                margin-bottom: 0.5rem;
            }
             #toolsArticle strong { color: var(--accent-color); opacity: 1;}




            @keyframes fadeInUp {
                to { opacity: 1; transform: translateY(0); }
            }


            @media (max-width: 992px) { /* Tablet */
                .tool-grid { grid-template-columns: repeat(2, 1fr); }
                header h1 { font-size: 2rem; }
            }


            @media (max-width: 768px) { /* Mobile */
                .tool-grid { grid-template-columns: 1fr; }
                .modal-content { width: 95%; margin: 10% auto; padding: 20px; }
                header h1 { font-size: 1.8rem; }
                .tool-card h2 { font-size: 1.3rem; }
                .modal-body button, .tool-interface button { padding: 0.7rem 1.2rem; font-size: 0.9rem; }
                #toolsArticle h2 { font-size: 1.6rem; }
                #toolsArticle h3 { font-size: 1.3rem; }
            }
        </style>
</head>
<body>
        <header>
            <h1>One Page Tools</h1>
        </header>


        <!-- Ad Placeholder 1 (Below Header) -->
        <div class="ad-placeholder">
            Your Advertisement Here (e.g., 728x90 Leaderboard)
        </div>


        <main>
            <div class="tool-grid">
                <!-- Tool cards will be dynamically generated by JavaScript -->
            </div>
        </main>


        <!-- Generic Modal Structure -->
        <div id="toolModal" class="modal">
            <div class="modal-content">
                <span class="close-button">&times;</span>
                <h2 id="modalTitle">Tool Title</h2>
                <div id="modalBody" class="modal-body">
                    <!-- Tool-specific content will be injected here -->
                </div>
                 <!-- Optional Ad Placeholder inside Modal -->
                <!-- <div class="ad-placeholder modal-ad-placeholder">Ad inside modal (e.g., 300x250)</div> -->
                <div id="modalAlert" class="modal-alert" style="display:none;"></div>
            </div>
        </div>


        <!-- Article Section -->
        <div id="toolsArticleContainer" style="max-width: 1200px; margin: 2rem auto; padding: 0 1rem;">
            <article id="toolsArticle">
                <h2>Welcome to One Page Tools: Your Ultimate Online Toolkit</h2>
                <p>In today's fast-paced digital world, efficiency and convenience are paramount. Whether you're a student, a professional, a content creator, or simply someone who interacts with digital media and data regularly, having quick access to a versatile set of tools can be a game-changer. That's where <strong>One Page Tools</strong> comes in. We've curated a collection of 20 essential, free-to-use online tools designed to simplify your tasks, boost your productivity, and empower your digital endeavors – all from the comfort of your web browser, with no installations required and a strong focus on client-side processing for speed and privacy.</p>
                <p>Our platform is built with a modern, premium, and minimalistic design, ensuring a seamless and enjoyable user experience across all devices – desktop, tablet, and mobile. Each tool is crafted to be fully functional using only HTML, CSS, and Vanilla JavaScript, meaning they are lightweight, fast, and respect your privacy by processing data directly in your browser whenever possible.</p>
                
                <h3>Why Choose One Page Tools?</h3>
                <ul>
                    <li><strong>Completely Free:</strong> All 20 tools are available at no cost.</li>
                    <li><strong>Client-Side Processing:</strong> For many tools, especially those handling sensitive data or files, processing happens directly in your browser. This means your files often don't even need to be uploaded to a server, enhancing speed and privacy.</li>
                    <li><strong>No Registration Required:</strong> Jump right in and start using the tools without any sign-ups or logins.</li>
                    <li><strong>User-Friendly Interface:</strong> Our clean, minimalistic design makes it easy to find and use the tool you need.</li>
                    <li><strong>Fully Responsive:</strong> Access One Page Tools on any device, anytime, anywhere.</li>
                    <li><strong>Diverse Range of Utilities:</strong> From image manipulation and media conversion to financial calculators and text utilities, we've got you covered.</li>
                </ul>


                <h3>Explore Our Suite of 20 Powerful Tools:</h3>


                <section id="article-image-tools">
                    <h3>🎨 Image Manipulation Tools</h3>
                    <p>Perfect for designers, photographers, and anyone working with digital images.</p>
                    
                    <h4>1. Image Converter</h4>
                    <p><strong>What it does:</strong> Easily convert your images between popular formats like JPG, PNG, and WEBP. Whether you need a transparent background (PNG), a highly compressed web-friendly format (WEBP), or a standard photographic format (JPG), this tool handles it swiftly using browser-native canvas technology.</p>
                    <p><strong>Use cases:</strong> Preparing images for web use, changing formats for compatibility, optimizing image types for specific needs.</p>


                    <h4>2. Image Compressor</h4>
                    <p><strong>What it does:</strong> Reduce the file size of your images without significant loss of quality. This tool uses canvas and adjustable quality settings (for JPEGs) to make your images lighter, faster to load on websites, and easier to share.</p>
                    <p><strong>Use cases:</strong> Optimizing website images for faster loading times, reducing storage space, sending images via email or messaging apps with size limits.</p>


                    <h4>3. Image Cropper</h4>
                    <p><strong>What it does:</strong> Upload an image, define your desired crop area with a visual preview, and export the perfectly cropped section. Ideal for focusing on specific parts of an image or adjusting aspect ratios.</p>
                    <p><strong>Use cases:</strong> Creating profile pictures, framing subjects, removing unwanted parts of an image, preparing images for specific layouts.</p>
                </section>


                <section id="article-media-tools">
                    <h3>🎬 Audio & Video Tools</h3>
                    <p>For content creators, students, or anyone needing to tweak audio and video files.</p>


                    <h4>4. Video Converter (Browser-Limited)</h4>
                    <p><strong>What it does:</strong> Convert video formats (e.g., MP4 to WebM or vice-versa) by re-encoding the video directly in your browser using MediaRecorder or canvas capabilities. Functionality and output quality depend on your browser's support for these technologies.</p>
                    <p><strong>Use cases:</strong> Making videos compatible with specific platforms or devices, converting to web-friendly formats. (Note: Browser capabilities are key here).</p>


                    <h4>5. Audio Converter (to WAV)</h4>
                    <p><strong>What it does:</strong> Convert various audio files that your browser can play into the versatile WAV format. This tool utilizes the Web Audio API for decoding and processing.</p>
                    <p><strong>Use cases:</strong> Preparing audio for editing software that prefers WAV, standardizing audio formats, creating uncompressed audio clips.</p>


                    <h4>6. Audio Trimmer</h4>
                    <p><strong>What it does:</strong> Upload an audio file, specify start and end times, and export the trimmed segment as a new WAV clip. Perfect for extracting soundbites, creating ringtones, or shortening audio recordings.</p>
                    <p><strong>Use cases:</strong> Editing podcasts or interviews, creating short audio samples, removing unwanted sections from recordings.</p>
                </section>


                <section id="article-calculator-tools">
                    <h3>🧮 Productivity & Financial Calculators</h3>
                    <p>Essential tools for everyday calculations, financial planning, and health monitoring.</p>


                    <h4>7. Age Calculator</h4>
                    <p><strong>What it does:</strong> Simply input a date of birth, and this tool will instantly calculate the age in years, months, and days. Accurate and quick.</p>
                    <p><strong>Use cases:</strong> Finding exact age for forms or records, curiosity, event planning based on age milestones.</p>


                    <h4>8. EMI Calculator</h4>
                    <p><strong>What it does:</strong> Plan your loans effectively. Input the loan amount, annual interest rate, and loan duration (in months) to calculate your Equated Monthly Installment (EMI), total interest payable, and total payment.</p>
                    <p><strong>Use cases:</strong> Financial planning for home loans, car loans, personal loans; understanding loan affordability.</p>


                    <h4>9. SIP Calculator</h4>
                    <p><strong>What it does:</strong> Estimate the future value of your Systematic Investment Plan (SIP) investments. Enter your monthly investment amount, expected annual interest rate, and investment duration (in years) to see potential returns.</p>
                    <p><strong>Use cases:</strong> Mutual fund investment planning, retirement planning, goal-based savings projections.</p>


                    <h4>10. BMI Calculator</h4>
                    <p><strong>What it does:</strong> Calculate your Body Mass Index (BMI) by entering your weight (in kg) and height (in cm). The tool also provides your BMI category (e.g., Underweight, Normal, Overweight).</p>
                    <p><strong>Use cases:</strong> Health and fitness tracking, understanding weight status, a general indicator for health assessment (consult a professional for medical advice).</p>
                </section>


                <section id="article-text-code-tools">
                    <h3>✒️ Text & Code Utilities</h3>
                    <p>A must-have for writers, developers, and anyone dealing with text or data.</p>


                    <h4>11. QR Code Generator</h4>
                    <p><strong>What it does:</strong> Enter any text, URL, contact information, or Wi-Fi credentials, and generate a downloadable QR code image instantly. Uses canvas for generation.</p>
                    <p><strong>Use cases:</strong> Sharing website links, contact details, Wi-Fi access easily; marketing materials, event ticketing.</p>


                    <h4>12. Password Generator</h4>
                    <p><strong>What it does:</strong> Create strong, secure, and random passwords. Customize the length and include/exclude uppercase letters, lowercase letters, numbers, and symbols to meet various security requirements.</p>
                    <p><strong>Use cases:</strong> Enhancing online security, creating unique passwords for different accounts, adhering to password complexity rules.</p>


                    <h4>13. Word Counter</h4>
                    <p><strong>What it does:</strong> A real-time text analysis tool. As you type or paste text, it counts words, characters (with and without spaces), spaces, and even estimates reading time. Essential for writers, students, and social media managers.</p>
                    <p><strong>Use cases:</strong> Meeting word count requirements for essays or articles, optimizing social media posts, tracking writing progress.</p>


                    <h4>14. Base64 Encoder/Decoder</h4>
                    <p><strong>What it does:</strong> Easily convert plain text to Base64 encoding or decode Base64 strings back to their original plain text form. Useful for data transmission or simple obfuscation.</p>
                    <p><strong>Use cases:</strong> Encoding data for URLs or HTML, decoding data from various web sources, simple data protection (not encryption).</p>


                    <h4>15. JSON Formatter</h4>
                    <p><strong>What it does:</strong> Paste your JSON data, and this tool will auto-format (pretty-print) it with proper indentation and syntax highlighting. It also helps validate the JSON structure and points out errors.</p>
                    <p><strong>Use cases:</strong> Debugging JSON APIs, making complex JSON data readable, validating JSON structures before use in applications.</p>
                </section>
                
                <section id="article-accessibility-interaction-tools">
                    <h3>🗣️ Accessibility & Interactive Tools</h3>
                    <p>Enhance accessibility and engage with content in new ways.</p>


                    <h4>16. Color Picker Tool</h4>
                    <p><strong>What it does:</strong> Visually pick a color using a standard color input, or enter a color code. The tool displays its HEX, RGB, and HSL values, making it easy to work with colors for web design, graphic design, or any digital project.</p>
                    <p><strong>Use cases:</strong> Web development, graphic design, digital art, matching colors, understanding color codes.</p>


                    <h4>17. Text to Speech</h4>
                    <p><strong>What it does:</strong> Enter any text, and listen to it being read aloud by your browser's built-in speech synthesis engine. You can often choose from different voices, and adjust the rate and pitch.</p>
                    <p><strong>Use cases:</strong> Accessibility for visually impaired users, proofreading text, learning pronunciation, multitasking by listening to content.</p>


                    <h4>18. Speech to Text</h4>
                    <p><strong>What it does:</strong> Use your microphone to convert your spoken words into text. This tool leverages your browser's Web Speech API for real-time voice recognition.</p>
                    <p><strong>Use cases:</strong> Dictating notes, writing emails hands-free, accessibility for users with motor impairments, transcribing short audio clips.</p>
                </section>


                <section id="article-measurement-time-tools">
                    <h3>📏 Measurement & Time Management Tools</h3>
                    <p>Practical utilities for conversions and keeping track of time.</p>


                    <h4>19. Unit Converter</h4>
                    <p><strong>What it does:</strong> A versatile converter for various units of measurement, including length (meters, feet, miles, etc.), weight (kilograms, pounds, ounces, etc.), and temperature (Celsius, Fahrenheit, Kelvin). Simple and intuitive.</p>
                    <p><strong>Use cases:</strong> Converting recipes, scientific calculations, travel planning, everyday measurement conversions.</p>


                    <h4>20. Timer / Stopwatch Tool</h4>
                    <p><strong>What it does:</strong> A dual-function tool. Set a countdown timer for tasks or use the stopwatch with lap functionality to measure elapsed time accurately. Clean interface and easy controls.</p>
                    <p><strong>Use cases:</strong> Time management (Pomodoro technique), cooking, workouts, tracking time for activities, conducting experiments.</p>
                </section>


                <h3>Privacy and Security at the Forefront</h3>
                <p>We understand the importance of your data. That's why One Page Tools is designed with a "privacy-first" approach. Many of our tools, particularly those that handle files (like image and audio converters/compressors/croppers), perform all operations directly within your browser. This means your files are not uploaded to our servers, offering you enhanced security and speed. For tools that require input (like calculators or text utilities), the data is processed client-side and is not stored or tracked beyond what's necessary for the tool's immediate function or basic anonymous usage analytics (if enabled by you).</p>


                <h3>Constantly Evolving</h3>
                <p>One Page Tools is a living project. We are committed to maintaining and improving our existing tools and potentially adding new ones based on user needs and technological advancements. Our goal is to be your go-to destination for quick, reliable, and free online utilities.</p>
                <p>We invite you to explore the One Page Tools and discover how our suite of tools can simplify your digital life. Bookmark us and share with friends and colleagues who might benefit from these handy utilities!</p>
            </article>
        </div>


        <!-- Ad Placeholder 2 (Before Footer/End of Page) -->
        <div class="ad-placeholder">
            Your Advertisement Here (e.g., 728x90 or 300x250)
        </div>




        <script>
            // --- QR Code Generation Library (Minimal Placeholder - qrcode.js by davidshimjs is a good option to embed) ---
            // For a fully functional QR code, you would embed a library like qrcode.js here.
            // Example structure if qrcode.js was embedded:
            /*
            var QRCode; (function(){ // QRCode Kapselung ... entire qrcode.js library code ... QRCode=t})()
            */
            // Since embedding a full library makes this example overly long, the QR tool uses a simplified canvas drawing.
            // A real implementation should use a proper QR library for compliant codes.
            // For demonstration, if you had a qrcode.js file, you'd paste its contents here.
            // For now, the script will check for a 'QRCode' object and use a fallback if not found.




            document.addEventListener('DOMContentLoaded', () => {
                const toolGrid = document.querySelector('.tool-grid');
                const modal = document.getElementById('toolModal');
                const modalTitle = document.getElementById('modalTitle');
                const modalBody = document.getElementById('modalBody');
                const modalAlert = document.getElementById('modalAlert');
                const closeButton = document.querySelector('.close-button');


                // --- Basic Browser-Native Analytics (Tool Usage Logger) ---
                function logToolUsage(toolId) {
                    try {
                        let usageData = JSON.parse(localStorage.getItem('multiToolHubUsage')) || [];
                        usageData.push({ tool: toolId, timestamp: new Date().toISOString() });
                        // Optional: Limit the size of usageData to prevent localStorage overflow
                        if (usageData.length > 1000) { // Keep last 1000 entries
                            usageData = usageData.slice(usageData.length - 1000);
                        }
                        localStorage.setItem('multiToolHubUsage', JSON.stringify(usageData));
                    } catch (e) {
                        console.error("Error logging tool usage:", e);
                    }
                }
                // Example: To see logged data, open browser console and type:
                // JSON.parse(localStorage.getItem('multiToolHubUsage'))




                const tools = [
                    { id: 'imageConverter', title: 'Image Converter', description: 'Convert between JPG, PNG, and WEBP formats.' },
                    { id: 'imageCompressor', title: 'Image Compressor', description: 'Compress image file size with quality settings.' },
                    { id: 'imageCropper', title: 'Image Cropper', description: 'Upload, crop image with preview, and export.' },
                    { id: 'videoConverter', title: 'Video Converter', description: 'Convert video (MP4 ↔ WebM via re-encoding). Browser-limited.' },
                    { id: 'audioConverter', title: 'Audio Converter', description: 'Convert uploaded audio to WAV format.' },
                    { id: 'audioTrimmer', title: 'Audio Trimmer', description: 'Upload, trim audio, and export trimmed WAV clip.' },
                    { id: 'ageCalculator', title: 'Age Calculator', description: 'Calculate age from date of birth.' },
                    { id: 'emiCalculator', title: 'EMI Calculator', description: 'Calculate Equated Monthly Installment for loans.' },
                    { id: 'sipCalculator', title: 'SIP Calculator', description: 'Calculate future value of SIP investments.' },
                    { id: 'qrCodeGenerator', title: 'QR Code Generator', description: 'Generate downloadable QR codes from text/URL.' },
                    { id: 'passwordGenerator', title: 'Password Generator', description: 'Generate secure passwords with custom options.' },
                    { id: 'wordCounter', title: 'Word Counter', description: 'Count words, characters, spaces, and estimate reading time.' },
                    { id: 'base64EncoderDecoder', title: 'Base64 Encoder/Decoder', description: 'Encode to Base64 or decode from Base64.' },
                    { id: 'colorPicker', title: 'Color Picker Tool', description: 'Pick colors and get HEX, RGB, HSL values.' },
                    { id: 'textToSpeech', title: 'Text to Speech', description: 'Convert text into spoken audio using browser voices.' },
                    { id: 'speechToText', title: 'Speech to Text', description: 'Convert voice from microphone into text.' },
                    { id: 'jsonFormatter', title: 'JSON Formatter', description: 'Format and validate JSON data.' },
                    { id: 'unitConverter', title: 'Unit Converter', description: 'Convert values between various units (length, weight, temp).' },
                    { id: 'bmiCalculator', title: 'BMI Calculator', description: 'Calculate Body Mass Index and category.' },
                    { id: 'timerStopwatch', title: 'Timer / Stopwatch', description: 'Simple timer and stopwatch functionality.' },
                ];


                function openModal(tool) {
                    modalTitle.textContent = tool.title;
                    modalBody.innerHTML = '';
                    hideAlert();
                    
                    const toolFunction = toolInitializers[tool.id];
                    if (toolFunction) {
                        toolFunction(modalBody);
                        logToolUsage(tool.id); // Log tool opening
                    } else {
                        modalBody.innerHTML = '<p>Tool UI not implemented yet.</p>';
                    }
                    modal.style.display = 'block';
                }


                function closeModal() {
                    modal.style.display = 'none';
                    modalBody.innerHTML = '';
                    if (window.currentToolCleanup) {
                        window.currentToolCleanup();
                        window.currentToolCleanup = null;
                    }
                }


                closeButton.onclick = closeModal;
                window.onclick = (event) => {
                    if (event.target === modal) {
                        closeModal();
                    }
                };
                
                function showAlert(message, type = 'info') {
                    modalAlert.textContent = message;
                    modalAlert.className = `modal-alert ${type}`;
                    modalAlert.style.display = 'block';
                }


                function hideAlert() {
                    modalAlert.style.display = 'none';
                }


                tools.forEach((tool, index) => {
                    const card = document.createElement('div');
                    card.className = 'tool-card';
                    card.style.animationDelay = `${index * 0.05}s`;
                    card.innerHTML = `
                        <h2>${tool.title}</h2>
                        <p>${tool.description}</p>
                        <button class="tool-button" data-toolid="${tool.id}">Open Tool</button>
                    `;
                    card.querySelector('.tool-button').addEventListener('click', () => openModal(tool));
                    toolGrid.appendChild(card);
                });


                const toolInitializers = {
                    imageConverter: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgConvFile" accept="image/jpeg,image/png,image/webp">
                            <label for="imgConvFormat">Convert to:</label>
                            <select id="imgConvFormat">
                                <option value="image/jpeg">JPEG</option>
                                <option value="image/png">PNG</option>
                                <option value="image/webp">WEBP</option>
                            </select>
                            <button id="imgConvButton">Convert & Download</button>
                            <p>Note: WEBP support varies by browser.</p>
                            <img id="imagePreview" src="#" alt="Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgConvFile');
                        const formatSelect = container.querySelector('#imgConvFormat');
                        const convertButton = container.querySelector('#imgConvButton');
                        const preview = container.querySelector('#imagePreview');
                        let originalFileName = 'converted_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    preview.src = event.target.result;
                                    preview.style.display = 'block';
                                }
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                 preview.style.display = 'none';
                                 preview.src="#";
                            }
                        };


                        convertButton.onclick = () => {
                            if (!fileInput.files || fileInput.files.length === 0) {
                                showAlert('Please select an image file first.', 'error');
                                return;
                            }
                            const file = fileInput.files[0];
                            const targetFormat = formatSelect.value;
                            const targetExtension = targetFormat.split('/')[1];


                            showAlert('Processing...', 'info');


                            const reader = new FileReader();
                            reader.onload = (event) => {
                                const img = new Image();
                                img.onload = () => {
                                    const canvas = document.createElement('canvas');
                                    canvas.width = img.width;
                                    canvas.height = img.height;
                                    const ctx = canvas.getContext('2d');
                                    ctx.drawImage(img, 0, 0);
                                    
                                    canvas.toBlob((blob) => {
                                        if (blob) {
                                            const url = URL.createObjectURL(blob);
                                            const a = document.createElement('a');
                                            a.href = url;
                                            a.download = `${originalFileName}_converted.${targetExtension}`;
                                            document.body.appendChild(a);
                                            a.click();
                                            document.body.removeChild(a);
                                            URL.revokeObjectURL(url);
                                            showAlert('Conversion successful!', 'success');
                                        } else {
                                            showAlert(`Error converting to ${targetFormat}. This format might not be supported for export by your browser.`, 'error');
                                        }
                                    }, targetFormat, 0.9);
                                };
                                img.onerror = () => showAlert('Could not load image. Ensure it is a valid JPG, PNG, or WEBP.', 'error');
                                img.src = event.target.result;
                            };
                            reader.readAsDataURL(file);
                        };
                    },


                    imageCompressor: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgCompFile" accept="image/jpeg,image/png">
                            <label for="imgCompQuality">Quality (0.1 - 1.0 for JPEG, ignored for PNG):</label>
                            <input type="number" id="imgCompQuality" value="0.7" min="0.1" max="1.0" step="0.1">
                            <button id="imgCompButton">Compress & Download</button>
                            <p>PNG compression is lossless and quality setting is ignored.</p>
                            <div id="compressionInfo" class="result-area" style="display:none;"></div>
                            <img id="imagePreviewComp" src="#" alt="Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgCompFile');
                        const qualityInput = container.querySelector('#imgCompQuality');
                        const compressButton = container.querySelector('#imgCompButton');
                        const compressionInfo = container.querySelector('#compressionInfo');
                        const preview = container.querySelector('#imagePreviewComp');
                        let originalFileName = 'compressed_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    preview.src = event.target.result;
                                    preview.style.display = 'block';
                                }
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                preview.style.display = 'none';
                                preview.src = "#";
                            }
                        };


                        compressButton.onclick = () => {
                            if (!fileInput.files || fileInput.files.length === 0) {
                                showAlert('Please select an image file.', 'error');
                                return;
                            }
                            const file = fileInput.files[0];
                            const quality = parseFloat(qualityInput.value);
                            const originalSize = (file.size / 1024).toFixed(2);
                            showAlert('Processing...', 'info');


                            const reader = new FileReader();
                            reader.onload = (event) => {
                                const img = new Image();
                                img.onload = () => {
                                    const canvas = document.createElement('canvas');
                                    const ctx = canvas.getContext('2d');
                                    canvas.width = img.width;
                                    canvas.height = img.height;
                                    ctx.drawImage(img, 0, 0);


                                    let outputFormat = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
                                    let extension = outputFormat.split('/')[1];


                                    canvas.toBlob((blob) => {
                                        if (blob) {
                                            const compressedSize = (blob.size / 1024).toFixed(2);
                                            const url = URL.createObjectURL(blob);
                                            const a = document.createElement('a');
                                            a.href = url;
                                            a.download = `${originalFileName}_compressed.${extension}`;
                                            document.body.appendChild(a);
                                            a.click();
                                            document.body.removeChild(a);
                                            URL.revokeObjectURL(url);
                                            
                                            compressionInfo.innerHTML = `Original Size: ${originalSize} KB<br>Compressed Size: ${compressedSize} KB<br>Reduction: ${((1 - compressedSize / originalSize) * 100).toFixed(2)}%`;
                                            compressionInfo.style.display = 'block';
                                            showAlert('Compression successful!', 'success');
                                        } else {
                                            showAlert('Error during compression.', 'error');
                                        }
                                    }, outputFormat, outputFormat === 'image/jpeg' ? quality : undefined);
                                };
                                img.onerror = () => showAlert('Could not load image.', 'error');
                                img.src = event.target.result;
                            };
                            reader.readAsDataURL(file);
                        };
                    },


                    imageCropper: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgCropFile" accept="image/*">
                            <p>After selecting an image, adjust crop parameters below. A real cropper would have a draggable overlay.</p>
                            <canvas id="cropCanvas" style="display:none;"></canvas>
                            <br>
                            <label for="cropX">Crop Start X (px):</label> <input type="number" id="cropX" value="0" style="width:80px;">
                            <label for="cropY">Crop Start Y (px):</label> <input type="number" id="cropY" value="0" style="width:80px;">
                            <br>
                            <label for="cropW">Crop Width (px):</label> <input type="number" id="cropW" value="100" style="width:80px;">
                            <label for="cropH">Crop Height (px):</label> <input type="number" id="cropH" value="100" style="width:80px;">
                            <br>
                            <button id="cropButton">Crop & Download</button>
                            <img id="croppedImagePreview" src="#" alt="Cropped Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgCropFile');
                        const cropCanvas = container.querySelector('#cropCanvas'); // This is the preview canvas
                        const ctx = cropCanvas.getContext('2d');
                        const cropXInput = container.querySelector('#cropX');
                        const cropYInput = container.querySelector('#cropY');
                        const cropWInput = container.querySelector('#cropW');
                        const cropHInput = container.querySelector('#cropH');
                        const cropButton = container.querySelector('#cropButton');
                        const croppedPreview = container.querySelector('#croppedImagePreview');
                        let sourceImage = null;
                        let originalFileName = 'cropped_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    sourceImage = new Image();
                                    sourceImage.onload = () => {
                                        const MAX_PREVIEW_DIM = 300; // Max width/height for preview canvas
                                        let scale = Math.min(MAX_PREVIEW_DIM / sourceImage.width, MAX_PREVIEW_DIM / sourceImage.height, 1);
                                        cropCanvas.width = sourceImage.width * scale;
                                        cropCanvas.height = sourceImage.height * scale;
                                        ctx.drawImage(sourceImage, 0, 0, cropCanvas.width, cropCanvas.height);
                                        cropCanvas.style.display = 'block';
                                        
                                        cropWInput.value = Math.floor(sourceImage.width / 2);
                                        cropHInput.value = Math.floor(sourceImage.height / 2);
                                        cropXInput.value = Math.floor(sourceImage.width / 4);
                                        cropYInput.value = Math.floor(sourceImage.height / 4);
                                        showAlert('Image loaded. Adjust crop parameters based on original image dimensions.', 'info');
                                    };
                                    sourceImage.src = event.target.result;
                                };
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                cropCanvas.style.display = 'none';
                                if (sourceImage) sourceImage.src = "";
                                sourceImage = null;
                            }
                        };


                        cropButton.onclick = () => {
                            if (!sourceImage) {
                                showAlert('Please select an image first.', 'error');
                                return;
                            }
                            const sx = parseInt(cropXInput.value);
                            const sy = parseInt(cropYInput.value);
                            const sWidth = parseInt(cropWInput.value);
                            const sHeight = parseInt(cropHInput.value);


                            if (isNaN(sx) || isNaN(sy) || isNaN(sWidth) || isNaN(sHeight) || sWidth <= 0 || sHeight <= 0) {
                                showAlert('Invalid crop dimensions. Ensure they are positive numbers.', 'error');
                                return;
                            }
                            if (sx + sWidth > sourceImage.width || sy + sHeight > sourceImage.height || sx < 0 || sy < 0) {
                                showAlert('Crop area is outside the image boundaries.', 'error');
                                return;
                            }
                            
                            showAlert('Cropping...', 'info');
                            const tempCanvas = document.createElement('canvas');
                            tempCanvas.width = sWidth;
                            tempCanvas.height = sHeight;
                            const tempCtx = tempCanvas.getContext('2d');
                            
                            try {
                                tempCtx.drawImage(sourceImage, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
                                const dataUrl = tempCanvas.toDataURL(fileInput.files[0].type || 'image/png');
                                croppedPreview.src = dataUrl;
                                croppedPreview.style.display = 'block';


                                const a = document.createElement('a');
                                a.href = dataUrl;
                                a.download = `${originalFileName}_cropped.${(fileInput.files[0].type || 'image/png').split('/')[1]}`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                showAlert('Image cropped and download started.', 'success');
                            } catch (error) {
                                showAlert('Error during cropping: ' + error.message, 'error');
                            }
                        };
                    },


                    videoConverter: (container) => {
                        container.innerHTML = `
                            <p>This tool attempts to convert playable videos to MP4 or WebM by re-encoding using MediaRecorder. Success and output quality depend on browser capabilities.</p>
                            <input type="file" id="vidConvFile" accept="video/*">
                            <label for="vidConvFormat">Convert to:</label>
                            <select id="vidConvFormat">
                                <option value="video/webm;codecs=vp8,opus">WebM (VP8/Opus)</option>
                                <option value="video/webm;codecs=vp9,opus">WebM (VP9/Opus - better quality)</option>
                                <option value="video/mp4;codecs=avc1.42E01E,mp4a.40.2">MP4 (H.264/AAC - browser support varies)</option>
                            </select>
                            <button id="vidConvButton">Convert & Download</button>
                            <video id="vidConvPreview" controls style="max-width:100%; margin-top:10px; display:none; background-color:black;"></video>
                            <p id="vidConvStatus" class="result-area" style="display:none;"></p>
                        `;
                        const fileInput = container.querySelector('#vidConvFile');
                        const formatSelect = container.querySelector('#vidConvFormat');
                        const convertButton = container.querySelector('#vidConvButton');
                        const videoPreview = container.querySelector('#vidConvPreview');
                        const statusP = container.querySelector('#vidConvStatus');
                        let mediaRecorder;
                        let recordedChunks = [];
                        let originalFileName = 'converted_video';


                        fileInput.onchange = (e) => {
                            recordedChunks = []; // Reset chunks
                            if (mediaRecorder && mediaRecorder.state === "recording") {
                                mediaRecorder.stop();
                            }
                            if (e.target.files && e.target.files[0]) {
                                const file = e.target.files[0];
                                originalFileName = file.name.split('.')[0] || 'video';
                                const url = URL.createObjectURL(file);
                                videoPreview.src = url;
                                videoPreview.style.display = 'block';
                                videoPreview.onloadedmetadata = () => {
                                    showAlert(`Video loaded. Duration: ${videoPreview.duration.toFixed(2)}s. Ready to convert.`, 'info');
                                    statusP.style.display = 'none';
                                }
                                videoPreview.onerror = () => showAlert('Error loading video. It might be an unsupported format.', 'error');
                            } else {
                                videoPreview.style.display = 'none';
                                videoPreview.src = "";
                            }
                        };


                        convertButton.onclick = () => {
                            if (!videoPreview.src || !videoPreview.src.startsWith('blob:')) {
                                showAlert('Please select a video file first.', 'error');
                                return;
                            }


                            const targetMimeType = formatSelect.value;
                            if (!MediaRecorder.isTypeSupported(targetMimeType)) {
                                showAlert(`Your browser does not support recording to ${targetMimeType}. Try another format or browser. Supported types often include 'video/webm', 'video/webm;codecs=vp8', 'video/webm;codecs=vp9'. MP4 support is less common for MediaRecorder.`, 'error');
                                return;
                            }


                            showAlert('Starting conversion... The video will play. Do not close modal.', 'info');
                            statusP.textContent = 'Conversion in progress... 0%';
                            statusP.style.display = 'block';
                            
                            videoPreview.currentTime = 0;
                            
                            const stream = videoPreview.captureStream ? videoPreview.captureStream() : videoPreview.mozCaptureStream ? videoPreview.mozCaptureStream() : null;


                            if (!stream) {
                                showAlert('Could not capture video stream. Your browser might not support this feature (captureStream/mozCaptureStream).', 'error');
                                return;
                            }


                            recordedChunks = [];
                            try {
                                mediaRecorder = new MediaRecorder(stream, { mimeType: targetMimeType });
                            } catch (err) {
                                 showAlert(`Error initializing MediaRecorder with ${targetMimeType}: ${err.message}. Try a simpler WebM option.`, 'error');
                                 return;
                            }




                            mediaRecorder.ondataavailable = (event) => {
                                if (event.data.size > 0) {
                                    recordedChunks.push(event.data);
                                }
                            };


                            mediaRecorder.onstop = () => {
                                if (recordedChunks.length === 0) {
                                    showAlert('Conversion stopped, but no data was recorded. The selected format/codec might be problematic for your browser.', 'error');
                                    statusP.textContent = 'Conversion failed: No data.';
                                    return;
                                }
                                const blob = new Blob(recordedChunks, { type: targetMimeType.split(';')[0] }); // Use base MIME type for Blob
                                const url = URL.createObjectURL(blob);
                                const a = document.createElement('a');
                                a.href = url;
                                a.download = `${originalFileName}_converted.${targetMimeType.split('/')[1].split(';')[0]}`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                URL.revokeObjectURL(url);
                                showAlert('Video conversion finished!', 'success');
                                statusP.textContent = 'Conversion complete!';
                                videoPreview.pause();
                            };
                            
                            mediaRecorder.onerror = (event) => {
                                showAlert(`MediaRecorder error: ${event.error ? event.error.name : 'Unknown error'}. Try a different format.`, 'error');
                                statusP.textContent = `Error: ${event.error ? event.error.name : 'Unknown'}`;
                                videoPreview.pause();
                            };
                            
                            videoPreview.onended = () => {
                                if (mediaRecorder && mediaRecorder.state === 'recording') {
                                    mediaRecorder.stop();
                                }
                            };
                            
                            videoPreview.ontimeupdate = () => {
                                if (videoPreview.duration && mediaRecorder && mediaRecorder.state === 'recording') {
                                    const progress = (videoPreview.currentTime / videoPreview.duration) * 100;
                                    statusP.textContent = `Conversion in progress... ${progress.toFixed(0)}%`;
                                }
                            };


                            videoPreview.play().then(() => {
                                mediaRecorder.start(1000); // Timeslice: ondataavailable every 1s (helps with large files)
                            }).catch(err => {
                                showAlert(`Error playing video for conversion: ${err.message}`, 'error');
                                statusP.textContent = `Error: ${err.message}`;
                            });
                        };
                        window.currentToolCleanup = () => {
                            if (mediaRecorder && mediaRecorder.state === 'recording') {
                                mediaRecorder.stop();
                            }
                            if(videoPreview) videoPreview.pause();
                            // Revoke object URL if videoPreview.src is a blob URL to free memory
                            if (videoPreview && videoPreview.src && videoPreview.src.startsWith('blob:')) {
                                URL.revokeObjectURL(videoPreview.src);
                            }
                        };
                    },
                    
                    audioConverter: (() => { // IIFE to share bufferToWave
                        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                        
                        function bufferToWave(abuffer) { // No 'len' needed, use abuffer.length
                            let numOfChan = abuffer.numberOfChannels,
                                length = abuffer.length * numOfChan * 2 + 44, // Correct length calculation
                                buffer = new ArrayBuffer(length),
                                view = new DataView(buffer),
                                channels = [], i, sample,
                                offset = 0, // Frame offset
                                pos = 0;        // Byte offset in buffer


                            // write WAVE header
                            view.setUint32(pos, 0x46464952, false); pos += 4; // "RIFF"
                            view.setUint32(pos, length - 8, true); pos += 4;  // file length - 8
                            view.setUint32(pos, 0x45564157, false); pos += 4; // "WAVE"


                            view.setUint32(pos, 0x20746d66, false); pos += 4; // "fmt " chunk
                            view.setUint32(pos, 16, true); pos += 4;              // length = 16
                            view.setUint16(pos, 1, true); pos += 2;               // PCM (uncompressed)
                            view.setUint16(pos, numOfChan, true); pos += 2;
                            view.setUint32(pos, abuffer.sampleRate, true); pos += 4;
                            view.setUint32(pos, abuffer.sampleRate * 2 * numOfChan, true); pos += 4; // "byte rate"
                            view.setUint16(pos, numOfChan * 2, true); pos += 2; // block align (channels * bytes/sample)
                            view.setUint16(pos, 16, true); pos += 2;              // bits per sample


                            view.setUint32(pos, 0x61746164, false); pos += 4; // "data" - chunk
                            view.setUint32(pos, abuffer.length * numOfChan * 2, true); pos += 4; // chunk length (audio data size)


                            for (i = 0; i < abuffer.numberOfChannels; i++)
                                channels.push(abuffer.getChannelData(i));


                            // Write interleaved data
                            for (offset = 0; offset < abuffer.length; offset++) {
                                for (i = 0; i < numOfChan; i++) {
                                    sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
                                    sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF; // scale to 16-bit signed int
                                    view.setInt16(pos, sample, true);
                                    pos += 2;
                                }
                            }
                            return new Blob([view], { type: 'audio/wav' });
                        }


                        return (container) => { // This is the actual toolInitializer function
                            container.innerHTML = `
                                <p>This tool converts various audio formats (that your browser can play) into WAV format.</p>
                                <input type="file" id="audioConvFile" accept="audio/*">
                                <button id="audioConvButton">Convert to WAV & Download</button>
                                <audio id="audioConvPreview" controls style="width:100%; margin-top:10px; display:none;"></audio>
                            `;
                            const fileInput = container.querySelector('#audioConvFile');
                            const convertButton = container.querySelector('#audioConvButton');
                            const audioPreview = container.querySelector('#audioConvPreview');
                            let decodedAudioBuffer = null; // Renamed to avoid conflict
                            let originalFileName = 'converted_audio';


                            fileInput.onchange = (e) => {
                                if (e.target.files && e.target.files[0]) {
                                    const file = e.target.files[0];
                                    originalFileName = file.name.split('.')[0] || 'audio';
                                    showAlert('Loading audio...', 'info');
                                    const reader = new FileReader();
                                    reader.onload = (event) => {
                                        audioContext.decodeAudioData(event.target.result)
                                            .then(buffer => { // Changed var name here
                                                decodedAudioBuffer = buffer;
                                                // Create a temporary URL for preview to avoid issues with large files / ArrayBuffer
                                                const tempUrl = URL.createObjectURL(file);
                                                audioPreview.src = tempUrl;
                                                audioPreview.style.display = 'block';
                                                audioPreview.oncanplaythrough = () => URL.revokeObjectURL(tempUrl); // Revoke after load
                                                showAlert('Audio loaded. Ready to convert to WAV.', 'success');
                                            })
                                            .catch(err => showAlert(`Error decoding audio: ${err.message}. Try a different format.`, 'error'));
                                    };
                                    reader.onerror = () => showAlert('Error reading file.', 'error');
                                    reader.readAsArrayBuffer(file);
                                } else {
                                    audioPreview.style.display = 'none';
                                    audioPreview.src = "";
                                    decodedAudioBuffer = null;
                                }
                            };


                            convertButton.onclick = () => {
                                if (!decodedAudioBuffer) {
                                    showAlert('Please select and load an audio file first.', 'error');
                                    return;
                                }
                                showAlert('Converting to WAV...', 'info');
                                try {
                                    const wavBlob = bufferToWave(decodedAudioBuffer); // Use the shared function
                                    const url = URL.createObjectURL(wavBlob);
                                    const a = document.createElement('a');
                                    a.href = url;
                                    a.download = `${originalFileName}_converted.wav`;
                                    document.body.appendChild(a);
                                    a.click();
                                    document.body.removeChild(a);
                                    URL.revokeObjectURL(url);
                                    showAlert('Conversion to WAV successful!', 'success');
                                } catch (err) {
                                    showAlert(`Error converting to WAV: ${err.message}`, 'error');
                                }
                            };
                            // Make bufferToWave accessible for the trimmer
                            toolInitializers.audioConverter.bufferToWave = bufferToWave;


                            window.currentToolCleanup = () => {
                                if (audioPreview && audioPreview.src && audioPreview.src.startsWith('blob:')) {
                                    URL.revokeObjectURL(audioPreview.src);
                                }
                            };
                        };
                    })(), // Immediately invoke the IIFE


                    audioTrimmer: (container) => {
                        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                        container.innerHTML = `
                            <input type="file" id="trimAudioFile" accept="audio/*">
                            <audio id="trimAudioPreview" controls style="width:100%; margin-top:10px; display:none;"></audio>
                            <div id="trimControls" style="display:none; margin-top:10px;">
                                <label for="trimStartTime">Start Time (s):</label>
                                <input type="number" id="trimStartTime" value="0" min="0" step="0.01">
                                <label for="trimEndTime">End Time (s):</label>
                                <input type="number" id="trimEndTime" value="0" min="0" step="0.01">
                                <p id="audioDurationInfo"></p>
                                <button id="trimButton">Trim & Download WAV</button>
                            </div>
                        `;
                        const fileInput = container.querySelector('#trimAudioFile');
                        const audioPreview = container.querySelector('#trimAudioPreview');
                        const trimControls = container.querySelector('#trimControls');
                        const startTimeInput = container.querySelector('#trimStartTime');
                        const endTimeInput = container.querySelector('#trimEndTime');
                        const durationInfo = container.querySelector('#audioDurationInfo');
                        const trimButton = container.querySelector('#trimButton');
                        
                        let sourceBuffer = null;
                        let originalFileName = 'trimmed_audio';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                const file = e.target.files[0];
                                originalFileName = file.name.split('.')[0] || 'audio';
                                showAlert('Loading audio...', 'info');
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    audioContext.decodeAudioData(event.target.result)
                                        .then(decodedBuffer => {
                                            sourceBuffer = decodedBuffer;
                                            const tempUrl = URL.createObjectURL(file);
                                            audioPreview.src = tempUrl;
                                            audioPreview.style.display = 'block';
                                            audioPreview.onloadedmetadata = () => {
                                                URL.revokeObjectURL(tempUrl); // Revoke after duration is known
                                                const duration = sourceBuffer.duration; // Use buffer duration for accuracy
                                                durationInfo.textContent = `Duration: ${duration.toFixed(2)}s`;
                                                endTimeInput.value = duration.toFixed(2);
                                                endTimeInput.max = duration.toFixed(2);
                                                startTimeInput.max = duration.toFixed(2);
                                                trimControls.style.display = 'block';
                                                showAlert('Audio loaded. Set trim times.', 'success');
                                            };
                                            audioPreview.onerror = () => showAlert('Error playing preview.', 'error');
                                        })
                                        .catch(err => showAlert(`Error decoding audio: ${err.message}`, 'error'));
                                };
                                reader.readAsArrayBuffer(file);
                            } else {
                                 audioPreview.style.display = 'none';
                                 trimControls.style.display = 'none';
                                 audioPreview.src = "";
                                 sourceBuffer = null;
                            }
                        };


                        trimButton.onclick = () => {
                            if (!sourceBuffer) {
                                showAlert('Please load an audio file first.', 'error');
                                return;
                            }
                            const startTime = parseFloat(startTimeInput.value);
                            const endTime = parseFloat(endTimeInput.value);


                            if (isNaN(startTime) || isNaN(endTime) || startTime < 0 || endTime <= startTime || endTime > sourceBuffer.duration) {
                                showAlert('Invalid start/end times. Ensure End Time is after Start Time and within audio duration.', 'error');
                                return;
                            }
                            
                            showAlert('Trimming audio...', 'info');
                            try {
                                const startOffset = Math.floor(startTime * sourceBuffer.sampleRate);
                                const endOffset = Math.floor(endTime * sourceBuffer.sampleRate);
                                const frameCount = endOffset - startOffset;


                                if (frameCount <=0) {
                                    showAlert('Trimmed duration is zero or negative.', 'error');
                                    return;
                                }


                                const trimmedBuffer = audioContext.createBuffer(
                                    sourceBuffer.numberOfChannels,
                                    frameCount,
                                    sourceBuffer.sampleRate
                                );


                                for (let i = 0; i < sourceBuffer.numberOfChannels; i++) {
                                    const channelData = sourceBuffer.getChannelData(i);
                                    const trimmedChannelData = trimmedBuffer.getChannelData(i);
                                    // Use subarray correctly: source.subarray(begin, end)
                                    trimmedChannelData.set(channelData.subarray(startOffset, endOffset));
                                }
                                
                                if (typeof toolInitializers.audioConverter.bufferToWave !== 'function') {
                                    showAlert('Audio conversion utility not found.', 'error');
                                    return;
                                }
                                const wavBlob = toolInitializers.audioConverter.bufferToWave(trimmedBuffer);


                                const url = URL.createObjectURL(wavBlob);
                                const a = document.createElement('a');
                                a.href = url;
                                a.download = `${originalFileName}_trimmed.wav`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                URL.revokeObjectURL(url);
                                showAlert('Audio trimmed and downloaded!', 'success');


                            } catch (err) {
                                showAlert(`Error trimming audio: ${err.message}`, 'error');
                                console.error("Trimming error:", err);
                            }
                        };
                        window.currentToolCleanup = () => {
                            if (audioPreview && audioPreview.src && audioPreview.src.startsWith('blob:')) {
                                URL.revokeObjectURL(audioPreview.src);
                            }
                        };
                    },
                    
                    // ... (The rest of the toolInitializers from the previous response) ...
                    // --- Age Calculator ---
                    ageCalculator: (container) => {
                        container.innerHTML = `
                            <label for="birthDate">Enter your Date of Birth:</label>
                            <input type="date" id="birthDate">
                            <button id="calculateAgeBtn">Calculate Age</button>
                            <div id="ageResult" class="result-area" style="display:none;"></div>
                        `;
                        const birthDateInput = container.querySelector('#birthDate');
                        const calculateBtn = container.querySelector('#calculateAgeBtn');
                        const ageResultDiv = container.querySelector('#ageResult');


                        calculateBtn.onclick = () => {
                            const birthDateString = birthDateInput.value;
                            if (!birthDateString) {
                                showAlert('Please enter your date of birth.', 'error');
                                ageResultDiv.style.display = 'none';
                                return;
                            }
                            const birthDate = new Date(birthDateString);
                            const today = new Date();


                            if (birthDate > today) {
                                showAlert('Birth date cannot be in the future.', 'error');
                                ageResultDiv.style.display = 'none';
                                return;
                            }


                            let years = today.getFullYear() - birthDate.getFullYear();
                            let months = today.getMonth() - birthDate.getMonth();
                            let days = today.getDate() - birthDate.getDate();


                            if (days < 0) {
                                months--;
                                // Get days in the previous month of 'today'
                                const prevMonth = new Date(today.getFullYear(), today.getMonth(), 0);
                                days += prevMonth.getDate();
                            }
                            if (months < 0) {
                                years--;
                                months += 12;
                            }
                            ageResultDiv.innerHTML = `You are: <br>
                                                    <strong>${years}</strong> years,
                                                    <strong>${months}</strong> months, and
                                                    <strong>${days}</strong> days old.`;
                            ageResultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- EMI Calculator ---
                    emiCalculator: (container) => {
                        container.innerHTML = `
                            <label for="loanAmount">Loan Amount (₹):</label>
                            <input type="number" id="loanAmount" placeholder="e.g., 100000" min="0">
                            <label for="interestRate">Annual Interest Rate (%):</label>
                            <input type="number" id="interestRate" placeholder="e.g., 10.5" min="0" step="0.01">
                            <label for="loanTenure">Loan Tenure (months):</label>
                            <input type="number" id="loanTenure" placeholder="e.g., 12" min="1">
                            <button id="calculateEmiBtn">Calculate EMI</button>
                            <div id="emiResult" class="result-area" style="display:none;"></div>
                        `;
                        const amountInput = container.querySelector('#loanAmount');
                        const rateInput = container.querySelector('#interestRate');
                        const tenureInput = container.querySelector('#loanTenure');
                        const calculateBtn = container.querySelector('#calculateEmiBtn');
                        const resultDiv = container.querySelector('#emiResult');


                        calculateBtn.onclick = () => {
                            const P = parseFloat(amountInput.value);
                            const annualRate = parseFloat(rateInput.value);
                            const N = parseInt(tenureInput.value);


                            if (isNaN(P) || isNaN(annualRate) || isNaN(N) || P <= 0 || annualRate < 0 || N <= 0) {
                                showAlert('Please enter valid positive numbers for amount & tenure, and a non-negative rate.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }
                            
                            if (annualRate === 0) { // Handle zero interest rate
                                 const EMI = P / N;
                                 resultDiv.innerHTML = `Monthly EMI: <strong>₹${EMI.toFixed(2)}</strong><br>
                                           Total Interest Payable: <strong>₹0.00</strong><br>
                                           Total Payment (Principal + Interest): <strong>₹${P.toFixed(2)}</strong>`;
                                resultDiv.style.display = 'block';
                                hideAlert();
                                return;
                            }




                            const r = annualRate / (12 * 100); // Monthly interest rate
                            const EMI = (P * r * Math.pow(1 + r, N)) / (Math.pow(1 + r, N) - 1);
                            const totalPayment = EMI * N;
                            const totalInterest = totalPayment - P;


                            if (!isFinite(EMI)) {
                                showAlert('Calculation resulted in an invalid number. Please check your inputs.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }


                            resultDiv.innerHTML = `Monthly EMI: <strong>₹${EMI.toFixed(2)}</strong><br>
                                                Total Interest Payable: <strong>₹${totalInterest.toFixed(2)}</strong><br>
                                                Total Payment (Principal + Interest): <strong>₹${totalPayment.toFixed(2)}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- SIP Calculator ---
                    sipCalculator: (container) => {
                        container.innerHTML = `
                            <label for="monthlyInvestment">Monthly Investment (₹):</label>
                            <input type="number" id="monthlyInvestment" placeholder="e.g., 5000" min="0">
                            <label for="expectedReturnRate">Expected Annual Return Rate (%):</label>
                            <input type="number" id="expectedReturnRate" placeholder="e.g., 12" min="0" step="0.01">
                            <label for="investmentDuration">Investment Duration (years):</label>
                            <input type="number" id="investmentDuration" placeholder="e.g., 10" min="1">
                            <button id="calculateSipBtn">Calculate Future Value</button>
                            <div id="sipResult" class="result-area" style="display:none;"></div>
                        `;
                        const monthlyInvestmentInput = container.querySelector('#monthlyInvestment');
                        const returnRateInput = container.querySelector('#expectedReturnRate');
                        const durationInput = container.querySelector('#investmentDuration');
                        const calculateBtn = container.querySelector('#calculateSipBtn');
                        const resultDiv = container.querySelector('#sipResult');


                        calculateBtn.onclick = () => {
                            const P = parseFloat(monthlyInvestmentInput.value);
                            const annualRate = parseFloat(returnRateInput.value);
                            const t_years = parseInt(durationInput.value);


                            if (isNaN(P) || isNaN(annualRate) || isNaN(t_years) || P <= 0 || annualRate < 0 || t_years <= 0) {
                                showAlert('Please enter valid positive numbers for investment & duration, and a non-negative rate.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }


                            const n = t_years * 12;
                            const i = annualRate / 12 / 100;
                            let M;


                            if (i === 0) { // Handle zero interest rate for SIP
                                M = P * n;
                            } else {
                                M = P * ( (Math.pow(1 + i, n) - 1) / i ) * (1 + i);
                            }
                            
                            const totalInvestment = P * n;
                            const wealthGained = M - totalInvestment;


                            resultDiv.innerHTML = `Invested Amount: <strong>₹${totalInvestment.toFixed(2)}</strong><br>
                                                Estimated Returns: <strong>₹${wealthGained.toFixed(2)}</strong><br>
                                                Total Future Value: <strong>₹${M.toFixed(2)}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- QR Code Generator ---
                    qrCodeGenerator: (container) => {
                        container.innerHTML = `
                            <label for="qrText">Text or URL for QR Code:</label>
                            <textarea id="qrText" placeholder="Enter text here"></textarea>
                            <label for="qrSize">Size (pixels):</label>
                            <input type="number" id="qrSize" value="200" min="50" max="500">
                            <button id="generateQrBtn">Generate QR Code</button>
                            <div id="qrCodeContainer" class="result-area" style="text-align:center; display:none; padding:10px; background-color:white;"></div>
                            <button id="downloadQrBtn" style="display:none;">Download QR Code</button>
                        `;
                        const textInput = container.querySelector('#qrText');
                        const sizeInput = container.querySelector('#qrSize');
                        const generateBtn = container.querySelector('#generateQrBtn');
                        const qrContainer = container.querySelector('#qrCodeContainer');
                        const downloadBtn = container.querySelector('#downloadQrBtn');
                        let qrCanvasInstance = null; // To hold the canvas element itself


                        generateBtn.onclick = () => {
                            const text = textInput.value.trim();
                            const size = parseInt(sizeInput.value);
                            if (!text) {
                                showAlert('Please enter text or URL for the QR code.', 'error');
                                qrContainer.style.display = 'none';
                                downloadBtn.style.display = 'none';
                                return;
                            }
                            if (isNaN(size) || size < 50 || size > 500) {
                                showAlert('Please enter a valid size between 50 and 500 pixels.', 'error');
                                return;
                            }


                            qrContainer.innerHTML = ''; // Clear previous QR code
                            
                            try {
                                if (typeof QRCode !== 'undefined') { // Check if a library like qrcode.js is loaded
                                    qrCanvasInstance = document.createElement('div'); // qrcode.js typically targets a div
                                    qrContainer.appendChild(qrCanvasInstance);
                                    new QRCode(qrCanvasInstance, {
                                        text: text,
                                        width: size,
                                        height: size,
                                        colorDark : "#000000",
                                        colorLight : "#ffffff",
                                        correctLevel : QRCode.CorrectLevel.H
                                    });
                                    // qrcode.js generates an img and a canvas, we want the canvas for download
                                    // Wait a moment for QRCode.js to render
                                    setTimeout(() => {
                                        const canvasFromLib = qrCanvasInstance.querySelector('canvas');
                                        if (canvasFromLib) {
                                            qrCanvasInstance = canvasFromLib; // Now qrCanvasInstance is the actual canvas
                                        } else {
                                            // Fallback if canvas not found, might be an img tag
                                            const imgFromLib = qrCanvasInstance.querySelector('img');
                                            if (imgFromLib) {
                                                // Create a canvas from the image
                                                const tempCanvas = document.createElement('canvas');
                                                tempCanvas.width = imgFromLib.width;
                                                tempCanvas.height = imgFromLib.height;
                                                tempCanvas.getContext('2d').drawImage(imgFromLib, 0, 0);
                                                qrCanvasInstance = tempCanvas;
                                            }
                                        }
                                    }, 100);
                                    showAlert('QR Code generated!', 'success');
                                } else {
                                    // Fallback to simplified canvas drawing if no library
                                    qrCanvasInstance = document.createElement('canvas');
                                    qrContainer.appendChild(qrCanvasInstance);
                                    const ctx = qrCanvasInstance.getContext('2d');
                                    qrCanvasInstance.width = size;
                                    qrCanvasInstance.height = size;
                                    ctx.fillStyle = 'white';
                                    ctx.fillRect(0, 0, size, size);
                                    ctx.fillStyle = 'black';
                                    ctx.font = `${size/15}px Arial`;
                                    ctx.textAlign = 'center';
                                    ctx.textBaseline = 'middle';
                                    // Simple placeholder text rendering - not a real QR
                                    const lines = text.match(/.{1,20}/g) || [text]; // Split text
                                    lines.forEach((line, i) => {
                                        ctx.fillText(line, size / 2, size / 2 - (lines.length/2 * (size/10)) + (i * (size/10)) );
                                    });
                                    ctx.strokeRect(size*0.1, size*0.1, size*0.8, size*0.8); // Border
                                    showAlert('QR Code (placeholder - library not found) generated. For real QR codes, integrate a library.', 'info');
                                }
                                qrContainer.style.display = 'block';
                                downloadBtn.style.display = 'inline-block';
                            } catch (e) {
                                showAlert('Error generating QR code: ' + e.message, 'error');
                                console.error("QR Gen Error:", e);
                                qrContainer.style.display = 'none';
                                downloadBtn.style.display = 'none';
                            }
                        };


                        downloadBtn.onclick = () => {
                            if (qrCanvasInstance && qrCanvasInstance.tagName === 'CANVAS') {
                                const dataURL = qrCanvasInstance.toDataURL('image/png');
                                const a = document.createElement('a');
                                a.href = dataURL;
                                a.download = 'qrcode.png';
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                            } else if (qrCanvasInstance && qrCanvasInstance.tagName === 'IMG') { // If library generated an IMG
                                 const a = document.createElement('a');
                                 a.href = qrCanvasInstance.src;
                                 a.download = 'qrcode.png';
                                 document.body.appendChild(a);
                                 a.click();
                                 document.body.removeChild(a);
                            } else {
                                showAlert('QR Code canvas not available for download.', 'error');
                            }
                        };
                    },


                    // --- Password Generator ---
                    passwordGenerator: (container) => {
                        container.innerHTML = `
                            <label for="passLength">Password Length:</label>
                            <input type="number" id="passLength" value="16" min="8" max="128">
                            <div class="option-group">
                                <input type="checkbox" id="incUppercase" checked> <label for="incUppercase">Uppercase (A-Z)</label><br>
                                <input type="checkbox" id="incLowercase" checked> <label for="incLowercase">Lowercase (a-z)</label><br>
                                <input type="checkbox" id="incNumbers" checked> <label for="incNumbers">Numbers (0-9)</label><br>
                                <input type="checkbox" id="incSymbols" checked> <label for="incSymbols">Symbols (!@#$%^&*)</label>
                            </div>
                            <button id="generatePassBtn">Generate Password</button>
                            <div class="result-area" style="margin-top:1rem; display:flex; align-items:center; justify-content:space-between;">
                                <input type="text" id="generatedPassword" readonly style="flex-grow:1; margin-right:10px; background-color: var(--background-color); border: 1px solid var(--tool-card-background); color: var(--text-color);">
                                <button id="copyPassBtn" title="Copy to Clipboard" style="padding: 0.5em 0.8em;">📋</button>
                            </div>
                        `;
                        const lengthInput = container.querySelector('#passLength');
                        const uppercaseCheck = container.querySelector('#incUppercase');
                        const lowercaseCheck = container.querySelector('#incLowercase');
                        const numbersCheck = container.querySelector('#incNumbers');
                        const symbolsCheck = container.querySelector('#incSymbols');
                        const generateBtn = container.querySelector('#generatePassBtn');
                        const passwordOutput = container.querySelector('#generatedPassword');
                        const copyBtn = container.querySelector('#copyPassBtn');


                        const charSets = {
                            uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                            lowercase: 'abcdefghijklmnopqrstuvwxyz',
                            numbers: '0123456789',
                            symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?'
                        };


                        generateBtn.onclick = () => {
                            const length = parseInt(lengthInput.value);
                            let charset = '';
                            let guaranteedChars = ''; // To ensure at least one char from each selected set


                            if (uppercaseCheck.checked) {
                                charset += charSets.uppercase;
                                guaranteedChars += charSets.uppercase[Math.floor(Math.random() * charSets.uppercase.length)];
                            }
                            if (lowercaseCheck.checked) {
                                charset += charSets.lowercase;
                                 guaranteedChars += charSets.lowercase[Math.floor(Math.random() * charSets.lowercase.length)];
                            }
                            if (numbersCheck.checked) {
                                charset += charSets.numbers;
                                guaranteedChars += charSets.numbers[Math.floor(Math.random() * charSets.numbers.length)];
                            }
                            if (symbolsCheck.checked) {
                                charset += charSets.symbols;
                                guaranteedChars += charSets.symbols[Math.floor(Math.random() * charSets.symbols.length)];
                            }


                            if (charset === '') {
                                showAlert('Please select at least one character type.', 'error');
                                passwordOutput.value = '';
                                return;
                            }
                            if (length < 8 || length > 128) {
                                showAlert('Password length must be between 8 and 128.', 'error');
                                return;
                            }
                            if (length < guaranteedChars.length) {
                                showAlert('Password length is too short to include one of each selected character type.', 'error');
                                return;
                            }




                            let password = guaranteedChars;
                            for (let i = guaranteedChars.length; i < length; i++) {
                                password += charset.charAt(Math.floor(Math.random() * charset.length));
                            }
                            
                            // Shuffle the password to make guaranteed characters random
                            password = password.split('').sort(() => 0.5 - Math.random()).join('');


                            passwordOutput.value = password;
                            hideAlert();
                        };


                        copyBtn.onclick = () => {
                            if (passwordOutput.value) {
                                navigator.clipboard.writeText(passwordOutput.value)
                                    .then(() => showAlert('Password copied to clipboard!', 'success'))
                                    .catch(err => showAlert('Failed to copy password.', 'error'));
                            }
                        };
                    },


                    // --- Word Counter ---
                    wordCounter: (container) => {
                        container.innerHTML = `
                            <textarea id="wcText" placeholder="Paste or type your text here..." rows="8"></textarea>
                            <div id="wcResult" class="result-area" style="margin-top:1rem;">
                                Words: <strong id="wcWords">0</strong><br>
                                Characters (with spaces): <strong id="wcCharsWithSpaces">0</strong><br>
                                Characters (no spaces): <strong id="wcCharsNoSpaces">0</strong><br>
                                Spaces: <strong id="wcSpaces">0</strong><br>
                                Sentences: <strong id="wcSentences">0</strong><br>
                                Paragraphs: <strong id="wcParagraphs">0</strong><br>
                                Reading Time: <strong id="wcReadingTime">~0 min</strong>
                            </div>
                        `;
                        const textArea = container.querySelector('#wcText');
                        const wordsSpan = container.querySelector('#wcWords');
                        const charsWithSpacesSpan = container.querySelector('#wcCharsWithSpaces');
                        const charsNoSpacesSpan = container.querySelector('#wcCharsNoSpaces');
                        const spacesSpan = container.querySelector('#wcSpaces');
                        const sentencesSpan = container.querySelector('#wcSentences');
                        const paragraphsSpan = container.querySelector('#wcParagraphs');
                        const readingTimeSpan = container.querySelector('#wcReadingTime');


                        textArea.oninput = () => {
                            const text = textArea.value;
                            
                            const wordsArray = text.match(/\b[-'\w]+\b/g);
                            const words = wordsArray ? wordsArray.length : 0;
                            
                            const charsWithSpaces = text.length;
                            const charsNoSpaces = text.replace(/\s+/g, '').length;
                            const spaces = (text.match(/\s/g) || []).length;


                            const sentencesArray = text.match(/[^.!?]+[.!?]+(\s|$)/g);
                            const sentences = sentencesArray ? sentencesArray.length : (text.trim() ? 1: 0);
                            
                            const paragraphsArray = text.split(/\n\s*\n/).filter(p => p.trim() !== '');
                            const paragraphs = paragraphsArray.length || (text.trim() ? 1:0);
                            
                            const wpm = 200;
                            const readingTimeMinutes = words / wpm;
                            let readingTimeText = '';
                            if (readingTimeMinutes === 0) {
                                readingTimeText = `~0 min`;
                            } else if (readingTimeMinutes < 1) {
                                const seconds = Math.round(readingTimeMinutes * 60);
                                readingTimeText = `~${seconds} sec`;
                            } else {
                                readingTimeText = `~${Math.ceil(readingTimeMinutes)} min`;
                            }


                            wordsSpan.textContent = words;
                            charsWithSpacesSpan.textContent = charsWithSpaces;
                            charsNoSpacesSpan.textContent = charsNoSpaces;
                            spacesSpan.textContent = spaces;
                            sentencesSpan.textContent = sentences;
                            paragraphsSpan.textContent = paragraphs;
                            readingTimeSpan.textContent = readingTimeText;
                        };
                    },


                    // --- Base64 Encoder/Decoder ---
                    base64EncoderDecoder: (container) => {
                        container.innerHTML = `
                            <textarea id="b64Input" placeholder="Enter text to encode or Base64 to decode" rows="5"></textarea>
                            <button id="b64EncodeBtn">Encode to Base64</button>
                            <button id="b64DecodeBtn">Decode from Base64</button>
                            <label for="b64Output" style="margin-top:1rem; display:block;">Result:</label>
                            <textarea id="b64Output" readonly rows="5"></textarea>
                        `;
                        const inputArea = container.querySelector('#b64Input');
                        const outputArea = container.querySelector('#b64Output');
                        const encodeBtn = container.querySelector('#b64EncodeBtn');
                        const decodeBtn = container.querySelector('#b64DecodeBtn');


                        encodeBtn.onclick = () => {
                            try {
                                // Handle UTF-8 characters correctly for btoa
                                const utf8Encoded = new TextEncoder().encode(inputArea.value);
                                let binaryString = '';
                                utf8Encoded.forEach(byte => binaryString += String.fromCharCode(byte));
                                outputArea.value = btoa(binaryString);
                                hideAlert();
                            } catch (e) {
                                showAlert('Error encoding: ' + e.message, 'error');
                                outputArea.value = '';
                            }
                        };


                        decodeBtn.onclick = () => {
                            try {
                                 // Handle UTF-8 characters correctly for atob
                                const binaryString = atob(inputArea.value);
                                const bytes = new Uint8Array(binaryString.length);
                                for (let i = 0; i < binaryString.length; i++) {
                                    bytes[i] = binaryString.charCodeAt(i);
                                }
                                outputArea.value = new TextDecoder().decode(bytes);
                                hideAlert();
                            } catch (e) {
                                showAlert('Error decoding: Invalid Base64 string or character issue. ' + e.message, 'error');
                                outputArea.value = '';
                            }
                        };
                    },


                    // --- Color Picker Tool ---
                    colorPicker: (container) => {
                        container.innerHTML = `
                            <label for="htmlColorPicker">Select a Color:</label>
                            <input type="color" id="htmlColorPicker" value="#FFD700" style="width:100%; height: 40px; margin-bottom:1rem;">
                            <div class="color-picker-display result-area">
                                <div id="colorPreview" class="color-preview"></div>
                                <div id="colorValues" class="color-values">
                                    <p>HEX: <strong id="hexValue"></strong> <button class="copy-color-val" data-type="hex" title="Copy HEX">📋</button></p>
                                    <p>RGB: <strong id="rgbValue"></strong> <button class="copy-color-val" data-type="rgb" title="Copy RGB">📋</button></p>
                                    <p>HSL: <strong id="hslValue"></strong> <button class="copy-color-val" data-type="hsl" title="Copy HSL">📋</button></p>
                                </div>
                            </div>
                        `;
                        const colorPickerInput = container.querySelector('#htmlColorPicker');
                        const colorPreviewDiv = container.querySelector('#colorPreview');
                        const hexValueSpan = container.querySelector('#hexValue');
                        const rgbValueSpan = container.querySelector('#rgbValue');
                        const hslValueSpan = container.querySelector('#hslValue');
                        const copyButtons = container.querySelectorAll('.copy-color-val');


                        let currentHex, currentRgb, currentHsl;


                        function updateColorValues(hex) {
                            currentHex = hex.toUpperCase();
                            colorPreviewDiv.style.backgroundColor = currentHex;
                            hexValueSpan.textContent = currentHex;


                            const rgb = hexToRgb(currentHex);
                            currentRgb = `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
                            rgbValueSpan.textContent = currentRgb;


                            const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
                            currentHsl = `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
                            hslValueSpan.textContent = currentHsl;
                        }


                        colorPickerInput.oninput = (e) => {
                            updateColorValues(e.target.value);
                            hideAlert();
                        };
                        
                        copyButtons.forEach(btn => {
                            btn.style.padding = "0.2em 0.5em"; // Smaller copy buttons
                            btn.style.marginLeft = "5px";
                            btn.onclick = () => {
                                let valueToCopy;
                                const type = btn.dataset.type;
                                if (type === 'hex') valueToCopy = currentHex;
                                else if (type === 'rgb') valueToCopy = currentRgb;
                                else if (type === 'hsl') valueToCopy = currentHsl;


                                if (valueToCopy) {
                                    navigator.clipboard.writeText(valueToCopy)
                                        .then(() => showAlert(`${type.toUpperCase()} value copied!`, 'success'))
                                        .catch(() => showAlert('Failed to copy.', 'error'));
                                }
                            };
                        });


                        updateColorValues(colorPickerInput.value); // Initial value


                        function hexToRgb(hex) {
                            let r = 0, g = 0, b = 0;
                            if (hex.length === 4) {
                                r = parseInt(hex[1] + hex[1], 16);
                                g = parseInt(hex[2] + hex[2], 16);
                                b = parseInt(hex[3] + hex[3], 16);
                            } else if (hex.length === 7) {
                                r = parseInt(hex.substring(1, 3), 16);
                                g = parseInt(hex.substring(3, 5), 16);
                                b = parseInt(hex.substring(5, 7), 16);
                            }
                            return { r, g, b };
                        }


                        function rgbToHsl(r, g, b) {
                            r /= 255; g /= 255; b /= 255;
                            const max = Math.max(r, g, b), min = Math.min(r, g, b);
                            let h, s, l = (max + min) / 2;


                            if (max === min) {
                                h = s = 0;
                            } else {
                                const d = max - min;
                                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
                                switch (max) {
                                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                                    case g: h = (b - r) / d + 2; break;
                                    case b: h = (r - g) / d + 4; break;
                                }
                                h /= 6;
                            }
                            return {
                                h: Math.round(h * 360),
                                s: Math.round(s * 100),
                                l: Math.round(l * 100)
                            };
                        }
                    },


                    // --- Text to Speech ---
                    textToSpeech: (container) => {
                        container.innerHTML = `
                            <textarea id="ttsText" placeholder="Enter text to speak..." rows="6"></textarea>
                            <div class="option-group">
                                <label for="ttsVoice">Voice:</label>
                                <select id="ttsVoice"></select>
                                <label for="ttsRate" style="margin-left:10px;">Rate: <span id="ttsRateVal">1</span></label>
                                <input type="range" id="ttsRate" min="0.5" max="2" value="1" step="0.1" style="vertical-align: middle;">
                                <label for="ttsPitch" style="margin-left:10px;">Pitch: <span id="ttsPitchVal">1</span></label>
                                <input type="range" id="ttsPitch" min="0" max="2" value="1" step="0.1" style="vertical-align: middle;">
                            </div>
                            <button id="ttsSpeakBtn">Speak</button>
                            <button id="ttsPauseBtn">Pause</button>
                            <button id="ttsResumeBtn">Resume</button>
                            <button id="ttsStopBtn">Stop</button>
                        `;
                        const textInput = container.querySelector('#ttsText');
                        const voiceSelect = container.querySelector('#ttsVoice');
                        const rateInput = container.querySelector('#ttsRate');
                        const pitchInput = container.querySelector('#ttsPitch');
                        const rateValSpan = container.querySelector('#ttsRateVal');
                        const pitchValSpan = container.querySelector('#ttsPitchVal');
                        const speakBtn = container.querySelector('#ttsSpeakBtn');
                        const pauseBtn = container.querySelector('#ttsPauseBtn');
                        const resumeBtn = container.querySelector('#ttsResumeBtn');
                        const stopBtn = container.querySelector('#ttsStopBtn');


                        const synth = window.speechSynthesis;
                        if (!synth) {
                            container.innerHTML = "<p>Sorry, your browser doesn't support Text to Speech.</p>";
                            return;
                        }
                        let voices = [];
                        let currentUtterance = null;


                        function populateVoiceList() {
                            voices = synth.getVoices().sort((a,b) => a.name.localeCompare(b.name));
                            const selectedVoiceName = voiceSelect.value;
                            voiceSelect.innerHTML = '';
                            voices.forEach(voice => {
                                const option = document.createElement('option');
                                option.textContent = `${voice.name} (${voice.lang})`;
                                option.value = voice.name;
                                if (voice.default) option.selected = true;
                                voiceSelect.appendChild(option);
                            });
                            if (selectedVoiceName) voiceSelect.value = selectedVoiceName; // Retain selection
                        }


                        populateVoiceList();
                        if (synth.onvoiceschanged !== undefined) {
                            synth.onvoiceschanged = populateVoiceList;
                        }
                        
                        rateInput.oninput = () => rateValSpan.textContent = rateInput.value;
                        pitchInput.oninput = () => pitchValSpan.textContent = pitchInput.value;




                        speakBtn.onclick = () => {
                            if (synth.speaking) { // If speaking, stop current and start new
                                synth.cancel();
                            }
                            if (textInput.value.trim() !== '') {
                                hideAlert();
                                currentUtterance = new SpeechSynthesisUtterance(textInput.value.trim());
                                const selectedVoice = voices.find(voice => voice.name === voiceSelect.value);
                                if (selectedVoice) currentUtterance.voice = selectedVoice;
                                currentUtterance.pitch = parseFloat(pitchInput.value);
                                currentUtterance.rate = parseFloat(rateInput.value);
                                currentUtterance.onstart = () => {
                                    speakBtn.disabled = true; pauseBtn.disabled = false; resumeBtn.disabled = true; stopBtn.disabled = false;
                                }
                                currentUtterance.onend = () => {
                                    speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                                    currentUtterance = null;
                                };
                                currentUtterance.onerror = (e) => {
                                    showAlert(`Error during speech: ${e.error}`, 'error');
                                    speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                                    currentUtterance = null;
                                };
                                synth.speak(currentUtterance);
                            } else {
                                showAlert('Please enter some text to speak.', 'error');
                            }
                        };
                        pauseBtn.onclick = () => {
                            if(synth.speaking && !synth.paused) {
                                 synth.pause();
                                 pauseBtn.disabled = true; resumeBtn.disabled = false;
                            }
                        };
                        resumeBtn.onclick = () => {
                             if(synth.paused) {
                                synth.resume();
                                pauseBtn.disabled = false; resumeBtn.disabled = true;
                             }
                        };
                        stopBtn.onclick = () => {
                            if(synth.speaking || synth.paused) {
                                synth.cancel(); // This also triggers onend for the utterance
                            }
                             speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                             currentUtterance = null;
                        };
                         // Initial button states
                        speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;


                        window.currentToolCleanup = () => {
                            if (synth && (synth.speaking || synth.paused)) {
                                synth.cancel();
                            }
                        };
                    },


                    // --- Speech to Text ---
                    speechToText: (container) => {
                        container.innerHTML = `
                            <p>Click "Start Listening" and speak into your microphone. Allow microphone access when prompted.</p>
                            <button id="sttStartBtn">Start Listening</button>
                            <button id="sttStopBtn" disabled>Stop Listening</button>
                            <textarea id="sttOutput" placeholder="Transcript will appear here..." readonly rows="6"></textarea>
                        `;
                        const startBtn = container.querySelector('#sttStartBtn');
                        const stopBtn = container.querySelector('#sttStopBtn');
                        const outputArea = container.querySelector('#sttOutput');


                        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
                        if (!SpeechRecognition) {
                            container.innerHTML = '<p>Speech Recognition API is not supported in your browser.</p>';
                            return;
                        }
                        const recognition = new SpeechRecognition();
                        recognition.continuous = true;
                        recognition.interimResults = true;
                        recognition.lang = navigator.language || 'en-US'; // Use browser's language


                        let finalTranscript = '';


                        recognition.onstart = () => {
                            startBtn.disabled = true;
                            stopBtn.disabled = false;
                            showAlert('Listening... Speak clearly.', 'info');
                        };
                        recognition.onend = () => {
                            startBtn.disabled = false;
                            stopBtn.disabled = true;
                            if (outputArea.value.trim() === '') hideAlert(); // Keep "Listening" if no final output yet
                            else if (finalTranscript.trim() !== '') showAlert('Stopped listening.', 'success');
                            else showAlert('Stopped. No speech clearly recognized.', 'info');
                        };
                        recognition.onerror = (event) => {
                            let errorMessage = `Speech recognition error: ${event.error}`;
                            if (event.error === 'no-speech') {
                                errorMessage = 'No speech was detected. Try speaking louder or closer to the microphone.';
                            } else if (event.error === 'audio-capture') {
                                errorMessage = 'Audio capture failed. Ensure your microphone is working and permitted.';
                            } else if (event.error === 'not-allowed') {
                                errorMessage = 'Microphone access denied. Please allow access in browser settings.';
                            }
                            showAlert(errorMessage, 'error');
                            startBtn.disabled = false;
                            stopBtn.disabled = true;
                        };
                        recognition.onresult = (event) => {
                            let interimTranscript = '';
                            for (let i = event.resultIndex; i < event.results.length; ++i) {
                                if (event.results[i].isFinal) {
                                    finalTranscript += event.results[i][0].transcript + ' ';
                                } else {
                                    interimTranscript += event.results[i][0].transcript;
                                }
                            }
                            outputArea.value = finalTranscript + interimTranscript;
                            if (interimTranscript) hideAlert(); // Hide general alerts if interim results are coming
                        };


                        startBtn.onclick = () => {
                            finalTranscript = ''; // Reset final transcript
                            outputArea.value = '';
                            try {
                                recognition.start();
                            } catch (e) { // Catch if already started
                                if (e.name === 'InvalidStateError') {
                                    recognition.stop(); // Stop first, then it will be restartable on next click or via onend
                                    showAlert('Recognition was already active. Try again.', 'info');
                                } else {
                                    showAlert('Could not start recognition: ' + e.message, 'error');
                                }
                            }
                        };
                        stopBtn.onclick = () => recognition.stop();
                        
                        window.currentToolCleanup = () => {
                            if (recognition) {
                                try { recognition.stop(); } catch(e){}
                            }
                        };
                    },


                    // --- JSON Formatter ---
                    jsonFormatter: (container) => {
                        container.innerHTML = `
                            <textarea id="jsonInput" placeholder="Paste your JSON data here..." rows="7"></textarea>
                            <div style="display:flex; align-items:center; margin-bottom:1rem;">
                                <label for="jsonSpaces" style="margin-right:10px; margin-bottom:0;">Indentation:</label>
                                <select id="jsonSpaces" style="width:auto; margin-bottom:0;">
                                    <option value="2">2 Spaces</option>
                                    <option value="4" selected>4 Spaces</option>
                                    <option value="tab">Tabs</option>
                                </select>
                                <button id="formatJsonBtn" style="margin-left:auto;">Format JSON</button>
                            </div>
                            <label for="jsonOutput" style="display:block; margin-bottom:0.5rem;">Formatted JSON:
                                <button id="copyJsonBtn" style="float:right; padding:0.3em 0.6em; margin-top:-5px;">Copy</button>
                            </label>
                            <textarea id="jsonOutput" readonly rows="7"></textarea>
                        `;
                        const inputArea = container.querySelector('#jsonInput');
                        const outputArea = container.querySelector('#jsonOutput');
                        const formatBtn = container.querySelector('#formatJsonBtn');
                        const copyBtn = container.querySelector('#copyJsonBtn');
                        const spacesSelect = container.querySelector('#jsonSpaces');


                        formatBtn.onclick = () => {
                            const jsonString = inputArea.value.trim();
                            if (!jsonString) {
                                showAlert('Input is empty. Paste some JSON data.', 'info');
                                outputArea.value = '';
                                return;
                            }
                            try {
                                const jsonObj = JSON.parse(jsonString);
                                const spacesOption = spacesSelect.value;
                                let indent;
                                if (spacesOption === 'tab') {
                                    indent = '\t';
                                } else {
                                    indent = parseInt(spacesOption);
                                }
                                outputArea.value = JSON.stringify(jsonObj, null, indent);
                                showAlert('JSON formatted successfully!', 'success');
                            } catch (e) {
                                outputArea.value = 'Error: Invalid JSON\n\n' + e.message;
                                showAlert('Invalid JSON: ' + e.message, 'error');
                            }
                        };
                        copyBtn.onclick = () => {
                            if (outputArea.value && !outputArea.value.startsWith('Error:')) {
                                navigator.clipboard.writeText(outputArea.value)
                                    .then(() => showAlert('Formatted JSON copied!', 'success'))
                                    .catch(err => showAlert('Failed to copy.', 'error'));
                            } else if (outputArea.value.startsWith('Error:')) {
                                showAlert('Cannot copy error message. Please format valid JSON first.', 'info');
                            } else {
                                 showAlert('Nothing to copy. Format some JSON first.', 'info');
                            }
                        };
                    },


                    // --- Unit Converter ---
                    unitConverter: (container) => {
                        const units = {
                            length: { name: "Length", items: { meter: 1, kilometer: 1000, centimeter: 0.01, millimeter: 0.001, mile: 1609.34, yard: 0.9144, foot: 0.3048, inch: 0.0254, nautical_mile: 1852 } },
                            weight: { name: "Weight/Mass", items: { kilogram: 1, gram: 0.001, milligram: 0.000001, metric_ton: 1000, pound: 0.45359237, ounce: 0.0283495231, stone: 6.35029318 } },
                            temperature: { name: "Temperature", items: { celsius: 'celsius', fahrenheit: 'fahrenheit', kelvin: 'kelvin' } },
                            area: { name: "Area", items: { square_meter: 1, square_kilometer: 1000000, square_mile: 2589988.11, square_yard: 0.836127, square_foot: 0.092903, acre: 4046.86, hectare: 10000 } },
                            volume: { name: "Volume", items: { cubic_meter: 1, liter: 0.001, milliliter: 0.000001, US_gallon: 0.00378541, US_quart: 0.000946353, US_pint: 0.000473176, US_cup: 0.000236588, US_fluid_ounce: 0.0000295735, imperial_gallon: 0.00454609, imperial_quart: 0.00113652, imperial_pint: 0.000568261, imperial_fluid_ounce: 0.0000284131 } },
                            speed: { name: "Speed", items: { meters_per_second: 1, kilometers_per_hour: 0.277778, miles_per_hour: 0.44704, knot: 0.514444 } },
                            time: { name: "Time", items: { second: 1, minute: 60, hour: 3600, day: 86400, week: 604800, month_avg: 2629746, year_avg: 31556952 } },
                            // Add more categories like pressure, energy, power, data storage etc.
                        };


                        container.innerHTML = `
                            <label for="ucCategory">Category:</label>
                            <select id="ucCategory"></select>
                            <div style="display:flex; gap:10px; margin-top:10px; align-items:flex-end;">
                                <div style="flex:2">
                                    <label for="ucInputValue">Value:</label>
                                    <input type="number" id="ucInputValue" value="1">
                                </div>
                                <div style="flex:3">
                                    <label for="ucFromUnit">From:</label>
                                    <select id="ucFromUnit"></select>
                                </div>
                                <div style="font-size:1.5rem; padding-bottom:0.5rem;">⇄</div>
                                <div style="flex:3">
                                    <label for="ucToUnit">To:</label>
                                    <select id="ucToUnit"></select>
                                </div>
                            </div>
                            <div id="ucResult" class="result-area" style="margin-top:1rem; font-weight:bold; font-size:1.2rem; text-align:center;">Enter values and see result here</div>
                        `;


                        const categorySelect = container.querySelector('#ucCategory');
                        const fromUnitSelect = container.querySelector('#ucFromUnit');
                        const toUnitSelect = container.querySelector('#ucToUnit');
                        const valueInput = container.querySelector('#ucInputValue');
                        const resultDiv = container.querySelector('#ucResult');


                        function populateCategories() {
                            for (const categoryKey in units) {
                                const option = document.createElement('option');
                                option.value = categoryKey;
                                option.textContent = units[categoryKey].name;
                                categorySelect.appendChild(option);
                            }
                        }


                        function populateUnitOptions(categoryKey) {
                            const unitSet = units[categoryKey].items;
                            fromUnitSelect.innerHTML = '';
                            toUnitSelect.innerHTML = '';
                            let count = 0;
                            for (const unit in unitSet) {
                                const optionText = unit.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); // Prettify name
                                
                                const option1 = document.createElement('option');
                                option1.value = unit;
                                option1.textContent = optionText;
                                fromUnitSelect.appendChild(option1);


                                const option2 = document.createElement('option');
                                option2.value = unit;
                                option2.textContent = optionText;
                                toUnitSelect.appendChild(option2);
                                
                                if (count === 1 && Object.keys(unitSet).length > 1) { // Select second item for "To" unit by default if available
                                    option2.selected = true;
                                }
                                count++;
                            }
                             if (Object.keys(unitSet).length === 1) { // If only one unit, select it for "To" as well
                                 toUnitSelect.selectedIndex = 0;
                             }
                        }
                        
                        function convertUnits() {
                            const categoryKey = categorySelect.value;
                            const fromUnit = fromUnitSelect.value;
                            const toUnit = toUnitSelect.value;
                            const inputValue = parseFloat(valueInput.value);


                            if (isNaN(inputValue)) {
                                resultDiv.textContent = 'Invalid input value.';
                                resultDiv.style.color = 'var(--accent-color)';
                                return;
                            }
                            
                            hideAlert();
                            let result;
                            const unitFactors = units[categoryKey].items;


                            if (categoryKey === 'temperature') {
                                if (fromUnit === toUnit) { result = inputValue; }
                                else if (fromUnit === 'celsius') {
                                    if (toUnit === 'fahrenheit') result = (inputValue * 9/5) + 32;
                                    else if (toUnit === 'kelvin') result = inputValue + 273.15;
                                } else if (fromUnit === 'fahrenheit') {
                                    if (toUnit === 'celsius') result = (inputValue - 32) * 5/9;
                                    else if (toUnit === 'kelvin') result = (inputValue - 32) * 5/9 + 273.15;
                                } else if (fromUnit === 'kelvin') {
                                    if (toUnit === 'celsius') result = inputValue - 273.15;
                                    else if (toUnit === 'fahrenheit') result = (inputValue - 273.15) * 9/5 + 32;
                                }
                            } else {
                                const baseValue = inputValue * unitFactors[fromUnit];
                                result = baseValue / unitFactors[toUnit];
                            }
                            
                            if (typeof result === 'undefined') {
                                 resultDiv.textContent = 'Conversion not supported or error.';
                                 resultDiv.style.color = 'var(--accent-color)';
                            } else {
                                const fromUnitText = fromUnitSelect.options[fromUnitSelect.selectedIndex].text;
                                const toUnitText = toUnitSelect.options[toUnitSelect.selectedIndex].text;
                                // Use more decimal places for precision, especially for small numbers
                                const resultPrecision = Math.abs(result) > 0.0001 || result === 0 ? 4 : 8;
                                resultDiv.textContent = `${inputValue} ${fromUnitText} = ${result.toFixed(resultPrecision)} ${toUnitText}`;
                                resultDiv.style.color = 'var(--text-color)';
                            }
                        }


                        categorySelect.onchange = () => {
                            populateUnitOptions(categorySelect.value);
                            convertUnits(); // Convert immediately on category change
                        };
                        fromUnitSelect.onchange = convertUnits;
                        toUnitSelect.onchange = convertUnits;
                        valueInput.oninput = convertUnits;
                        
                        populateCategories();
                        populateUnitOptions(categorySelect.value); // Initial population for default category
                        convertUnits(); // Initial conversion
                    },


                    // --- BMI Calculator ---
                    bmiCalculator: (container) => {
                        container.innerHTML = `
                            <label for="bmiWeight">Weight (kg):</label>
                            <input type="number" id="bmiWeight" placeholder="e.g., 70" min="0">
                            <label for="bmiHeight">Height (cm):</label>
                            <input type="number" id="bmiHeight" placeholder="e.g., 175" min="0">
                            <button id="calculateBmiBtn">Calculate BMI</button>
                            <div id="bmiResult" class="result-area" style="display:none; text-align:center;"></div>
                        `;
                        const weightInput = container.querySelector('#bmiWeight');
                        const heightInput = container.querySelector('#bmiHeight');
                        const calculateBtn = container.querySelector('#calculateBmiBtn');
                        const resultDiv = container.querySelector('#bmiResult');


                        calculateBtn.onclick = () => {
                            const weight = parseFloat(weightInput.value);
                            const heightCm = parseFloat(heightInput.value);


                            if (isNaN(weight) || isNaN(heightCm) || weight <= 0 || heightCm <= 0) {
                                showAlert('Please enter valid positive numbers for weight and height.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }
                            const heightM = heightCm / 100;
                            const bmi = weight / (heightM * heightM);
                            let category = '';
                            let color = 'var(--text-color)';


                            if (bmi < 18.5) { category = 'Underweight'; color = '#3498db'; } // Blue
                            else if (bmi < 24.9) { category = 'Normal weight'; color = '#2ecc71'; } // Green
                            else if (bmi < 29.9) { category = 'Overweight'; color = '#f1c40f'; } // Yellow
                            else if (bmi < 34.9) { category = 'Obesity Class I'; color = '#e67e22'; } // Orange
                            else if (bmi < 39.9) { category = 'Obesity Class II'; color = '#e74c3c'; } // Red
                            else { category = 'Obesity Class III (Severe)'; color = '#c0392b'; } // Darker Red




                            resultDiv.innerHTML = `Your BMI: <strong style="font-size:1.5em;">${bmi.toFixed(2)}</strong><br>
                                                Category: <strong style="color:${color};">${category}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- Timer / Stopwatch ---
                    timerStopwatch: (container) => {
                        container.innerHTML = `
                            <div class="tabs">
                                <button class="tab-button active" data-tab="timerTab">Timer</button>
                                <button class="tab-button" data-tab="stopwatchTab">Stopwatch</button>
                            </div>


                            <div id="timerTab" class="tab-content" style="padding-top:1rem;">
                                <h3>Timer</h3>
                                <div style="display:flex; justify-content:space-around; margin-bottom:1rem;">
                                    <div><label for="timerHours">Hours:</label><br><input type="number" id="timerHours" min="0" max="99" value="0" style="width:60px;"></div>
                                    <div><label for="timerMinutes">Mins:</label><br><input type="number" id="timerMinutes" min="0" max="59" value="5" style="width:60px;"></div>
                                    <div><label for="timerSeconds">Secs:</label><br><input type="number" id="timerSeconds" min="0" max="59" value="0" style="width:60px;"></div>
                                </div>
                                <div class="timer-display" id="timerDisplay">00:05:00</div>
                                <div style="text-align:center;">
                                    <button id="timerStart">Start</button>
                                    <button id="timerPause" disabled>Pause</button>
                                    <button id="timerReset">Reset</button>
                                </div>
                            </div>


                            <div id="stopwatchTab" class="tab-content" style="display:none; padding-top:1rem;">
                                <h3>Stopwatch</h3>
                                <div class="stopwatch-display" id="stopwatchDisplay">00:00:00.00</div>
                                <div style="text-align:center; margin-bottom:1rem;">
                                    <button id="stopwatchStart">Start</button>
                                    <button id="stopwatchStop" disabled>Stop</button>
                                    <button id="stopwatchReset" disabled>Reset</button>
                                    <button id="stopwatchLap" disabled>Lap</button>
                                </div>
                                <ul id="lapsList" class="laps-list"></ul>
                            </div>
                        `;


                        const tabButtons = container.querySelectorAll('.tab-button');
                        const tabContents = container.querySelectorAll('.tab-content');
                        tabButtons.forEach(button => {
                            button.onclick = () => {
                                tabButtons.forEach(btn => btn.classList.remove('active'));
                                button.classList.add('active');
                                tabContents.forEach(content => content.style.display = 'none');
                                container.querySelector(`#${button.dataset.tab}`).style.display = 'block';
                                if (button.dataset.tab === 'timerTab') resetTimerState(false); else resetStopwatchState(false);
                            };
                        });


                        // Timer Logic
                        const timerHoursInput = container.querySelector('#timerHours');
                        const timerMinutesInput = container.querySelector('#timerMinutes');
                        const timerSecondsInput = container.querySelector('#timerSeconds');
                        const timerDisplay = container.querySelector('#timerDisplay');
                        const timerStartBtn = container.querySelector('#timerStart');
                        const timerPauseBtn = container.querySelector('#timerPause');
                        const timerResetBtn = container.querySelector('#timerReset');
                        let timerInterval;
                        let timerTotalSeconds;
                        let timerIsRunning = false;


                        function updateTimerDisplayDOM() {
                            const h = Math.floor(timerTotalSeconds / 3600);
                            const m = Math.floor((timerTotalSeconds % 3600) / 60);
                            const s = timerTotalSeconds % 60;
                            timerDisplay.textContent = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
                        }
                        
                        function setTimerFromInputs() {
                            const h = parseInt(timerHoursInput.value) || 0;
                            const m = parseInt(timerMinutesInput.value) || 0;
                            const s = parseInt(timerSecondsInput.value) || 0;
                            timerTotalSeconds = (h * 3600) + (m * 60) + s;
                            updateTimerDisplayDOM();
                        }
                        [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(input => {
                            input.onchange = () => { if (!timerIsRunning) setTimerFromInputs(); };
                            input.onkeyup = () => { if (!timerIsRunning) setTimerFromInputs(); }; // For immediate update
                        });


                        timerStartBtn.onclick = () => {
                            if (timerIsRunning) return; // Already running
                            if (timerTotalSeconds === undefined || timerTotalSeconds === 0) setTimerFromInputs(); // Set if not set from pause or initially 0


                            if (timerTotalSeconds <= 0) {
                                showAlert('Set a duration greater than 0.', 'error');
                                return;
                            }
                            hideAlert();
                            timerIsRunning = true;
                            timerStartBtn.disabled = true;
                            timerPauseBtn.disabled = false;
                            [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = true);


                            timerInterval = setInterval(() => {
                                if (timerTotalSeconds > 0) {
                                    timerTotalSeconds--;
                                    updateTimerDisplayDOM();
                                } else {
                                    clearInterval(timerInterval);
                                    timerIsRunning = false;
                                    timerDisplay.textContent = "Time's Up!";
                                    showAlert("Timer finished!", "success");
                                    timerStartBtn.disabled = false;
                                    timerPauseBtn.disabled = true;
                                    [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false);
                                    try { new Audio('https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3').play(); } catch(e){} // Example sound
                                }
                            }, 1000);
                        };
                        timerPauseBtn.onclick = () => {
                            clearInterval(timerInterval);
                            timerIsRunning = false;
                            timerStartBtn.disabled = false;
                            timerPauseBtn.disabled = true;
                             [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false); // Re-enable inputs on pause
                        };
                        
                        function resetTimerState(showDefaultTime = true) {
                            clearInterval(timerInterval);
                            timerIsRunning = false;
                            if (showDefaultTime) {
                                timerHoursInput.value = '0';
                                timerMinutesInput.value = '5';
                                timerSecondsInput.value = '0';
                            }
                            setTimerFromInputs();
                            timerStartBtn.disabled = false;
                            timerPauseBtn.disabled = true;
                            [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false);
                            hideAlert();
                            timerDisplay.textContent = "00:05:00"; // Reset visual
                            if (showDefaultTime) setTimerFromInputs(); // Recalculate timerTotalSeconds
                        }
                        timerResetBtn.onclick = () => resetTimerState(true);
                        


                        // Stopwatch Logic
                        const stopwatchDisplay = container.querySelector('#stopwatchDisplay');
                        const stopwatchStartBtn = container.querySelector('#stopwatchStart');
                        const stopwatchStopBtn = container.querySelector('#stopwatchStop');
                        const stopwatchResetBtn = container.querySelector('#stopwatchReset');
                        const stopwatchLapBtn = container.querySelector('#stopwatchLap');
                        const lapsList = container.querySelector('#lapsList');
                        let stopwatchInterval;
                        let stopwatchStartTime;
                        let stopwatchElapsedTime = 0;
                        let lapNumber = 1;
                        let stopwatchIsRunning = false;


                        function formatStopwatchTime(ms) {
                            const totalSeconds = Math.floor(ms / 1000);
                            const minutes = Math.floor(totalSeconds / 60);
                            const seconds = totalSeconds % 60;
                            const milliseconds = Math.floor((ms % 1000)/10);
                            return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(2, '0')}`;
                        }


                        stopwatchStartBtn.onclick = () => {
                            if (stopwatchIsRunning) return;
                            stopwatchIsRunning = true;
                            stopwatchStartTime = Date.now() - stopwatchElapsedTime;
                            stopwatchInterval = setInterval(() => {
                                stopwatchElapsedTime = Date.now() - stopwatchStartTime;
                                stopwatchDisplay.textContent = formatStopwatchTime(stopwatchElapsedTime);
                            }, 10);
                            stopwatchStartBtn.disabled = true;
                            stopwatchStopBtn.disabled = false;
                            stopwatchLapBtn.disabled = false;
                            stopwatchResetBtn.disabled = false; // Enable reset when running or paused
                        };
                        stopwatchStopBtn.onclick = () => {
                            clearInterval(stopwatchInterval);
                            stopwatchIsRunning = false;
                            stopwatchStartBtn.disabled = false;
                            stopwatchStopBtn.disabled = true;
                            // Lap button can be active if paused with time
                            stopwatchLapBtn.disabled = stopwatchElapsedTime === 0;
                        };
                        function resetStopwatchState(fromTabSwitch = false) {
                            clearInterval(stopwatchInterval);
                            stopwatchIsRunning = false;
                            stopwatchElapsedTime = 0;
                            lapNumber = 1;
                            stopwatchDisplay.textContent = formatStopwatchTime(0);
                            if (!fromTabSwitch) lapsList.innerHTML = ''; // Clear laps only on explicit reset
                            stopwatchStartBtn.disabled = false;
                            stopwatchStopBtn.disabled = true;
                            stopwatchLapBtn.disabled = true;
                            stopwatchResetBtn.disabled = true; // Disabled when at 00:00
                        }
                        stopwatchResetBtn.onclick = () => resetStopwatchState(false);
                        
                        stopwatchLapBtn.onclick = () => {
                            if (stopwatchElapsedTime > 0) {
                                const lapTime = stopwatchElapsedTime;
                                const li = document.createElement('li');
                                li.textContent = `Lap ${lapNumber++}: ${formatStopwatchTime(lapTime)}`;
                                lapsList.prepend(li);
                            }
                        };
                        
                        resetTimerState(true); // Initial call for timer
                        resetStopwatchState(false); // Initial call for stopwatch


                        window.currentToolCleanup = () => {
                            clearInterval(timerInterval);
                            clearInterval(stopwatchInterval);
                        };
                    }                    
                };


                // Initial call for the default active tab (Timer) in Timer/Stopwatch if it's the first tool
                // This is more robustly handled by the tab switching logic now.
                // Ensure timer tab is default and its UI is correctly initialized
                const defaultTimerTabButton = document.querySelector('.tab-button[data-tab="timerTab"]');
                if (defaultTimerTabButton) defaultTimerTabButton.click();




            });
        </script>
</body>
</html>">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <!-- SEO Meta Tags -->
        <title>One Page Tools - Your Ultimate Collection of Free Online Tools</title>
        <meta name="description" content="One Page Tools offers 20+ free, responsive, and easy-to-use online tools including image converters, calculators, text utilities, and more. All client-side, no uploads required for many tools!">
        <meta name="keywords" content="online tools, free tools, image converter, image compressor, image cropper, video converter, audio converter, audio trimmer, age calculator, emi calculator, sip calculator, qr code generator, password generator, word counter, base64, color picker, text to speech, speech to text, json formatter, unit converter, bmi calculator, timer, stopwatch, multi tool, utility website">
        <meta name="author" content="One Page Tools">
        <link rel="canonical" href="YOUR_CANONICAL_URL_HERE"> <!-- Replace with your actual domain -->


        <!-- Open Graph Meta Tags (for Facebook, LinkedIn, etc.) -->
        <meta property="og:title" content="One Page Tools - Free Online Utilities">
        <meta property="og:description" content="Access a suite of 20 powerful online tools for images, audio, video, calculations, and text processing. Fast, free, and secure.">
        <meta property="og:type" content="website">
        <meta property="og:url" content="YOUR_CANONICAL_URL_HERE"> <!-- Replace with your actual domain -->
        <meta property="og:image" content="YOUR_OG_IMAGE_URL_HERE"> <!-- URL to an image representing your site (e.g., logo, screenshot) -->
        <meta property="og:image:width" content="1200">
        <meta property="og:image:height" content="630">
        <meta property="og:site_name" content="One Page Tools">


        <!-- Twitter Card Meta Tags -->
        <meta name="twitter:card" content="summary_large_image">
        <meta name="twitter:title" content="One Page Tools - 20+ Free Online Tools">
        <meta name="twitter:description" content="Your one-stop hub for versatile online tools: converters, calculators, generators, and more. Fully responsive and client-side.">
        <meta name="twitter:image" content="YOUR_TWITTER_IMAGE_URL_HERE"> <!-- URL to an image for Twitter card -->
        <!-- <meta name="twitter:site" content="@YourTwitterHandle"> --> <!-- Optional: Your Twitter handle -->


        <!-- Favicon (replace with your actual favicon links) -->
        <link rel="icon" href="/favicon.ico" sizes="any">
        <link rel="icon" href="/favicon.svg" type="image/svg+xml">
        <link rel="apple-touch-icon" href="/apple-touch-icon.png">




        <!-- Schema.org Markup (JSON-LD) -->
        <script type="application/ld+json">
        {
          "@context": "https://schema.org",
          "@type": "WebSite",
          "name": "One Page Tools",
          "url": "YOUR_CANONICAL_URL_HERE", // Replace
          "description": "A comprehensive collection of free online tools for various tasks including image manipulation, calculations, text utilities, and media conversion. All tools are client-side and designed for ease of use.",
          "potentialAction": {
            "@type": "SearchAction",
            "target": "YOUR_CANONICAL_URL_HERE#search?q={search_term_string}", // Replace if you implement site search
            "query-input": "required name=search_term_string"
          },
          "publisher": {
            "@type": "Organization",
            "name": "One Page Tools",
            "logo": {
              "@type": "ImageObject",
              "url": "YOUR_LOGO_URL_HERE", // Replace
              "width": 600, // Adjust
              "height": 60 // Adjust
            }
          },
          "mainEntity": {
            "@type": "ItemList",
            "name": "Available Tools",
            "description": "List of tools available on One Page Tools.",
            "itemListElement": [
              // Tools will be listed here. I'll add a few examples.
              // In a full implementation, you'd list all 20.
              { "@type": "ListItem", "position": 1, "item": { "@type": "Service", "name": "Image Converter", "description": "Convert images between JPG, PNG, and WEBP formats." } },
              { "@type": "ListItem", "position": 2, "item": { "@type": "Service", "name": "Age Calculator", "description": "Calculate age in years, months, and days from a birth date." } },
              { "@type": "ListItem", "position": 3, "item": { "@type": "Service", "name": "QR Code Generator", "description": "Generate QR codes from text or URLs." } }
              // ... (add all 20 tools here similarly)
            ]
          }
        }
        </script>
        <!-- Example HowTo Schema for one tool (you'd ideally create one for each) -->
        <script type="application/ld+json">
        {
          "@context": "https://schema.org",
          "@type": "HowTo",
          "name": "How to use the Image Converter on One Page Tools",
          "description": "A step-by-step guide to convert image formats (JPG, PNG, WEBP) using the One Page Tools's Image Converter.",
          "step": [
            {
              "@type": "HowToStep",
              "name": "Open Tool",
              "text": "Navigate to the One Page Tools and click on the 'Image Converter' tool card.",
              "url": "YOUR_CANONICAL_URL_HERE#imageConverter" // Link to the tool if possible
            },
            {
              "@type": "HowToStep",
              "name": "Upload Image",
              "text": "Click the 'Choose File' button and select the image you want to convert from your device."
            },
            {
              "@type": "HowToStep",
              "name": "Select Output Format",
              "text": "Choose your desired output format (JPG, PNG, or WEBP) from the dropdown menu."
            },
            {
              "@type": "HowToStep",
              "name": "Convert and Download",
              "text": "Click the 'Convert & Download' button. The converted image will be processed and downloaded automatically."
            }
          ],
          "estimatedCost": {
            "@type": "MonetaryAmount",
            "currency": "USD",
            "value": "0"
          },
          "supply": [
            { "@type": "HowToSupply", "name": "A digital image file (JPG, PNG, or initial WEBP)" },
            { "@type": "HowToSupply", "name": "A web browser" }
          ],
          "tool": [
            { "@type": "HowToTool", "name": "Web browser with JavaScript enabled" },
            { "@type": "HowToTool", "name": "One Page Tools Image Converter" }
          ],
          "totalTime": "PT1M" // Estimated time: 1 minute
        }
        </script>
    
        <!-- Placeholder for Analytics (e.g., Google Analytics) -->
        <!--
        <script async src="https://www.googletagmanager.com/gtag/js?id=YOUR_GA_TRACKING_ID"></script>
        <script>
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', 'YOUR_GA_TRACKING_ID');
        </script>
        -->


        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">


        <style>
            :root {
                --background-color: #1E1E2F;
                --text-color: #EAEAEA;
                --header-background: #2B2D42;
                --accent-color: #FFD700;
                --tool-card-background: #3A3D5B;
                --button-hover-color: #E6C200;
                --soft-box-shadow: 0 8px 16px rgba(255, 215, 0, 0.1);
                --card-hover-box-shadow: 0 12px 24px rgba(255, 215, 0, 0.2);
                --font-family: 'Poppins', sans-serif;
                --ad-placeholder-bg: #4a4e69;
                --ad-placeholder-border: #6c757d;
            }


            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }


            body {
                font-family: var(--font-family);
                background-color: var(--background-color);
                color: var(--text-color);
                line-height: 1.6;
                padding-bottom: 50px;
            }


            header {
                background-color: var(--header-background);
                color: var(--accent-color);
                padding: 1.5rem 1rem;
                text-align: center;
                box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
                margin-bottom: 1rem; /* Reduced margin for ad space */
            }


            header h1 {
                font-weight: 700;
                font-size: 2.5rem;
            }


            .ad-placeholder {
                background-color: var(--ad-placeholder-bg);
                border: 1px dashed var(--ad-placeholder-border);
                color: var(--text-color);
                text-align: center;
                padding: 1rem;
                margin: 1rem auto; /* Centered */
                max-width: 728px; /* Common ad width */
                min-height: 90px; /* Common ad height */
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 0.9rem;
                border-radius: 5px;
            }
            .ad-placeholder-sidebar { /* Example for a different type of ad */
                max-width: 300px;
                min-height: 250px;
            }




            main {
                max-width: 1200px;
                margin: 0 auto;
                padding: 0 1rem;
            }


            .tool-grid {
                display: grid;
                grid-template-columns: repeat(3, 1fr);
                gap: 1.5rem;
            }


            .tool-card {
                background-color: var(--tool-card-background);
                padding: 1.5rem;
                border-radius: 10px;
                box-shadow: var(--soft-box-shadow);
                transition: transform 0.3s ease, background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                opacity: 0; /* For fade-in animation */
                transform: translateY(20px); /* For fade-in animation */
                animation: fadeInUp 0.5s ease-out forwards;
            }


            .tool-card:hover {
                transform: translateY(-5px);
                background-color: var(--accent-color);
                color: var(--background-color);
                box-shadow: var(--card-hover-box-shadow);
            }


            .tool-card h2 {
                font-size: 1.5rem;
                margin-bottom: 0.75rem;
                color: var(--text-color);
                transition: color 0.3s ease;
            }


            .tool-card:hover h2 {
                color: var(--background-color);
            }


            .tool-card p {
                font-size: 0.9rem;
                margin-bottom: 1rem;
                flex-grow: 1;
                opacity: 0.9;
            }


            .tool-card:hover p {
                color: var(--background-color);
                opacity: 1;
            }


            .tool-button {
                background-color: var(--accent-color);
                color: var(--background-color);
                border: none;
                padding: 0.75rem 1.25rem;
                border-radius: 5px;
                font-weight: bold;
                cursor: pointer;
                text-align: center;
                transition: background-color 0.3s ease, color 0.3s ease;
                align-self: flex-start;
            }


            .tool-card:hover .tool-button {
                background-color: var(--background-color);
                color: var(--accent-color);
            }


            .tool-button:hover {
                background-color: var(--button-hover-color) !important;
                color: var(--background-color) !important;
            }


            .modal {
                display: none;
                position: fixed;
                z-index: 1000;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                overflow: auto;
                background-color: rgba(0, 0, 0, 0.6);
                animation: fadeIn 0.3s ease-in-out;
            }


            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }


            .modal-content {
                background-color: var(--header-background);
                margin: 5% auto;
                padding: 25px;
                border-radius: 10px;
                width: 90%;
                max-width: 700px;
                box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
                position: relative;
                animation: slideIn 0.3s ease-in-out;
            }
            
            /* Modal Ad Placeholder - example, might be too intrusive */
            /* .modal-ad-placeholder { margin-top: 1rem; } */




            @keyframes slideIn {
                from { transform: translateY(-50px); opacity: 0; }
                to { transform: translateY(0); opacity: 1; }
            }


            .close-button {
                color: var(--text-color);
                float: right;
                font-size: 28px;
                font-weight: bold;
                transition: color 0.2s ease;
            }


            .close-button:hover,
            .close-button:focus {
                color: var(--accent-color);
                text-decoration: none;
                cursor: pointer;
            }


            .modal-body {
                margin-top: 1.5rem;
                color: var(--text-color);
            }


            .modal-body h3 {
                color: var(--accent-color);
                margin-bottom: 1rem;
            }


            .modal-body label {
                display: block;
                margin-bottom: 0.5rem;
                font-weight: 600;
            }


            .modal-body input[type="text"],
            .modal-body input[type="number"],
            .modal-body input[type="date"],
            .modal-body input[type="file"],
            .modal-body textarea,
            .modal-body select {
                width: 100%;
                padding: 0.75rem;
                margin-bottom: 1rem;
                border-radius: 5px;
                border: 1px solid var(--tool-card-background);
                background-color: var(--background-color);
                color: var(--text-color);
                font-family: var(--font-family);
                font-size: 1rem;
            }


            .modal-body input[type="file"] { padding: 0.5rem; }
            .modal-body textarea { min-height: 100px; resize: vertical; }


            .modal-body button, .tool-interface button {
                background-color: var(--accent-color);
                color: var(--background-color);
                border: none;
                padding: 0.8rem 1.5rem;
                border-radius: 5px;
                font-weight: bold;
                cursor: pointer;
                transition: background-color 0.3s ease;
                margin-top: 0.5rem;
                margin-right: 0.5rem;
            }


            .modal-body button:hover, .tool-interface button:hover {
                background-color: var(--button-hover-color);
            }


            .modal-body .result-area, .tool-interface .result-area {
                margin-top: 1rem;
                padding: 1rem;
                background-color: var(--background-color);
                border-radius: 5px;
                border: 1px solid var(--tool-card-background);
                white-space: pre-wrap;
                word-wrap: break-word;
            }


            .modal-body .option-group {
                margin-bottom: 1rem;
                padding: 0.75rem;
                border: 1px solid var(--tool-card-background);
                border-radius: 5px;
            }
            .modal-body .option-group label { display: inline-block; margin-right: 10px; }


            .modal-alert {
                margin-top: 1rem;
                padding: 0.75rem;
                border-radius: 5px;
                font-weight: bold;
            }
            .modal-alert.success { background-color: #28a745; color: white; }
            .modal-alert.error { background-color: #dc3545; color: white; }
            .modal-alert.info { background-color: #17a2b8; color: white; }


            #qrCodeContainer canvas {
                display: block;
                margin: 1rem auto;
                border: 5px solid var(--accent-color);
            }


            #imagePreview, #croppedImagePreview, #imagePreviewComp {
                max-width: 100%;
                max-height: 300px;
                margin-top: 1rem;
                border: 1px solid var(--accent-color);
                background-color: var(--background-color); /* Ensure preview area has background */
            }
            
            #cropCanvas { /* Style for the cropper canvas */
                border:1px solid var(--accent-color);
                max-width:100%;
                display:block; /* Important for canvas */
                background-color: var(--background-color);
                margin-top: 10px;
            }




            .color-picker-display { display: flex; align-items: center; margin-top: 1rem; }
            .color-picker-display .color-preview { width: 50px; height: 50px; border-radius: 5px; margin-right: 1rem; border: 1px solid var(--text-color); }
            .color-picker-display .color-values p { margin: 0.25rem 0; }


            .timer-display, .stopwatch-display { font-size: 2.5rem; font-weight: bold; text-align: center; margin-bottom: 1rem; color: var(--accent-color); }
            .laps-list { max-height: 150px; overflow-y: auto; border: 1px solid var(--tool-card-background); padding: 0.5rem; margin-top: 1rem; }
            .laps-list li { list-style: none; padding: 0.25rem 0; border-bottom: 1px dashed var(--tool-card-background); }
            .laps-list li:last-child { border-bottom: none; }
            
            .tabs { display:flex; margin-bottom:1rem; border-bottom:1px solid var(--tool-card-background); }
            .tab-button { background:none; border:none; color: var(--text-color); padding: 0.75rem 1rem; cursor:pointer; font-size: 1rem; font-family: var(--font-family); transition: background-color 0.3s, color 0.3s; }
            .tab-button.active { background-color: var(--accent-color); color: var(--background-color); border-bottom: 2px solid var(--accent-color); font-weight: bold; }
            .tab-button:not(.active):hover { background-color: var(--tool-card-background); }


            #toolsArticle {
                margin-top: 3rem;
                padding: 1.5rem;
                background-color: var(--tool-card-background);
                border-radius: 8px;
            }
            #toolsArticle h2 {
                color: var(--accent-color);
                margin-bottom: 1rem;
                text-align: center;
                font-size: 2rem;
            }
            #toolsArticle h3 {
                color: var(--accent-color);
                opacity: 0.9;
                margin-top: 1.5rem;
                margin-bottom: 0.5rem;
                border-bottom: 1px solid var(--header-background);
                padding-bottom: 0.3rem;
            }
            #toolsArticle p, #toolsArticle ul {
                margin-bottom: 1rem;
                font-size: 0.95rem;
                opacity: 0.9;
            }
            #toolsArticle li {
                margin-left: 1.5rem;
                margin-bottom: 0.5rem;
            }
             #toolsArticle strong { color: var(--accent-color); opacity: 1;}




            @keyframes fadeInUp {
                to { opacity: 1; transform: translateY(0); }
            }


            @media (max-width: 992px) { /* Tablet */
                .tool-grid { grid-template-columns: repeat(2, 1fr); }
                header h1 { font-size: 2rem; }
            }


            @media (max-width: 768px) { /* Mobile */
                .tool-grid { grid-template-columns: 1fr; }
                .modal-content { width: 95%; margin: 10% auto; padding: 20px; }
                header h1 { font-size: 1.8rem; }
                .tool-card h2 { font-size: 1.3rem; }
                .modal-body button, .tool-interface button { padding: 0.7rem 1.2rem; font-size: 0.9rem; }
                #toolsArticle h2 { font-size: 1.6rem; }
                #toolsArticle h3 { font-size: 1.3rem; }
            }
        </style>
</head>
<body>
        <header>
            <h1>One Page Tools</h1>
        </header>


        <!-- Ad Placeholder 1 (Below Header) -->
        <div class="ad-placeholder">
            Your Advertisement Here (e.g., 728x90 Leaderboard)
        </div>


        <main>
            <div class="tool-grid">
                <!-- Tool cards will be dynamically generated by JavaScript -->
            </div>
        </main>


        <!-- Generic Modal Structure -->
        <div id="toolModal" class="modal">
            <div class="modal-content">
                <span class="close-button">&times;</span>
                <h2 id="modalTitle">Tool Title</h2>
                <div id="modalBody" class="modal-body">
                    <!-- Tool-specific content will be injected here -->
                </div>
                 <!-- Optional Ad Placeholder inside Modal -->
                <!-- <div class="ad-placeholder modal-ad-placeholder">Ad inside modal (e.g., 300x250)</div> -->
                <div id="modalAlert" class="modal-alert" style="display:none;"></div>
            </div>
        </div>


        <!-- Article Section -->
        <div id="toolsArticleContainer" style="max-width: 1200px; margin: 2rem auto; padding: 0 1rem;">
            <article id="toolsArticle">
                <h2>Welcome to One Page Tools: Your Ultimate Online Toolkit</h2>
                <p>In today's fast-paced digital world, efficiency and convenience are paramount. Whether you're a student, a professional, a content creator, or simply someone who interacts with digital media and data regularly, having quick access to a versatile set of tools can be a game-changer. That's where <strong>One Page Tools</strong> comes in. We've curated a collection of 20 essential, free-to-use online tools designed to simplify your tasks, boost your productivity, and empower your digital endeavors – all from the comfort of your web browser, with no installations required and a strong focus on client-side processing for speed and privacy.</p>
                <p>Our platform is built with a modern, premium, and minimalistic design, ensuring a seamless and enjoyable user experience across all devices – desktop, tablet, and mobile. Each tool is crafted to be fully functional using only HTML, CSS, and Vanilla JavaScript, meaning they are lightweight, fast, and respect your privacy by processing data directly in your browser whenever possible.</p>
                
                <h3>Why Choose One Page Tools?</h3>
                <ul>
                    <li><strong>Completely Free:</strong> All 20 tools are available at no cost.</li>
                    <li><strong>Client-Side Processing:</strong> For many tools, especially those handling sensitive data or files, processing happens directly in your browser. This means your files often don't even need to be uploaded to a server, enhancing speed and privacy.</li>
                    <li><strong>No Registration Required:</strong> Jump right in and start using the tools without any sign-ups or logins.</li>
                    <li><strong>User-Friendly Interface:</strong> Our clean, minimalistic design makes it easy to find and use the tool you need.</li>
                    <li><strong>Fully Responsive:</strong> Access One Page Tools on any device, anytime, anywhere.</li>
                    <li><strong>Diverse Range of Utilities:</strong> From image manipulation and media conversion to financial calculators and text utilities, we've got you covered.</li>
                </ul>


                <h3>Explore Our Suite of 20 Powerful Tools:</h3>


                <section id="article-image-tools">
                    <h3>🎨 Image Manipulation Tools</h3>
                    <p>Perfect for designers, photographers, and anyone working with digital images.</p>
                    
                    <h4>1. Image Converter</h4>
                    <p><strong>What it does:</strong> Easily convert your images between popular formats like JPG, PNG, and WEBP. Whether you need a transparent background (PNG), a highly compressed web-friendly format (WEBP), or a standard photographic format (JPG), this tool handles it swiftly using browser-native canvas technology.</p>
                    <p><strong>Use cases:</strong> Preparing images for web use, changing formats for compatibility, optimizing image types for specific needs.</p>


                    <h4>2. Image Compressor</h4>
                    <p><strong>What it does:</strong> Reduce the file size of your images without significant loss of quality. This tool uses canvas and adjustable quality settings (for JPEGs) to make your images lighter, faster to load on websites, and easier to share.</p>
                    <p><strong>Use cases:</strong> Optimizing website images for faster loading times, reducing storage space, sending images via email or messaging apps with size limits.</p>


                    <h4>3. Image Cropper</h4>
                    <p><strong>What it does:</strong> Upload an image, define your desired crop area with a visual preview, and export the perfectly cropped section. Ideal for focusing on specific parts of an image or adjusting aspect ratios.</p>
                    <p><strong>Use cases:</strong> Creating profile pictures, framing subjects, removing unwanted parts of an image, preparing images for specific layouts.</p>
                </section>


                <section id="article-media-tools">
                    <h3>🎬 Audio & Video Tools</h3>
                    <p>For content creators, students, or anyone needing to tweak audio and video files.</p>


                    <h4>4. Video Converter (Browser-Limited)</h4>
                    <p><strong>What it does:</strong> Convert video formats (e.g., MP4 to WebM or vice-versa) by re-encoding the video directly in your browser using MediaRecorder or canvas capabilities. Functionality and output quality depend on your browser's support for these technologies.</p>
                    <p><strong>Use cases:</strong> Making videos compatible with specific platforms or devices, converting to web-friendly formats. (Note: Browser capabilities are key here).</p>


                    <h4>5. Audio Converter (to WAV)</h4>
                    <p><strong>What it does:</strong> Convert various audio files that your browser can play into the versatile WAV format. This tool utilizes the Web Audio API for decoding and processing.</p>
                    <p><strong>Use cases:</strong> Preparing audio for editing software that prefers WAV, standardizing audio formats, creating uncompressed audio clips.</p>


                    <h4>6. Audio Trimmer</h4>
                    <p><strong>What it does:</strong> Upload an audio file, specify start and end times, and export the trimmed segment as a new WAV clip. Perfect for extracting soundbites, creating ringtones, or shortening audio recordings.</p>
                    <p><strong>Use cases:</strong> Editing podcasts or interviews, creating short audio samples, removing unwanted sections from recordings.</p>
                </section>


                <section id="article-calculator-tools">
                    <h3>🧮 Productivity & Financial Calculators</h3>
                    <p>Essential tools for everyday calculations, financial planning, and health monitoring.</p>


                    <h4>7. Age Calculator</h4>
                    <p><strong>What it does:</strong> Simply input a date of birth, and this tool will instantly calculate the age in years, months, and days. Accurate and quick.</p>
                    <p><strong>Use cases:</strong> Finding exact age for forms or records, curiosity, event planning based on age milestones.</p>


                    <h4>8. EMI Calculator</h4>
                    <p><strong>What it does:</strong> Plan your loans effectively. Input the loan amount, annual interest rate, and loan duration (in months) to calculate your Equated Monthly Installment (EMI), total interest payable, and total payment.</p>
                    <p><strong>Use cases:</strong> Financial planning for home loans, car loans, personal loans; understanding loan affordability.</p>


                    <h4>9. SIP Calculator</h4>
                    <p><strong>What it does:</strong> Estimate the future value of your Systematic Investment Plan (SIP) investments. Enter your monthly investment amount, expected annual interest rate, and investment duration (in years) to see potential returns.</p>
                    <p><strong>Use cases:</strong> Mutual fund investment planning, retirement planning, goal-based savings projections.</p>


                    <h4>10. BMI Calculator</h4>
                    <p><strong>What it does:</strong> Calculate your Body Mass Index (BMI) by entering your weight (in kg) and height (in cm). The tool also provides your BMI category (e.g., Underweight, Normal, Overweight).</p>
                    <p><strong>Use cases:</strong> Health and fitness tracking, understanding weight status, a general indicator for health assessment (consult a professional for medical advice).</p>
                </section>


                <section id="article-text-code-tools">
                    <h3>✒️ Text & Code Utilities</h3>
                    <p>A must-have for writers, developers, and anyone dealing with text or data.</p>


                    <h4>11. QR Code Generator</h4>
                    <p><strong>What it does:</strong> Enter any text, URL, contact information, or Wi-Fi credentials, and generate a downloadable QR code image instantly. Uses canvas for generation.</p>
                    <p><strong>Use cases:</strong> Sharing website links, contact details, Wi-Fi access easily; marketing materials, event ticketing.</p>


                    <h4>12. Password Generator</h4>
                    <p><strong>What it does:</strong> Create strong, secure, and random passwords. Customize the length and include/exclude uppercase letters, lowercase letters, numbers, and symbols to meet various security requirements.</p>
                    <p><strong>Use cases:</strong> Enhancing online security, creating unique passwords for different accounts, adhering to password complexity rules.</p>


                    <h4>13. Word Counter</h4>
                    <p><strong>What it does:</strong> A real-time text analysis tool. As you type or paste text, it counts words, characters (with and without spaces), spaces, and even estimates reading time. Essential for writers, students, and social media managers.</p>
                    <p><strong>Use cases:</strong> Meeting word count requirements for essays or articles, optimizing social media posts, tracking writing progress.</p>


                    <h4>14. Base64 Encoder/Decoder</h4>
                    <p><strong>What it does:</strong> Easily convert plain text to Base64 encoding or decode Base64 strings back to their original plain text form. Useful for data transmission or simple obfuscation.</p>
                    <p><strong>Use cases:</strong> Encoding data for URLs or HTML, decoding data from various web sources, simple data protection (not encryption).</p>


                    <h4>15. JSON Formatter</h4>
                    <p><strong>What it does:</strong> Paste your JSON data, and this tool will auto-format (pretty-print) it with proper indentation and syntax highlighting. It also helps validate the JSON structure and points out errors.</p>
                    <p><strong>Use cases:</strong> Debugging JSON APIs, making complex JSON data readable, validating JSON structures before use in applications.</p>
                </section>
                
                <section id="article-accessibility-interaction-tools">
                    <h3>🗣️ Accessibility & Interactive Tools</h3>
                    <p>Enhance accessibility and engage with content in new ways.</p>


                    <h4>16. Color Picker Tool</h4>
                    <p><strong>What it does:</strong> Visually pick a color using a standard color input, or enter a color code. The tool displays its HEX, RGB, and HSL values, making it easy to work with colors for web design, graphic design, or any digital project.</p>
                    <p><strong>Use cases:</strong> Web development, graphic design, digital art, matching colors, understanding color codes.</p>


                    <h4>17. Text to Speech</h4>
                    <p><strong>What it does:</strong> Enter any text, and listen to it being read aloud by your browser's built-in speech synthesis engine. You can often choose from different voices, and adjust the rate and pitch.</p>
                    <p><strong>Use cases:</strong> Accessibility for visually impaired users, proofreading text, learning pronunciation, multitasking by listening to content.</p>


                    <h4>18. Speech to Text</h4>
                    <p><strong>What it does:</strong> Use your microphone to convert your spoken words into text. This tool leverages your browser's Web Speech API for real-time voice recognition.</p>
                    <p><strong>Use cases:</strong> Dictating notes, writing emails hands-free, accessibility for users with motor impairments, transcribing short audio clips.</p>
                </section>


                <section id="article-measurement-time-tools">
                    <h3>📏 Measurement & Time Management Tools</h3>
                    <p>Practical utilities for conversions and keeping track of time.</p>


                    <h4>19. Unit Converter</h4>
                    <p><strong>What it does:</strong> A versatile converter for various units of measurement, including length (meters, feet, miles, etc.), weight (kilograms, pounds, ounces, etc.), and temperature (Celsius, Fahrenheit, Kelvin). Simple and intuitive.</p>
                    <p><strong>Use cases:</strong> Converting recipes, scientific calculations, travel planning, everyday measurement conversions.</p>


                    <h4>20. Timer / Stopwatch Tool</h4>
                    <p><strong>What it does:</strong> A dual-function tool. Set a countdown timer for tasks or use the stopwatch with lap functionality to measure elapsed time accurately. Clean interface and easy controls.</p>
                    <p><strong>Use cases:</strong> Time management (Pomodoro technique), cooking, workouts, tracking time for activities, conducting experiments.</p>
                </section>


                <h3>Privacy and Security at the Forefront</h3>
                <p>We understand the importance of your data. That's why One Page Tools is designed with a "privacy-first" approach. Many of our tools, particularly those that handle files (like image and audio converters/compressors/croppers), perform all operations directly within your browser. This means your files are not uploaded to our servers, offering you enhanced security and speed. For tools that require input (like calculators or text utilities), the data is processed client-side and is not stored or tracked beyond what's necessary for the tool's immediate function or basic anonymous usage analytics (if enabled by you).</p>


                <h3>Constantly Evolving</h3>
                <p>One Page Tools is a living project. We are committed to maintaining and improving our existing tools and potentially adding new ones based on user needs and technological advancements. Our goal is to be your go-to destination for quick, reliable, and free online utilities.</p>
                <p>We invite you to explore the One Page Tools and discover how our suite of tools can simplify your digital life. Bookmark us and share with friends and colleagues who might benefit from these handy utilities!</p>
            </article>
        </div>


        <!-- Ad Placeholder 2 (Before Footer/End of Page) -->
        <div class="ad-placeholder">
            Your Advertisement Here (e.g., 728x90 or 300x250)
        </div>




        <script>
            // --- QR Code Generation Library (Minimal Placeholder - qrcode.js by davidshimjs is a good option to embed) ---
            // For a fully functional QR code, you would embed a library like qrcode.js here.
            // Example structure if qrcode.js was embedded:
            /*
            var QRCode; (function(){ // QRCode Kapselung ... entire qrcode.js library code ... QRCode=t})()
            */
            // Since embedding a full library makes this example overly long, the QR tool uses a simplified canvas drawing.
            // A real implementation should use a proper QR library for compliant codes.
            // For demonstration, if you had a qrcode.js file, you'd paste its contents here.
            // For now, the script will check for a 'QRCode' object and use a fallback if not found.




            document.addEventListener('DOMContentLoaded', () => {
                const toolGrid = document.querySelector('.tool-grid');
                const modal = document.getElementById('toolModal');
                const modalTitle = document.getElementById('modalTitle');
                const modalBody = document.getElementById('modalBody');
                const modalAlert = document.getElementById('modalAlert');
                const closeButton = document.querySelector('.close-button');


                // --- Basic Browser-Native Analytics (Tool Usage Logger) ---
                function logToolUsage(toolId) {
                    try {
                        let usageData = JSON.parse(localStorage.getItem('multiToolHubUsage')) || [];
                        usageData.push({ tool: toolId, timestamp: new Date().toISOString() });
                        // Optional: Limit the size of usageData to prevent localStorage overflow
                        if (usageData.length > 1000) { // Keep last 1000 entries
                            usageData = usageData.slice(usageData.length - 1000);
                        }
                        localStorage.setItem('multiToolHubUsage', JSON.stringify(usageData));
                    } catch (e) {
                        console.error("Error logging tool usage:", e);
                    }
                }
                // Example: To see logged data, open browser console and type:
                // JSON.parse(localStorage.getItem('multiToolHubUsage'))




                const tools = [
                    { id: 'imageConverter', title: 'Image Converter', description: 'Convert between JPG, PNG, and WEBP formats.' },
                    { id: 'imageCompressor', title: 'Image Compressor', description: 'Compress image file size with quality settings.' },
                    { id: 'imageCropper', title: 'Image Cropper', description: 'Upload, crop image with preview, and export.' },
                    { id: 'videoConverter', title: 'Video Converter', description: 'Convert video (MP4 ↔ WebM via re-encoding). Browser-limited.' },
                    { id: 'audioConverter', title: 'Audio Converter', description: 'Convert uploaded audio to WAV format.' },
                    { id: 'audioTrimmer', title: 'Audio Trimmer', description: 'Upload, trim audio, and export trimmed WAV clip.' },
                    { id: 'ageCalculator', title: 'Age Calculator', description: 'Calculate age from date of birth.' },
                    { id: 'emiCalculator', title: 'EMI Calculator', description: 'Calculate Equated Monthly Installment for loans.' },
                    { id: 'sipCalculator', title: 'SIP Calculator', description: 'Calculate future value of SIP investments.' },
                    { id: 'qrCodeGenerator', title: 'QR Code Generator', description: 'Generate downloadable QR codes from text/URL.' },
                    { id: 'passwordGenerator', title: 'Password Generator', description: 'Generate secure passwords with custom options.' },
                    { id: 'wordCounter', title: 'Word Counter', description: 'Count words, characters, spaces, and estimate reading time.' },
                    { id: 'base64EncoderDecoder', title: 'Base64 Encoder/Decoder', description: 'Encode to Base64 or decode from Base64.' },
                    { id: 'colorPicker', title: 'Color Picker Tool', description: 'Pick colors and get HEX, RGB, HSL values.' },
                    { id: 'textToSpeech', title: 'Text to Speech', description: 'Convert text into spoken audio using browser voices.' },
                    { id: 'speechToText', title: 'Speech to Text', description: 'Convert voice from microphone into text.' },
                    { id: 'jsonFormatter', title: 'JSON Formatter', description: 'Format and validate JSON data.' },
                    { id: 'unitConverter', title: 'Unit Converter', description: 'Convert values between various units (length, weight, temp).' },
                    { id: 'bmiCalculator', title: 'BMI Calculator', description: 'Calculate Body Mass Index and category.' },
                    { id: 'timerStopwatch', title: 'Timer / Stopwatch', description: 'Simple timer and stopwatch functionality.' },
                ];


                function openModal(tool) {
                    modalTitle.textContent = tool.title;
                    modalBody.innerHTML = '';
                    hideAlert();
                    
                    const toolFunction = toolInitializers[tool.id];
                    if (toolFunction) {
                        toolFunction(modalBody);
                        logToolUsage(tool.id); // Log tool opening
                    } else {
                        modalBody.innerHTML = '<p>Tool UI not implemented yet.</p>';
                    }
                    modal.style.display = 'block';
                }


                function closeModal() {
                    modal.style.display = 'none';
                    modalBody.innerHTML = '';
                    if (window.currentToolCleanup) {
                        window.currentToolCleanup();
                        window.currentToolCleanup = null;
                    }
                }


                closeButton.onclick = closeModal;
                window.onclick = (event) => {
                    if (event.target === modal) {
                        closeModal();
                    }
                };
                
                function showAlert(message, type = 'info') {
                    modalAlert.textContent = message;
                    modalAlert.className = `modal-alert ${type}`;
                    modalAlert.style.display = 'block';
                }


                function hideAlert() {
                    modalAlert.style.display = 'none';
                }


                tools.forEach((tool, index) => {
                    const card = document.createElement('div');
                    card.className = 'tool-card';
                    card.style.animationDelay = `${index * 0.05}s`;
                    card.innerHTML = `
                        <h2>${tool.title}</h2>
                        <p>${tool.description}</p>
                        <button class="tool-button" data-toolid="${tool.id}">Open Tool</button>
                    `;
                    card.querySelector('.tool-button').addEventListener('click', () => openModal(tool));
                    toolGrid.appendChild(card);
                });


                const toolInitializers = {
                    imageConverter: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgConvFile" accept="image/jpeg,image/png,image/webp">
                            <label for="imgConvFormat">Convert to:</label>
                            <select id="imgConvFormat">
                                <option value="image/jpeg">JPEG</option>
                                <option value="image/png">PNG</option>
                                <option value="image/webp">WEBP</option>
                            </select>
                            <button id="imgConvButton">Convert & Download</button>
                            <p>Note: WEBP support varies by browser.</p>
                            <img id="imagePreview" src="#" alt="Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgConvFile');
                        const formatSelect = container.querySelector('#imgConvFormat');
                        const convertButton = container.querySelector('#imgConvButton');
                        const preview = container.querySelector('#imagePreview');
                        let originalFileName = 'converted_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    preview.src = event.target.result;
                                    preview.style.display = 'block';
                                }
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                 preview.style.display = 'none';
                                 preview.src="#";
                            }
                        };


                        convertButton.onclick = () => {
                            if (!fileInput.files || fileInput.files.length === 0) {
                                showAlert('Please select an image file first.', 'error');
                                return;
                            }
                            const file = fileInput.files[0];
                            const targetFormat = formatSelect.value;
                            const targetExtension = targetFormat.split('/')[1];


                            showAlert('Processing...', 'info');


                            const reader = new FileReader();
                            reader.onload = (event) => {
                                const img = new Image();
                                img.onload = () => {
                                    const canvas = document.createElement('canvas');
                                    canvas.width = img.width;
                                    canvas.height = img.height;
                                    const ctx = canvas.getContext('2d');
                                    ctx.drawImage(img, 0, 0);
                                    
                                    canvas.toBlob((blob) => {
                                        if (blob) {
                                            const url = URL.createObjectURL(blob);
                                            const a = document.createElement('a');
                                            a.href = url;
                                            a.download = `${originalFileName}_converted.${targetExtension}`;
                                            document.body.appendChild(a);
                                            a.click();
                                            document.body.removeChild(a);
                                            URL.revokeObjectURL(url);
                                            showAlert('Conversion successful!', 'success');
                                        } else {
                                            showAlert(`Error converting to ${targetFormat}. This format might not be supported for export by your browser.`, 'error');
                                        }
                                    }, targetFormat, 0.9);
                                };
                                img.onerror = () => showAlert('Could not load image. Ensure it is a valid JPG, PNG, or WEBP.', 'error');
                                img.src = event.target.result;
                            };
                            reader.readAsDataURL(file);
                        };
                    },


                    imageCompressor: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgCompFile" accept="image/jpeg,image/png">
                            <label for="imgCompQuality">Quality (0.1 - 1.0 for JPEG, ignored for PNG):</label>
                            <input type="number" id="imgCompQuality" value="0.7" min="0.1" max="1.0" step="0.1">
                            <button id="imgCompButton">Compress & Download</button>
                            <p>PNG compression is lossless and quality setting is ignored.</p>
                            <div id="compressionInfo" class="result-area" style="display:none;"></div>
                            <img id="imagePreviewComp" src="#" alt="Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgCompFile');
                        const qualityInput = container.querySelector('#imgCompQuality');
                        const compressButton = container.querySelector('#imgCompButton');
                        const compressionInfo = container.querySelector('#compressionInfo');
                        const preview = container.querySelector('#imagePreviewComp');
                        let originalFileName = 'compressed_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    preview.src = event.target.result;
                                    preview.style.display = 'block';
                                }
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                preview.style.display = 'none';
                                preview.src = "#";
                            }
                        };


                        compressButton.onclick = () => {
                            if (!fileInput.files || fileInput.files.length === 0) {
                                showAlert('Please select an image file.', 'error');
                                return;
                            }
                            const file = fileInput.files[0];
                            const quality = parseFloat(qualityInput.value);
                            const originalSize = (file.size / 1024).toFixed(2);
                            showAlert('Processing...', 'info');


                            const reader = new FileReader();
                            reader.onload = (event) => {
                                const img = new Image();
                                img.onload = () => {
                                    const canvas = document.createElement('canvas');
                                    const ctx = canvas.getContext('2d');
                                    canvas.width = img.width;
                                    canvas.height = img.height;
                                    ctx.drawImage(img, 0, 0);


                                    let outputFormat = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
                                    let extension = outputFormat.split('/')[1];


                                    canvas.toBlob((blob) => {
                                        if (blob) {
                                            const compressedSize = (blob.size / 1024).toFixed(2);
                                            const url = URL.createObjectURL(blob);
                                            const a = document.createElement('a');
                                            a.href = url;
                                            a.download = `${originalFileName}_compressed.${extension}`;
                                            document.body.appendChild(a);
                                            a.click();
                                            document.body.removeChild(a);
                                            URL.revokeObjectURL(url);
                                            
                                            compressionInfo.innerHTML = `Original Size: ${originalSize} KB<br>Compressed Size: ${compressedSize} KB<br>Reduction: ${((1 - compressedSize / originalSize) * 100).toFixed(2)}%`;
                                            compressionInfo.style.display = 'block';
                                            showAlert('Compression successful!', 'success');
                                        } else {
                                            showAlert('Error during compression.', 'error');
                                        }
                                    }, outputFormat, outputFormat === 'image/jpeg' ? quality : undefined);
                                };
                                img.onerror = () => showAlert('Could not load image.', 'error');
                                img.src = event.target.result;
                            };
                            reader.readAsDataURL(file);
                        };
                    },


                    imageCropper: (container) => {
                        container.innerHTML = `
                            <input type="file" id="imgCropFile" accept="image/*">
                            <p>After selecting an image, adjust crop parameters below. A real cropper would have a draggable overlay.</p>
                            <canvas id="cropCanvas" style="display:none;"></canvas>
                            <br>
                            <label for="cropX">Crop Start X (px):</label> <input type="number" id="cropX" value="0" style="width:80px;">
                            <label for="cropY">Crop Start Y (px):</label> <input type="number" id="cropY" value="0" style="width:80px;">
                            <br>
                            <label for="cropW">Crop Width (px):</label> <input type="number" id="cropW" value="100" style="width:80px;">
                            <label for="cropH">Crop Height (px):</label> <input type="number" id="cropH" value="100" style="width:80px;">
                            <br>
                            <button id="cropButton">Crop & Download</button>
                            <img id="croppedImagePreview" src="#" alt="Cropped Preview" style="display:none;">
                        `;
                        const fileInput = container.querySelector('#imgCropFile');
                        const cropCanvas = container.querySelector('#cropCanvas'); // This is the preview canvas
                        const ctx = cropCanvas.getContext('2d');
                        const cropXInput = container.querySelector('#cropX');
                        const cropYInput = container.querySelector('#cropY');
                        const cropWInput = container.querySelector('#cropW');
                        const cropHInput = container.querySelector('#cropH');
                        const cropButton = container.querySelector('#cropButton');
                        const croppedPreview = container.querySelector('#croppedImagePreview');
                        let sourceImage = null;
                        let originalFileName = 'cropped_image';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                originalFileName = e.target.files[0].name.split('.')[0] || 'image';
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    sourceImage = new Image();
                                    sourceImage.onload = () => {
                                        const MAX_PREVIEW_DIM = 300; // Max width/height for preview canvas
                                        let scale = Math.min(MAX_PREVIEW_DIM / sourceImage.width, MAX_PREVIEW_DIM / sourceImage.height, 1);
                                        cropCanvas.width = sourceImage.width * scale;
                                        cropCanvas.height = sourceImage.height * scale;
                                        ctx.drawImage(sourceImage, 0, 0, cropCanvas.width, cropCanvas.height);
                                        cropCanvas.style.display = 'block';
                                        
                                        cropWInput.value = Math.floor(sourceImage.width / 2);
                                        cropHInput.value = Math.floor(sourceImage.height / 2);
                                        cropXInput.value = Math.floor(sourceImage.width / 4);
                                        cropYInput.value = Math.floor(sourceImage.height / 4);
                                        showAlert('Image loaded. Adjust crop parameters based on original image dimensions.', 'info');
                                    };
                                    sourceImage.src = event.target.result;
                                };
                                reader.readAsDataURL(e.target.files[0]);
                            } else {
                                cropCanvas.style.display = 'none';
                                if (sourceImage) sourceImage.src = "";
                                sourceImage = null;
                            }
                        };


                        cropButton.onclick = () => {
                            if (!sourceImage) {
                                showAlert('Please select an image first.', 'error');
                                return;
                            }
                            const sx = parseInt(cropXInput.value);
                            const sy = parseInt(cropYInput.value);
                            const sWidth = parseInt(cropWInput.value);
                            const sHeight = parseInt(cropHInput.value);


                            if (isNaN(sx) || isNaN(sy) || isNaN(sWidth) || isNaN(sHeight) || sWidth <= 0 || sHeight <= 0) {
                                showAlert('Invalid crop dimensions. Ensure they are positive numbers.', 'error');
                                return;
                            }
                            if (sx + sWidth > sourceImage.width || sy + sHeight > sourceImage.height || sx < 0 || sy < 0) {
                                showAlert('Crop area is outside the image boundaries.', 'error');
                                return;
                            }
                            
                            showAlert('Cropping...', 'info');
                            const tempCanvas = document.createElement('canvas');
                            tempCanvas.width = sWidth;
                            tempCanvas.height = sHeight;
                            const tempCtx = tempCanvas.getContext('2d');
                            
                            try {
                                tempCtx.drawImage(sourceImage, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
                                const dataUrl = tempCanvas.toDataURL(fileInput.files[0].type || 'image/png');
                                croppedPreview.src = dataUrl;
                                croppedPreview.style.display = 'block';


                                const a = document.createElement('a');
                                a.href = dataUrl;
                                a.download = `${originalFileName}_cropped.${(fileInput.files[0].type || 'image/png').split('/')[1]}`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                showAlert('Image cropped and download started.', 'success');
                            } catch (error) {
                                showAlert('Error during cropping: ' + error.message, 'error');
                            }
                        };
                    },


                    videoConverter: (container) => {
                        container.innerHTML = `
                            <p>This tool attempts to convert playable videos to MP4 or WebM by re-encoding using MediaRecorder. Success and output quality depend on browser capabilities.</p>
                            <input type="file" id="vidConvFile" accept="video/*">
                            <label for="vidConvFormat">Convert to:</label>
                            <select id="vidConvFormat">
                                <option value="video/webm;codecs=vp8,opus">WebM (VP8/Opus)</option>
                                <option value="video/webm;codecs=vp9,opus">WebM (VP9/Opus - better quality)</option>
                                <option value="video/mp4;codecs=avc1.42E01E,mp4a.40.2">MP4 (H.264/AAC - browser support varies)</option>
                            </select>
                            <button id="vidConvButton">Convert & Download</button>
                            <video id="vidConvPreview" controls style="max-width:100%; margin-top:10px; display:none; background-color:black;"></video>
                            <p id="vidConvStatus" class="result-area" style="display:none;"></p>
                        `;
                        const fileInput = container.querySelector('#vidConvFile');
                        const formatSelect = container.querySelector('#vidConvFormat');
                        const convertButton = container.querySelector('#vidConvButton');
                        const videoPreview = container.querySelector('#vidConvPreview');
                        const statusP = container.querySelector('#vidConvStatus');
                        let mediaRecorder;
                        let recordedChunks = [];
                        let originalFileName = 'converted_video';


                        fileInput.onchange = (e) => {
                            recordedChunks = []; // Reset chunks
                            if (mediaRecorder && mediaRecorder.state === "recording") {
                                mediaRecorder.stop();
                            }
                            if (e.target.files && e.target.files[0]) {
                                const file = e.target.files[0];
                                originalFileName = file.name.split('.')[0] || 'video';
                                const url = URL.createObjectURL(file);
                                videoPreview.src = url;
                                videoPreview.style.display = 'block';
                                videoPreview.onloadedmetadata = () => {
                                    showAlert(`Video loaded. Duration: ${videoPreview.duration.toFixed(2)}s. Ready to convert.`, 'info');
                                    statusP.style.display = 'none';
                                }
                                videoPreview.onerror = () => showAlert('Error loading video. It might be an unsupported format.', 'error');
                            } else {
                                videoPreview.style.display = 'none';
                                videoPreview.src = "";
                            }
                        };


                        convertButton.onclick = () => {
                            if (!videoPreview.src || !videoPreview.src.startsWith('blob:')) {
                                showAlert('Please select a video file first.', 'error');
                                return;
                            }


                            const targetMimeType = formatSelect.value;
                            if (!MediaRecorder.isTypeSupported(targetMimeType)) {
                                showAlert(`Your browser does not support recording to ${targetMimeType}. Try another format or browser. Supported types often include 'video/webm', 'video/webm;codecs=vp8', 'video/webm;codecs=vp9'. MP4 support is less common for MediaRecorder.`, 'error');
                                return;
                            }


                            showAlert('Starting conversion... The video will play. Do not close modal.', 'info');
                            statusP.textContent = 'Conversion in progress... 0%';
                            statusP.style.display = 'block';
                            
                            videoPreview.currentTime = 0;
                            
                            const stream = videoPreview.captureStream ? videoPreview.captureStream() : videoPreview.mozCaptureStream ? videoPreview.mozCaptureStream() : null;


                            if (!stream) {
                                showAlert('Could not capture video stream. Your browser might not support this feature (captureStream/mozCaptureStream).', 'error');
                                return;
                            }


                            recordedChunks = [];
                            try {
                                mediaRecorder = new MediaRecorder(stream, { mimeType: targetMimeType });
                            } catch (err) {
                                 showAlert(`Error initializing MediaRecorder with ${targetMimeType}: ${err.message}. Try a simpler WebM option.`, 'error');
                                 return;
                            }




                            mediaRecorder.ondataavailable = (event) => {
                                if (event.data.size > 0) {
                                    recordedChunks.push(event.data);
                                }
                            };


                            mediaRecorder.onstop = () => {
                                if (recordedChunks.length === 0) {
                                    showAlert('Conversion stopped, but no data was recorded. The selected format/codec might be problematic for your browser.', 'error');
                                    statusP.textContent = 'Conversion failed: No data.';
                                    return;
                                }
                                const blob = new Blob(recordedChunks, { type: targetMimeType.split(';')[0] }); // Use base MIME type for Blob
                                const url = URL.createObjectURL(blob);
                                const a = document.createElement('a');
                                a.href = url;
                                a.download = `${originalFileName}_converted.${targetMimeType.split('/')[1].split(';')[0]}`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                URL.revokeObjectURL(url);
                                showAlert('Video conversion finished!', 'success');
                                statusP.textContent = 'Conversion complete!';
                                videoPreview.pause();
                            };
                            
                            mediaRecorder.onerror = (event) => {
                                showAlert(`MediaRecorder error: ${event.error ? event.error.name : 'Unknown error'}. Try a different format.`, 'error');
                                statusP.textContent = `Error: ${event.error ? event.error.name : 'Unknown'}`;
                                videoPreview.pause();
                            };
                            
                            videoPreview.onended = () => {
                                if (mediaRecorder && mediaRecorder.state === 'recording') {
                                    mediaRecorder.stop();
                                }
                            };
                            
                            videoPreview.ontimeupdate = () => {
                                if (videoPreview.duration && mediaRecorder && mediaRecorder.state === 'recording') {
                                    const progress = (videoPreview.currentTime / videoPreview.duration) * 100;
                                    statusP.textContent = `Conversion in progress... ${progress.toFixed(0)}%`;
                                }
                            };


                            videoPreview.play().then(() => {
                                mediaRecorder.start(1000); // Timeslice: ondataavailable every 1s (helps with large files)
                            }).catch(err => {
                                showAlert(`Error playing video for conversion: ${err.message}`, 'error');
                                statusP.textContent = `Error: ${err.message}`;
                            });
                        };
                        window.currentToolCleanup = () => {
                            if (mediaRecorder && mediaRecorder.state === 'recording') {
                                mediaRecorder.stop();
                            }
                            if(videoPreview) videoPreview.pause();
                            // Revoke object URL if videoPreview.src is a blob URL to free memory
                            if (videoPreview && videoPreview.src && videoPreview.src.startsWith('blob:')) {
                                URL.revokeObjectURL(videoPreview.src);
                            }
                        };
                    },
                    
                    audioConverter: (() => { // IIFE to share bufferToWave
                        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                        
                        function bufferToWave(abuffer) { // No 'len' needed, use abuffer.length
                            let numOfChan = abuffer.numberOfChannels,
                                length = abuffer.length * numOfChan * 2 + 44, // Correct length calculation
                                buffer = new ArrayBuffer(length),
                                view = new DataView(buffer),
                                channels = [], i, sample,
                                offset = 0, // Frame offset
                                pos = 0;        // Byte offset in buffer


                            // write WAVE header
                            view.setUint32(pos, 0x46464952, false); pos += 4; // "RIFF"
                            view.setUint32(pos, length - 8, true); pos += 4;  // file length - 8
                            view.setUint32(pos, 0x45564157, false); pos += 4; // "WAVE"


                            view.setUint32(pos, 0x20746d66, false); pos += 4; // "fmt " chunk
                            view.setUint32(pos, 16, true); pos += 4;              // length = 16
                            view.setUint16(pos, 1, true); pos += 2;               // PCM (uncompressed)
                            view.setUint16(pos, numOfChan, true); pos += 2;
                            view.setUint32(pos, abuffer.sampleRate, true); pos += 4;
                            view.setUint32(pos, abuffer.sampleRate * 2 * numOfChan, true); pos += 4; // "byte rate"
                            view.setUint16(pos, numOfChan * 2, true); pos += 2; // block align (channels * bytes/sample)
                            view.setUint16(pos, 16, true); pos += 2;              // bits per sample


                            view.setUint32(pos, 0x61746164, false); pos += 4; // "data" - chunk
                            view.setUint32(pos, abuffer.length * numOfChan * 2, true); pos += 4; // chunk length (audio data size)


                            for (i = 0; i < abuffer.numberOfChannels; i++)
                                channels.push(abuffer.getChannelData(i));


                            // Write interleaved data
                            for (offset = 0; offset < abuffer.length; offset++) {
                                for (i = 0; i < numOfChan; i++) {
                                    sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
                                    sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF; // scale to 16-bit signed int
                                    view.setInt16(pos, sample, true);
                                    pos += 2;
                                }
                            }
                            return new Blob([view], { type: 'audio/wav' });
                        }


                        return (container) => { // This is the actual toolInitializer function
                            container.innerHTML = `
                                <p>This tool converts various audio formats (that your browser can play) into WAV format.</p>
                                <input type="file" id="audioConvFile" accept="audio/*">
                                <button id="audioConvButton">Convert to WAV & Download</button>
                                <audio id="audioConvPreview" controls style="width:100%; margin-top:10px; display:none;"></audio>
                            `;
                            const fileInput = container.querySelector('#audioConvFile');
                            const convertButton = container.querySelector('#audioConvButton');
                            const audioPreview = container.querySelector('#audioConvPreview');
                            let decodedAudioBuffer = null; // Renamed to avoid conflict
                            let originalFileName = 'converted_audio';


                            fileInput.onchange = (e) => {
                                if (e.target.files && e.target.files[0]) {
                                    const file = e.target.files[0];
                                    originalFileName = file.name.split('.')[0] || 'audio';
                                    showAlert('Loading audio...', 'info');
                                    const reader = new FileReader();
                                    reader.onload = (event) => {
                                        audioContext.decodeAudioData(event.target.result)
                                            .then(buffer => { // Changed var name here
                                                decodedAudioBuffer = buffer;
                                                // Create a temporary URL for preview to avoid issues with large files / ArrayBuffer
                                                const tempUrl = URL.createObjectURL(file);
                                                audioPreview.src = tempUrl;
                                                audioPreview.style.display = 'block';
                                                audioPreview.oncanplaythrough = () => URL.revokeObjectURL(tempUrl); // Revoke after load
                                                showAlert('Audio loaded. Ready to convert to WAV.', 'success');
                                            })
                                            .catch(err => showAlert(`Error decoding audio: ${err.message}. Try a different format.`, 'error'));
                                    };
                                    reader.onerror = () => showAlert('Error reading file.', 'error');
                                    reader.readAsArrayBuffer(file);
                                } else {
                                    audioPreview.style.display = 'none';
                                    audioPreview.src = "";
                                    decodedAudioBuffer = null;
                                }
                            };


                            convertButton.onclick = () => {
                                if (!decodedAudioBuffer) {
                                    showAlert('Please select and load an audio file first.', 'error');
                                    return;
                                }
                                showAlert('Converting to WAV...', 'info');
                                try {
                                    const wavBlob = bufferToWave(decodedAudioBuffer); // Use the shared function
                                    const url = URL.createObjectURL(wavBlob);
                                    const a = document.createElement('a');
                                    a.href = url;
                                    a.download = `${originalFileName}_converted.wav`;
                                    document.body.appendChild(a);
                                    a.click();
                                    document.body.removeChild(a);
                                    URL.revokeObjectURL(url);
                                    showAlert('Conversion to WAV successful!', 'success');
                                } catch (err) {
                                    showAlert(`Error converting to WAV: ${err.message}`, 'error');
                                }
                            };
                            // Make bufferToWave accessible for the trimmer
                            toolInitializers.audioConverter.bufferToWave = bufferToWave;


                            window.currentToolCleanup = () => {
                                if (audioPreview && audioPreview.src && audioPreview.src.startsWith('blob:')) {
                                    URL.revokeObjectURL(audioPreview.src);
                                }
                            };
                        };
                    })(), // Immediately invoke the IIFE


                    audioTrimmer: (container) => {
                        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                        container.innerHTML = `
                            <input type="file" id="trimAudioFile" accept="audio/*">
                            <audio id="trimAudioPreview" controls style="width:100%; margin-top:10px; display:none;"></audio>
                            <div id="trimControls" style="display:none; margin-top:10px;">
                                <label for="trimStartTime">Start Time (s):</label>
                                <input type="number" id="trimStartTime" value="0" min="0" step="0.01">
                                <label for="trimEndTime">End Time (s):</label>
                                <input type="number" id="trimEndTime" value="0" min="0" step="0.01">
                                <p id="audioDurationInfo"></p>
                                <button id="trimButton">Trim & Download WAV</button>
                            </div>
                        `;
                        const fileInput = container.querySelector('#trimAudioFile');
                        const audioPreview = container.querySelector('#trimAudioPreview');
                        const trimControls = container.querySelector('#trimControls');
                        const startTimeInput = container.querySelector('#trimStartTime');
                        const endTimeInput = container.querySelector('#trimEndTime');
                        const durationInfo = container.querySelector('#audioDurationInfo');
                        const trimButton = container.querySelector('#trimButton');
                        
                        let sourceBuffer = null;
                        let originalFileName = 'trimmed_audio';


                        fileInput.onchange = (e) => {
                            if (e.target.files && e.target.files[0]) {
                                const file = e.target.files[0];
                                originalFileName = file.name.split('.')[0] || 'audio';
                                showAlert('Loading audio...', 'info');
                                const reader = new FileReader();
                                reader.onload = (event) => {
                                    audioContext.decodeAudioData(event.target.result)
                                        .then(decodedBuffer => {
                                            sourceBuffer = decodedBuffer;
                                            const tempUrl = URL.createObjectURL(file);
                                            audioPreview.src = tempUrl;
                                            audioPreview.style.display = 'block';
                                            audioPreview.onloadedmetadata = () => {
                                                URL.revokeObjectURL(tempUrl); // Revoke after duration is known
                                                const duration = sourceBuffer.duration; // Use buffer duration for accuracy
                                                durationInfo.textContent = `Duration: ${duration.toFixed(2)}s`;
                                                endTimeInput.value = duration.toFixed(2);
                                                endTimeInput.max = duration.toFixed(2);
                                                startTimeInput.max = duration.toFixed(2);
                                                trimControls.style.display = 'block';
                                                showAlert('Audio loaded. Set trim times.', 'success');
                                            };
                                            audioPreview.onerror = () => showAlert('Error playing preview.', 'error');
                                        })
                                        .catch(err => showAlert(`Error decoding audio: ${err.message}`, 'error'));
                                };
                                reader.readAsArrayBuffer(file);
                            } else {
                                 audioPreview.style.display = 'none';
                                 trimControls.style.display = 'none';
                                 audioPreview.src = "";
                                 sourceBuffer = null;
                            }
                        };


                        trimButton.onclick = () => {
                            if (!sourceBuffer) {
                                showAlert('Please load an audio file first.', 'error');
                                return;
                            }
                            const startTime = parseFloat(startTimeInput.value);
                            const endTime = parseFloat(endTimeInput.value);


                            if (isNaN(startTime) || isNaN(endTime) || startTime < 0 || endTime <= startTime || endTime > sourceBuffer.duration) {
                                showAlert('Invalid start/end times. Ensure End Time is after Start Time and within audio duration.', 'error');
                                return;
                            }
                            
                            showAlert('Trimming audio...', 'info');
                            try {
                                const startOffset = Math.floor(startTime * sourceBuffer.sampleRate);
                                const endOffset = Math.floor(endTime * sourceBuffer.sampleRate);
                                const frameCount = endOffset - startOffset;


                                if (frameCount <=0) {
                                    showAlert('Trimmed duration is zero or negative.', 'error');
                                    return;
                                }


                                const trimmedBuffer = audioContext.createBuffer(
                                    sourceBuffer.numberOfChannels,
                                    frameCount,
                                    sourceBuffer.sampleRate
                                );


                                for (let i = 0; i < sourceBuffer.numberOfChannels; i++) {
                                    const channelData = sourceBuffer.getChannelData(i);
                                    const trimmedChannelData = trimmedBuffer.getChannelData(i);
                                    // Use subarray correctly: source.subarray(begin, end)
                                    trimmedChannelData.set(channelData.subarray(startOffset, endOffset));
                                }
                                
                                if (typeof toolInitializers.audioConverter.bufferToWave !== 'function') {
                                    showAlert('Audio conversion utility not found.', 'error');
                                    return;
                                }
                                const wavBlob = toolInitializers.audioConverter.bufferToWave(trimmedBuffer);


                                const url = URL.createObjectURL(wavBlob);
                                const a = document.createElement('a');
                                a.href = url;
                                a.download = `${originalFileName}_trimmed.wav`;
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                                URL.revokeObjectURL(url);
                                showAlert('Audio trimmed and downloaded!', 'success');


                            } catch (err) {
                                showAlert(`Error trimming audio: ${err.message}`, 'error');
                                console.error("Trimming error:", err);
                            }
                        };
                        window.currentToolCleanup = () => {
                            if (audioPreview && audioPreview.src && audioPreview.src.startsWith('blob:')) {
                                URL.revokeObjectURL(audioPreview.src);
                            }
                        };
                    },
                    
                    // ... (The rest of the toolInitializers from the previous response) ...
                    // --- Age Calculator ---
                    ageCalculator: (container) => {
                        container.innerHTML = `
                            <label for="birthDate">Enter your Date of Birth:</label>
                            <input type="date" id="birthDate">
                            <button id="calculateAgeBtn">Calculate Age</button>
                            <div id="ageResult" class="result-area" style="display:none;"></div>
                        `;
                        const birthDateInput = container.querySelector('#birthDate');
                        const calculateBtn = container.querySelector('#calculateAgeBtn');
                        const ageResultDiv = container.querySelector('#ageResult');


                        calculateBtn.onclick = () => {
                            const birthDateString = birthDateInput.value;
                            if (!birthDateString) {
                                showAlert('Please enter your date of birth.', 'error');
                                ageResultDiv.style.display = 'none';
                                return;
                            }
                            const birthDate = new Date(birthDateString);
                            const today = new Date();


                            if (birthDate > today) {
                                showAlert('Birth date cannot be in the future.', 'error');
                                ageResultDiv.style.display = 'none';
                                return;
                            }


                            let years = today.getFullYear() - birthDate.getFullYear();
                            let months = today.getMonth() - birthDate.getMonth();
                            let days = today.getDate() - birthDate.getDate();


                            if (days < 0) {
                                months--;
                                // Get days in the previous month of 'today'
                                const prevMonth = new Date(today.getFullYear(), today.getMonth(), 0);
                                days += prevMonth.getDate();
                            }
                            if (months < 0) {
                                years--;
                                months += 12;
                            }
                            ageResultDiv.innerHTML = `You are: <br>
                                                    <strong>${years}</strong> years,
                                                    <strong>${months}</strong> months, and
                                                    <strong>${days}</strong> days old.`;
                            ageResultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- EMI Calculator ---
                    emiCalculator: (container) => {
                        container.innerHTML = `
                            <label for="loanAmount">Loan Amount (₹):</label>
                            <input type="number" id="loanAmount" placeholder="e.g., 100000" min="0">
                            <label for="interestRate">Annual Interest Rate (%):</label>
                            <input type="number" id="interestRate" placeholder="e.g., 10.5" min="0" step="0.01">
                            <label for="loanTenure">Loan Tenure (months):</label>
                            <input type="number" id="loanTenure" placeholder="e.g., 12" min="1">
                            <button id="calculateEmiBtn">Calculate EMI</button>
                            <div id="emiResult" class="result-area" style="display:none;"></div>
                        `;
                        const amountInput = container.querySelector('#loanAmount');
                        const rateInput = container.querySelector('#interestRate');
                        const tenureInput = container.querySelector('#loanTenure');
                        const calculateBtn = container.querySelector('#calculateEmiBtn');
                        const resultDiv = container.querySelector('#emiResult');


                        calculateBtn.onclick = () => {
                            const P = parseFloat(amountInput.value);
                            const annualRate = parseFloat(rateInput.value);
                            const N = parseInt(tenureInput.value);


                            if (isNaN(P) || isNaN(annualRate) || isNaN(N) || P <= 0 || annualRate < 0 || N <= 0) {
                                showAlert('Please enter valid positive numbers for amount & tenure, and a non-negative rate.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }
                            
                            if (annualRate === 0) { // Handle zero interest rate
                                 const EMI = P / N;
                                 resultDiv.innerHTML = `Monthly EMI: <strong>₹${EMI.toFixed(2)}</strong><br>
                                           Total Interest Payable: <strong>₹0.00</strong><br>
                                           Total Payment (Principal + Interest): <strong>₹${P.toFixed(2)}</strong>`;
                                resultDiv.style.display = 'block';
                                hideAlert();
                                return;
                            }




                            const r = annualRate / (12 * 100); // Monthly interest rate
                            const EMI = (P * r * Math.pow(1 + r, N)) / (Math.pow(1 + r, N) - 1);
                            const totalPayment = EMI * N;
                            const totalInterest = totalPayment - P;


                            if (!isFinite(EMI)) {
                                showAlert('Calculation resulted in an invalid number. Please check your inputs.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }


                            resultDiv.innerHTML = `Monthly EMI: <strong>₹${EMI.toFixed(2)}</strong><br>
                                                Total Interest Payable: <strong>₹${totalInterest.toFixed(2)}</strong><br>
                                                Total Payment (Principal + Interest): <strong>₹${totalPayment.toFixed(2)}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- SIP Calculator ---
                    sipCalculator: (container) => {
                        container.innerHTML = `
                            <label for="monthlyInvestment">Monthly Investment (₹):</label>
                            <input type="number" id="monthlyInvestment" placeholder="e.g., 5000" min="0">
                            <label for="expectedReturnRate">Expected Annual Return Rate (%):</label>
                            <input type="number" id="expectedReturnRate" placeholder="e.g., 12" min="0" step="0.01">
                            <label for="investmentDuration">Investment Duration (years):</label>
                            <input type="number" id="investmentDuration" placeholder="e.g., 10" min="1">
                            <button id="calculateSipBtn">Calculate Future Value</button>
                            <div id="sipResult" class="result-area" style="display:none;"></div>
                        `;
                        const monthlyInvestmentInput = container.querySelector('#monthlyInvestment');
                        const returnRateInput = container.querySelector('#expectedReturnRate');
                        const durationInput = container.querySelector('#investmentDuration');
                        const calculateBtn = container.querySelector('#calculateSipBtn');
                        const resultDiv = container.querySelector('#sipResult');


                        calculateBtn.onclick = () => {
                            const P = parseFloat(monthlyInvestmentInput.value);
                            const annualRate = parseFloat(returnRateInput.value);
                            const t_years = parseInt(durationInput.value);


                            if (isNaN(P) || isNaN(annualRate) || isNaN(t_years) || P <= 0 || annualRate < 0 || t_years <= 0) {
                                showAlert('Please enter valid positive numbers for investment & duration, and a non-negative rate.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }


                            const n = t_years * 12;
                            const i = annualRate / 12 / 100;
                            let M;


                            if (i === 0) { // Handle zero interest rate for SIP
                                M = P * n;
                            } else {
                                M = P * ( (Math.pow(1 + i, n) - 1) / i ) * (1 + i);
                            }
                            
                            const totalInvestment = P * n;
                            const wealthGained = M - totalInvestment;


                            resultDiv.innerHTML = `Invested Amount: <strong>₹${totalInvestment.toFixed(2)}</strong><br>
                                                Estimated Returns: <strong>₹${wealthGained.toFixed(2)}</strong><br>
                                                Total Future Value: <strong>₹${M.toFixed(2)}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- QR Code Generator ---
                    qrCodeGenerator: (container) => {
                        container.innerHTML = `
                            <label for="qrText">Text or URL for QR Code:</label>
                            <textarea id="qrText" placeholder="Enter text here"></textarea>
                            <label for="qrSize">Size (pixels):</label>
                            <input type="number" id="qrSize" value="200" min="50" max="500">
                            <button id="generateQrBtn">Generate QR Code</button>
                            <div id="qrCodeContainer" class="result-area" style="text-align:center; display:none; padding:10px; background-color:white;"></div>
                            <button id="downloadQrBtn" style="display:none;">Download QR Code</button>
                        `;
                        const textInput = container.querySelector('#qrText');
                        const sizeInput = container.querySelector('#qrSize');
                        const generateBtn = container.querySelector('#generateQrBtn');
                        const qrContainer = container.querySelector('#qrCodeContainer');
                        const downloadBtn = container.querySelector('#downloadQrBtn');
                        let qrCanvasInstance = null; // To hold the canvas element itself


                        generateBtn.onclick = () => {
                            const text = textInput.value.trim();
                            const size = parseInt(sizeInput.value);
                            if (!text) {
                                showAlert('Please enter text or URL for the QR code.', 'error');
                                qrContainer.style.display = 'none';
                                downloadBtn.style.display = 'none';
                                return;
                            }
                            if (isNaN(size) || size < 50 || size > 500) {
                                showAlert('Please enter a valid size between 50 and 500 pixels.', 'error');
                                return;
                            }


                            qrContainer.innerHTML = ''; // Clear previous QR code
                            
                            try {
                                if (typeof QRCode !== 'undefined') { // Check if a library like qrcode.js is loaded
                                    qrCanvasInstance = document.createElement('div'); // qrcode.js typically targets a div
                                    qrContainer.appendChild(qrCanvasInstance);
                                    new QRCode(qrCanvasInstance, {
                                        text: text,
                                        width: size,
                                        height: size,
                                        colorDark : "#000000",
                                        colorLight : "#ffffff",
                                        correctLevel : QRCode.CorrectLevel.H
                                    });
                                    // qrcode.js generates an img and a canvas, we want the canvas for download
                                    // Wait a moment for QRCode.js to render
                                    setTimeout(() => {
                                        const canvasFromLib = qrCanvasInstance.querySelector('canvas');
                                        if (canvasFromLib) {
                                            qrCanvasInstance = canvasFromLib; // Now qrCanvasInstance is the actual canvas
                                        } else {
                                            // Fallback if canvas not found, might be an img tag
                                            const imgFromLib = qrCanvasInstance.querySelector('img');
                                            if (imgFromLib) {
                                                // Create a canvas from the image
                                                const tempCanvas = document.createElement('canvas');
                                                tempCanvas.width = imgFromLib.width;
                                                tempCanvas.height = imgFromLib.height;
                                                tempCanvas.getContext('2d').drawImage(imgFromLib, 0, 0);
                                                qrCanvasInstance = tempCanvas;
                                            }
                                        }
                                    }, 100);
                                    showAlert('QR Code generated!', 'success');
                                } else {
                                    // Fallback to simplified canvas drawing if no library
                                    qrCanvasInstance = document.createElement('canvas');
                                    qrContainer.appendChild(qrCanvasInstance);
                                    const ctx = qrCanvasInstance.getContext('2d');
                                    qrCanvasInstance.width = size;
                                    qrCanvasInstance.height = size;
                                    ctx.fillStyle = 'white';
                                    ctx.fillRect(0, 0, size, size);
                                    ctx.fillStyle = 'black';
                                    ctx.font = `${size/15}px Arial`;
                                    ctx.textAlign = 'center';
                                    ctx.textBaseline = 'middle';
                                    // Simple placeholder text rendering - not a real QR
                                    const lines = text.match(/.{1,20}/g) || [text]; // Split text
                                    lines.forEach((line, i) => {
                                        ctx.fillText(line, size / 2, size / 2 - (lines.length/2 * (size/10)) + (i * (size/10)) );
                                    });
                                    ctx.strokeRect(size*0.1, size*0.1, size*0.8, size*0.8); // Border
                                    showAlert('QR Code (placeholder - library not found) generated. For real QR codes, integrate a library.', 'info');
                                }
                                qrContainer.style.display = 'block';
                                downloadBtn.style.display = 'inline-block';
                            } catch (e) {
                                showAlert('Error generating QR code: ' + e.message, 'error');
                                console.error("QR Gen Error:", e);
                                qrContainer.style.display = 'none';
                                downloadBtn.style.display = 'none';
                            }
                        };


                        downloadBtn.onclick = () => {
                            if (qrCanvasInstance && qrCanvasInstance.tagName === 'CANVAS') {
                                const dataURL = qrCanvasInstance.toDataURL('image/png');
                                const a = document.createElement('a');
                                a.href = dataURL;
                                a.download = 'qrcode.png';
                                document.body.appendChild(a);
                                a.click();
                                document.body.removeChild(a);
                            } else if (qrCanvasInstance && qrCanvasInstance.tagName === 'IMG') { // If library generated an IMG
                                 const a = document.createElement('a');
                                 a.href = qrCanvasInstance.src;
                                 a.download = 'qrcode.png';
                                 document.body.appendChild(a);
                                 a.click();
                                 document.body.removeChild(a);
                            } else {
                                showAlert('QR Code canvas not available for download.', 'error');
                            }
                        };
                    },


                    // --- Password Generator ---
                    passwordGenerator: (container) => {
                        container.innerHTML = `
                            <label for="passLength">Password Length:</label>
                            <input type="number" id="passLength" value="16" min="8" max="128">
                            <div class="option-group">
                                <input type="checkbox" id="incUppercase" checked> <label for="incUppercase">Uppercase (A-Z)</label><br>
                                <input type="checkbox" id="incLowercase" checked> <label for="incLowercase">Lowercase (a-z)</label><br>
                                <input type="checkbox" id="incNumbers" checked> <label for="incNumbers">Numbers (0-9)</label><br>
                                <input type="checkbox" id="incSymbols" checked> <label for="incSymbols">Symbols (!@#$%^&*)</label>
                            </div>
                            <button id="generatePassBtn">Generate Password</button>
                            <div class="result-area" style="margin-top:1rem; display:flex; align-items:center; justify-content:space-between;">
                                <input type="text" id="generatedPassword" readonly style="flex-grow:1; margin-right:10px; background-color: var(--background-color); border: 1px solid var(--tool-card-background); color: var(--text-color);">
                                <button id="copyPassBtn" title="Copy to Clipboard" style="padding: 0.5em 0.8em;">📋</button>
                            </div>
                        `;
                        const lengthInput = container.querySelector('#passLength');
                        const uppercaseCheck = container.querySelector('#incUppercase');
                        const lowercaseCheck = container.querySelector('#incLowercase');
                        const numbersCheck = container.querySelector('#incNumbers');
                        const symbolsCheck = container.querySelector('#incSymbols');
                        const generateBtn = container.querySelector('#generatePassBtn');
                        const passwordOutput = container.querySelector('#generatedPassword');
                        const copyBtn = container.querySelector('#copyPassBtn');


                        const charSets = {
                            uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                            lowercase: 'abcdefghijklmnopqrstuvwxyz',
                            numbers: '0123456789',
                            symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?'
                        };


                        generateBtn.onclick = () => {
                            const length = parseInt(lengthInput.value);
                            let charset = '';
                            let guaranteedChars = ''; // To ensure at least one char from each selected set


                            if (uppercaseCheck.checked) {
                                charset += charSets.uppercase;
                                guaranteedChars += charSets.uppercase[Math.floor(Math.random() * charSets.uppercase.length)];
                            }
                            if (lowercaseCheck.checked) {
                                charset += charSets.lowercase;
                                 guaranteedChars += charSets.lowercase[Math.floor(Math.random() * charSets.lowercase.length)];
                            }
                            if (numbersCheck.checked) {
                                charset += charSets.numbers;
                                guaranteedChars += charSets.numbers[Math.floor(Math.random() * charSets.numbers.length)];
                            }
                            if (symbolsCheck.checked) {
                                charset += charSets.symbols;
                                guaranteedChars += charSets.symbols[Math.floor(Math.random() * charSets.symbols.length)];
                            }


                            if (charset === '') {
                                showAlert('Please select at least one character type.', 'error');
                                passwordOutput.value = '';
                                return;
                            }
                            if (length < 8 || length > 128) {
                                showAlert('Password length must be between 8 and 128.', 'error');
                                return;
                            }
                            if (length < guaranteedChars.length) {
                                showAlert('Password length is too short to include one of each selected character type.', 'error');
                                return;
                            }




                            let password = guaranteedChars;
                            for (let i = guaranteedChars.length; i < length; i++) {
                                password += charset.charAt(Math.floor(Math.random() * charset.length));
                            }
                            
                            // Shuffle the password to make guaranteed characters random
                            password = password.split('').sort(() => 0.5 - Math.random()).join('');


                            passwordOutput.value = password;
                            hideAlert();
                        };


                        copyBtn.onclick = () => {
                            if (passwordOutput.value) {
                                navigator.clipboard.writeText(passwordOutput.value)
                                    .then(() => showAlert('Password copied to clipboard!', 'success'))
                                    .catch(err => showAlert('Failed to copy password.', 'error'));
                            }
                        };
                    },


                    // --- Word Counter ---
                    wordCounter: (container) => {
                        container.innerHTML = `
                            <textarea id="wcText" placeholder="Paste or type your text here..." rows="8"></textarea>
                            <div id="wcResult" class="result-area" style="margin-top:1rem;">
                                Words: <strong id="wcWords">0</strong><br>
                                Characters (with spaces): <strong id="wcCharsWithSpaces">0</strong><br>
                                Characters (no spaces): <strong id="wcCharsNoSpaces">0</strong><br>
                                Spaces: <strong id="wcSpaces">0</strong><br>
                                Sentences: <strong id="wcSentences">0</strong><br>
                                Paragraphs: <strong id="wcParagraphs">0</strong><br>
                                Reading Time: <strong id="wcReadingTime">~0 min</strong>
                            </div>
                        `;
                        const textArea = container.querySelector('#wcText');
                        const wordsSpan = container.querySelector('#wcWords');
                        const charsWithSpacesSpan = container.querySelector('#wcCharsWithSpaces');
                        const charsNoSpacesSpan = container.querySelector('#wcCharsNoSpaces');
                        const spacesSpan = container.querySelector('#wcSpaces');
                        const sentencesSpan = container.querySelector('#wcSentences');
                        const paragraphsSpan = container.querySelector('#wcParagraphs');
                        const readingTimeSpan = container.querySelector('#wcReadingTime');


                        textArea.oninput = () => {
                            const text = textArea.value;
                            
                            const wordsArray = text.match(/\b[-'\w]+\b/g);
                            const words = wordsArray ? wordsArray.length : 0;
                            
                            const charsWithSpaces = text.length;
                            const charsNoSpaces = text.replace(/\s+/g, '').length;
                            const spaces = (text.match(/\s/g) || []).length;


                            const sentencesArray = text.match(/[^.!?]+[.!?]+(\s|$)/g);
                            const sentences = sentencesArray ? sentencesArray.length : (text.trim() ? 1: 0);
                            
                            const paragraphsArray = text.split(/\n\s*\n/).filter(p => p.trim() !== '');
                            const paragraphs = paragraphsArray.length || (text.trim() ? 1:0);
                            
                            const wpm = 200;
                            const readingTimeMinutes = words / wpm;
                            let readingTimeText = '';
                            if (readingTimeMinutes === 0) {
                                readingTimeText = `~0 min`;
                            } else if (readingTimeMinutes < 1) {
                                const seconds = Math.round(readingTimeMinutes * 60);
                                readingTimeText = `~${seconds} sec`;
                            } else {
                                readingTimeText = `~${Math.ceil(readingTimeMinutes)} min`;
                            }


                            wordsSpan.textContent = words;
                            charsWithSpacesSpan.textContent = charsWithSpaces;
                            charsNoSpacesSpan.textContent = charsNoSpaces;
                            spacesSpan.textContent = spaces;
                            sentencesSpan.textContent = sentences;
                            paragraphsSpan.textContent = paragraphs;
                            readingTimeSpan.textContent = readingTimeText;
                        };
                    },


                    // --- Base64 Encoder/Decoder ---
                    base64EncoderDecoder: (container) => {
                        container.innerHTML = `
                            <textarea id="b64Input" placeholder="Enter text to encode or Base64 to decode" rows="5"></textarea>
                            <button id="b64EncodeBtn">Encode to Base64</button>
                            <button id="b64DecodeBtn">Decode from Base64</button>
                            <label for="b64Output" style="margin-top:1rem; display:block;">Result:</label>
                            <textarea id="b64Output" readonly rows="5"></textarea>
                        `;
                        const inputArea = container.querySelector('#b64Input');
                        const outputArea = container.querySelector('#b64Output');
                        const encodeBtn = container.querySelector('#b64EncodeBtn');
                        const decodeBtn = container.querySelector('#b64DecodeBtn');


                        encodeBtn.onclick = () => {
                            try {
                                // Handle UTF-8 characters correctly for btoa
                                const utf8Encoded = new TextEncoder().encode(inputArea.value);
                                let binaryString = '';
                                utf8Encoded.forEach(byte => binaryString += String.fromCharCode(byte));
                                outputArea.value = btoa(binaryString);
                                hideAlert();
                            } catch (e) {
                                showAlert('Error encoding: ' + e.message, 'error');
                                outputArea.value = '';
                            }
                        };


                        decodeBtn.onclick = () => {
                            try {
                                 // Handle UTF-8 characters correctly for atob
                                const binaryString = atob(inputArea.value);
                                const bytes = new Uint8Array(binaryString.length);
                                for (let i = 0; i < binaryString.length; i++) {
                                    bytes[i] = binaryString.charCodeAt(i);
                                }
                                outputArea.value = new TextDecoder().decode(bytes);
                                hideAlert();
                            } catch (e) {
                                showAlert('Error decoding: Invalid Base64 string or character issue. ' + e.message, 'error');
                                outputArea.value = '';
                            }
                        };
                    },


                    // --- Color Picker Tool ---
                    colorPicker: (container) => {
                        container.innerHTML = `
                            <label for="htmlColorPicker">Select a Color:</label>
                            <input type="color" id="htmlColorPicker" value="#FFD700" style="width:100%; height: 40px; margin-bottom:1rem;">
                            <div class="color-picker-display result-area">
                                <div id="colorPreview" class="color-preview"></div>
                                <div id="colorValues" class="color-values">
                                    <p>HEX: <strong id="hexValue"></strong> <button class="copy-color-val" data-type="hex" title="Copy HEX">📋</button></p>
                                    <p>RGB: <strong id="rgbValue"></strong> <button class="copy-color-val" data-type="rgb" title="Copy RGB">📋</button></p>
                                    <p>HSL: <strong id="hslValue"></strong> <button class="copy-color-val" data-type="hsl" title="Copy HSL">📋</button></p>
                                </div>
                            </div>
                        `;
                        const colorPickerInput = container.querySelector('#htmlColorPicker');
                        const colorPreviewDiv = container.querySelector('#colorPreview');
                        const hexValueSpan = container.querySelector('#hexValue');
                        const rgbValueSpan = container.querySelector('#rgbValue');
                        const hslValueSpan = container.querySelector('#hslValue');
                        const copyButtons = container.querySelectorAll('.copy-color-val');


                        let currentHex, currentRgb, currentHsl;


                        function updateColorValues(hex) {
                            currentHex = hex.toUpperCase();
                            colorPreviewDiv.style.backgroundColor = currentHex;
                            hexValueSpan.textContent = currentHex;


                            const rgb = hexToRgb(currentHex);
                            currentRgb = `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
                            rgbValueSpan.textContent = currentRgb;


                            const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
                            currentHsl = `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
                            hslValueSpan.textContent = currentHsl;
                        }


                        colorPickerInput.oninput = (e) => {
                            updateColorValues(e.target.value);
                            hideAlert();
                        };
                        
                        copyButtons.forEach(btn => {
                            btn.style.padding = "0.2em 0.5em"; // Smaller copy buttons
                            btn.style.marginLeft = "5px";
                            btn.onclick = () => {
                                let valueToCopy;
                                const type = btn.dataset.type;
                                if (type === 'hex') valueToCopy = currentHex;
                                else if (type === 'rgb') valueToCopy = currentRgb;
                                else if (type === 'hsl') valueToCopy = currentHsl;


                                if (valueToCopy) {
                                    navigator.clipboard.writeText(valueToCopy)
                                        .then(() => showAlert(`${type.toUpperCase()} value copied!`, 'success'))
                                        .catch(() => showAlert('Failed to copy.', 'error'));
                                }
                            };
                        });


                        updateColorValues(colorPickerInput.value); // Initial value


                        function hexToRgb(hex) {
                            let r = 0, g = 0, b = 0;
                            if (hex.length === 4) {
                                r = parseInt(hex[1] + hex[1], 16);
                                g = parseInt(hex[2] + hex[2], 16);
                                b = parseInt(hex[3] + hex[3], 16);
                            } else if (hex.length === 7) {
                                r = parseInt(hex.substring(1, 3), 16);
                                g = parseInt(hex.substring(3, 5), 16);
                                b = parseInt(hex.substring(5, 7), 16);
                            }
                            return { r, g, b };
                        }


                        function rgbToHsl(r, g, b) {
                            r /= 255; g /= 255; b /= 255;
                            const max = Math.max(r, g, b), min = Math.min(r, g, b);
                            let h, s, l = (max + min) / 2;


                            if (max === min) {
                                h = s = 0;
                            } else {
                                const d = max - min;
                                s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
                                switch (max) {
                                    case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                                    case g: h = (b - r) / d + 2; break;
                                    case b: h = (r - g) / d + 4; break;
                                }
                                h /= 6;
                            }
                            return {
                                h: Math.round(h * 360),
                                s: Math.round(s * 100),
                                l: Math.round(l * 100)
                            };
                        }
                    },


                    // --- Text to Speech ---
                    textToSpeech: (container) => {
                        container.innerHTML = `
                            <textarea id="ttsText" placeholder="Enter text to speak..." rows="6"></textarea>
                            <div class="option-group">
                                <label for="ttsVoice">Voice:</label>
                                <select id="ttsVoice"></select>
                                <label for="ttsRate" style="margin-left:10px;">Rate: <span id="ttsRateVal">1</span></label>
                                <input type="range" id="ttsRate" min="0.5" max="2" value="1" step="0.1" style="vertical-align: middle;">
                                <label for="ttsPitch" style="margin-left:10px;">Pitch: <span id="ttsPitchVal">1</span></label>
                                <input type="range" id="ttsPitch" min="0" max="2" value="1" step="0.1" style="vertical-align: middle;">
                            </div>
                            <button id="ttsSpeakBtn">Speak</button>
                            <button id="ttsPauseBtn">Pause</button>
                            <button id="ttsResumeBtn">Resume</button>
                            <button id="ttsStopBtn">Stop</button>
                        `;
                        const textInput = container.querySelector('#ttsText');
                        const voiceSelect = container.querySelector('#ttsVoice');
                        const rateInput = container.querySelector('#ttsRate');
                        const pitchInput = container.querySelector('#ttsPitch');
                        const rateValSpan = container.querySelector('#ttsRateVal');
                        const pitchValSpan = container.querySelector('#ttsPitchVal');
                        const speakBtn = container.querySelector('#ttsSpeakBtn');
                        const pauseBtn = container.querySelector('#ttsPauseBtn');
                        const resumeBtn = container.querySelector('#ttsResumeBtn');
                        const stopBtn = container.querySelector('#ttsStopBtn');


                        const synth = window.speechSynthesis;
                        if (!synth) {
                            container.innerHTML = "<p>Sorry, your browser doesn't support Text to Speech.</p>";
                            return;
                        }
                        let voices = [];
                        let currentUtterance = null;


                        function populateVoiceList() {
                            voices = synth.getVoices().sort((a,b) => a.name.localeCompare(b.name));
                            const selectedVoiceName = voiceSelect.value;
                            voiceSelect.innerHTML = '';
                            voices.forEach(voice => {
                                const option = document.createElement('option');
                                option.textContent = `${voice.name} (${voice.lang})`;
                                option.value = voice.name;
                                if (voice.default) option.selected = true;
                                voiceSelect.appendChild(option);
                            });
                            if (selectedVoiceName) voiceSelect.value = selectedVoiceName; // Retain selection
                        }


                        populateVoiceList();
                        if (synth.onvoiceschanged !== undefined) {
                            synth.onvoiceschanged = populateVoiceList;
                        }
                        
                        rateInput.oninput = () => rateValSpan.textContent = rateInput.value;
                        pitchInput.oninput = () => pitchValSpan.textContent = pitchInput.value;




                        speakBtn.onclick = () => {
                            if (synth.speaking) { // If speaking, stop current and start new
                                synth.cancel();
                            }
                            if (textInput.value.trim() !== '') {
                                hideAlert();
                                currentUtterance = new SpeechSynthesisUtterance(textInput.value.trim());
                                const selectedVoice = voices.find(voice => voice.name === voiceSelect.value);
                                if (selectedVoice) currentUtterance.voice = selectedVoice;
                                currentUtterance.pitch = parseFloat(pitchInput.value);
                                currentUtterance.rate = parseFloat(rateInput.value);
                                currentUtterance.onstart = () => {
                                    speakBtn.disabled = true; pauseBtn.disabled = false; resumeBtn.disabled = true; stopBtn.disabled = false;
                                }
                                currentUtterance.onend = () => {
                                    speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                                    currentUtterance = null;
                                };
                                currentUtterance.onerror = (e) => {
                                    showAlert(`Error during speech: ${e.error}`, 'error');
                                    speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                                    currentUtterance = null;
                                };
                                synth.speak(currentUtterance);
                            } else {
                                showAlert('Please enter some text to speak.', 'error');
                            }
                        };
                        pauseBtn.onclick = () => {
                            if(synth.speaking && !synth.paused) {
                                 synth.pause();
                                 pauseBtn.disabled = true; resumeBtn.disabled = false;
                            }
                        };
                        resumeBtn.onclick = () => {
                             if(synth.paused) {
                                synth.resume();
                                pauseBtn.disabled = false; resumeBtn.disabled = true;
                             }
                        };
                        stopBtn.onclick = () => {
                            if(synth.speaking || synth.paused) {
                                synth.cancel(); // This also triggers onend for the utterance
                            }
                             speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;
                             currentUtterance = null;
                        };
                         // Initial button states
                        speakBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true;


                        window.currentToolCleanup = () => {
                            if (synth && (synth.speaking || synth.paused)) {
                                synth.cancel();
                            }
                        };
                    },


                    // --- Speech to Text ---
                    speechToText: (container) => {
                        container.innerHTML = `
                            <p>Click "Start Listening" and speak into your microphone. Allow microphone access when prompted.</p>
                            <button id="sttStartBtn">Start Listening</button>
                            <button id="sttStopBtn" disabled>Stop Listening</button>
                            <textarea id="sttOutput" placeholder="Transcript will appear here..." readonly rows="6"></textarea>
                        `;
                        const startBtn = container.querySelector('#sttStartBtn');
                        const stopBtn = container.querySelector('#sttStopBtn');
                        const outputArea = container.querySelector('#sttOutput');


                        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
                        if (!SpeechRecognition) {
                            container.innerHTML = '<p>Speech Recognition API is not supported in your browser.</p>';
                            return;
                        }
                        const recognition = new SpeechRecognition();
                        recognition.continuous = true;
                        recognition.interimResults = true;
                        recognition.lang = navigator.language || 'en-US'; // Use browser's language


                        let finalTranscript = '';


                        recognition.onstart = () => {
                            startBtn.disabled = true;
                            stopBtn.disabled = false;
                            showAlert('Listening... Speak clearly.', 'info');
                        };
                        recognition.onend = () => {
                            startBtn.disabled = false;
                            stopBtn.disabled = true;
                            if (outputArea.value.trim() === '') hideAlert(); // Keep "Listening" if no final output yet
                            else if (finalTranscript.trim() !== '') showAlert('Stopped listening.', 'success');
                            else showAlert('Stopped. No speech clearly recognized.', 'info');
                        };
                        recognition.onerror = (event) => {
                            let errorMessage = `Speech recognition error: ${event.error}`;
                            if (event.error === 'no-speech') {
                                errorMessage = 'No speech was detected. Try speaking louder or closer to the microphone.';
                            } else if (event.error === 'audio-capture') {
                                errorMessage = 'Audio capture failed. Ensure your microphone is working and permitted.';
                            } else if (event.error === 'not-allowed') {
                                errorMessage = 'Microphone access denied. Please allow access in browser settings.';
                            }
                            showAlert(errorMessage, 'error');
                            startBtn.disabled = false;
                            stopBtn.disabled = true;
                        };
                        recognition.onresult = (event) => {
                            let interimTranscript = '';
                            for (let i = event.resultIndex; i < event.results.length; ++i) {
                                if (event.results[i].isFinal) {
                                    finalTranscript += event.results[i][0].transcript + ' ';
                                } else {
                                    interimTranscript += event.results[i][0].transcript;
                                }
                            }
                            outputArea.value = finalTranscript + interimTranscript;
                            if (interimTranscript) hideAlert(); // Hide general alerts if interim results are coming
                        };


                        startBtn.onclick = () => {
                            finalTranscript = ''; // Reset final transcript
                            outputArea.value = '';
                            try {
                                recognition.start();
                            } catch (e) { // Catch if already started
                                if (e.name === 'InvalidStateError') {
                                    recognition.stop(); // Stop first, then it will be restartable on next click or via onend
                                    showAlert('Recognition was already active. Try again.', 'info');
                                } else {
                                    showAlert('Could not start recognition: ' + e.message, 'error');
                                }
                            }
                        };
                        stopBtn.onclick = () => recognition.stop();
                        
                        window.currentToolCleanup = () => {
                            if (recognition) {
                                try { recognition.stop(); } catch(e){}
                            }
                        };
                    },


                    // --- JSON Formatter ---
                    jsonFormatter: (container) => {
                        container.innerHTML = `
                            <textarea id="jsonInput" placeholder="Paste your JSON data here..." rows="7"></textarea>
                            <div style="display:flex; align-items:center; margin-bottom:1rem;">
                                <label for="jsonSpaces" style="margin-right:10px; margin-bottom:0;">Indentation:</label>
                                <select id="jsonSpaces" style="width:auto; margin-bottom:0;">
                                    <option value="2">2 Spaces</option>
                                    <option value="4" selected>4 Spaces</option>
                                    <option value="tab">Tabs</option>
                                </select>
                                <button id="formatJsonBtn" style="margin-left:auto;">Format JSON</button>
                            </div>
                            <label for="jsonOutput" style="display:block; margin-bottom:0.5rem;">Formatted JSON:
                                <button id="copyJsonBtn" style="float:right; padding:0.3em 0.6em; margin-top:-5px;">Copy</button>
                            </label>
                            <textarea id="jsonOutput" readonly rows="7"></textarea>
                        `;
                        const inputArea = container.querySelector('#jsonInput');
                        const outputArea = container.querySelector('#jsonOutput');
                        const formatBtn = container.querySelector('#formatJsonBtn');
                        const copyBtn = container.querySelector('#copyJsonBtn');
                        const spacesSelect = container.querySelector('#jsonSpaces');


                        formatBtn.onclick = () => {
                            const jsonString = inputArea.value.trim();
                            if (!jsonString) {
                                showAlert('Input is empty. Paste some JSON data.', 'info');
                                outputArea.value = '';
                                return;
                            }
                            try {
                                const jsonObj = JSON.parse(jsonString);
                                const spacesOption = spacesSelect.value;
                                let indent;
                                if (spacesOption === 'tab') {
                                    indent = '\t';
                                } else {
                                    indent = parseInt(spacesOption);
                                }
                                outputArea.value = JSON.stringify(jsonObj, null, indent);
                                showAlert('JSON formatted successfully!', 'success');
                            } catch (e) {
                                outputArea.value = 'Error: Invalid JSON\n\n' + e.message;
                                showAlert('Invalid JSON: ' + e.message, 'error');
                            }
                        };
                        copyBtn.onclick = () => {
                            if (outputArea.value && !outputArea.value.startsWith('Error:')) {
                                navigator.clipboard.writeText(outputArea.value)
                                    .then(() => showAlert('Formatted JSON copied!', 'success'))
                                    .catch(err => showAlert('Failed to copy.', 'error'));
                            } else if (outputArea.value.startsWith('Error:')) {
                                showAlert('Cannot copy error message. Please format valid JSON first.', 'info');
                            } else {
                                 showAlert('Nothing to copy. Format some JSON first.', 'info');
                            }
                        };
                    },


                    // --- Unit Converter ---
                    unitConverter: (container) => {
                        const units = {
                            length: { name: "Length", items: { meter: 1, kilometer: 1000, centimeter: 0.01, millimeter: 0.001, mile: 1609.34, yard: 0.9144, foot: 0.3048, inch: 0.0254, nautical_mile: 1852 } },
                            weight: { name: "Weight/Mass", items: { kilogram: 1, gram: 0.001, milligram: 0.000001, metric_ton: 1000, pound: 0.45359237, ounce: 0.0283495231, stone: 6.35029318 } },
                            temperature: { name: "Temperature", items: { celsius: 'celsius', fahrenheit: 'fahrenheit', kelvin: 'kelvin' } },
                            area: { name: "Area", items: { square_meter: 1, square_kilometer: 1000000, square_mile: 2589988.11, square_yard: 0.836127, square_foot: 0.092903, acre: 4046.86, hectare: 10000 } },
                            volume: { name: "Volume", items: { cubic_meter: 1, liter: 0.001, milliliter: 0.000001, US_gallon: 0.00378541, US_quart: 0.000946353, US_pint: 0.000473176, US_cup: 0.000236588, US_fluid_ounce: 0.0000295735, imperial_gallon: 0.00454609, imperial_quart: 0.00113652, imperial_pint: 0.000568261, imperial_fluid_ounce: 0.0000284131 } },
                            speed: { name: "Speed", items: { meters_per_second: 1, kilometers_per_hour: 0.277778, miles_per_hour: 0.44704, knot: 0.514444 } },
                            time: { name: "Time", items: { second: 1, minute: 60, hour: 3600, day: 86400, week: 604800, month_avg: 2629746, year_avg: 31556952 } },
                            // Add more categories like pressure, energy, power, data storage etc.
                        };


                        container.innerHTML = `
                            <label for="ucCategory">Category:</label>
                            <select id="ucCategory"></select>
                            <div style="display:flex; gap:10px; margin-top:10px; align-items:flex-end;">
                                <div style="flex:2">
                                    <label for="ucInputValue">Value:</label>
                                    <input type="number" id="ucInputValue" value="1">
                                </div>
                                <div style="flex:3">
                                    <label for="ucFromUnit">From:</label>
                                    <select id="ucFromUnit"></select>
                                </div>
                                <div style="font-size:1.5rem; padding-bottom:0.5rem;">⇄</div>
                                <div style="flex:3">
                                    <label for="ucToUnit">To:</label>
                                    <select id="ucToUnit"></select>
                                </div>
                            </div>
                            <div id="ucResult" class="result-area" style="margin-top:1rem; font-weight:bold; font-size:1.2rem; text-align:center;">Enter values and see result here</div>
                        `;


                        const categorySelect = container.querySelector('#ucCategory');
                        const fromUnitSelect = container.querySelector('#ucFromUnit');
                        const toUnitSelect = container.querySelector('#ucToUnit');
                        const valueInput = container.querySelector('#ucInputValue');
                        const resultDiv = container.querySelector('#ucResult');


                        function populateCategories() {
                            for (const categoryKey in units) {
                                const option = document.createElement('option');
                                option.value = categoryKey;
                                option.textContent = units[categoryKey].name;
                                categorySelect.appendChild(option);
                            }
                        }


                        function populateUnitOptions(categoryKey) {
                            const unitSet = units[categoryKey].items;
                            fromUnitSelect.innerHTML = '';
                            toUnitSelect.innerHTML = '';
                            let count = 0;
                            for (const unit in unitSet) {
                                const optionText = unit.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); // Prettify name
                                
                                const option1 = document.createElement('option');
                                option1.value = unit;
                                option1.textContent = optionText;
                                fromUnitSelect.appendChild(option1);


                                const option2 = document.createElement('option');
                                option2.value = unit;
                                option2.textContent = optionText;
                                toUnitSelect.appendChild(option2);
                                
                                if (count === 1 && Object.keys(unitSet).length > 1) { // Select second item for "To" unit by default if available
                                    option2.selected = true;
                                }
                                count++;
                            }
                             if (Object.keys(unitSet).length === 1) { // If only one unit, select it for "To" as well
                                 toUnitSelect.selectedIndex = 0;
                             }
                        }
                        
                        function convertUnits() {
                            const categoryKey = categorySelect.value;
                            const fromUnit = fromUnitSelect.value;
                            const toUnit = toUnitSelect.value;
                            const inputValue = parseFloat(valueInput.value);


                            if (isNaN(inputValue)) {
                                resultDiv.textContent = 'Invalid input value.';
                                resultDiv.style.color = 'var(--accent-color)';
                                return;
                            }
                            
                            hideAlert();
                            let result;
                            const unitFactors = units[categoryKey].items;


                            if (categoryKey === 'temperature') {
                                if (fromUnit === toUnit) { result = inputValue; }
                                else if (fromUnit === 'celsius') {
                                    if (toUnit === 'fahrenheit') result = (inputValue * 9/5) + 32;
                                    else if (toUnit === 'kelvin') result = inputValue + 273.15;
                                } else if (fromUnit === 'fahrenheit') {
                                    if (toUnit === 'celsius') result = (inputValue - 32) * 5/9;
                                    else if (toUnit === 'kelvin') result = (inputValue - 32) * 5/9 + 273.15;
                                } else if (fromUnit === 'kelvin') {
                                    if (toUnit === 'celsius') result = inputValue - 273.15;
                                    else if (toUnit === 'fahrenheit') result = (inputValue - 273.15) * 9/5 + 32;
                                }
                            } else {
                                const baseValue = inputValue * unitFactors[fromUnit];
                                result = baseValue / unitFactors[toUnit];
                            }
                            
                            if (typeof result === 'undefined') {
                                 resultDiv.textContent = 'Conversion not supported or error.';
                                 resultDiv.style.color = 'var(--accent-color)';
                            } else {
                                const fromUnitText = fromUnitSelect.options[fromUnitSelect.selectedIndex].text;
                                const toUnitText = toUnitSelect.options[toUnitSelect.selectedIndex].text;
                                // Use more decimal places for precision, especially for small numbers
                                const resultPrecision = Math.abs(result) > 0.0001 || result === 0 ? 4 : 8;
                                resultDiv.textContent = `${inputValue} ${fromUnitText} = ${result.toFixed(resultPrecision)} ${toUnitText}`;
                                resultDiv.style.color = 'var(--text-color)';
                            }
                        }


                        categorySelect.onchange = () => {
                            populateUnitOptions(categorySelect.value);
                            convertUnits(); // Convert immediately on category change
                        };
                        fromUnitSelect.onchange = convertUnits;
                        toUnitSelect.onchange = convertUnits;
                        valueInput.oninput = convertUnits;
                        
                        populateCategories();
                        populateUnitOptions(categorySelect.value); // Initial population for default category
                        convertUnits(); // Initial conversion
                    },


                    // --- BMI Calculator ---
                    bmiCalculator: (container) => {
                        container.innerHTML = `
                            <label for="bmiWeight">Weight (kg):</label>
                            <input type="number" id="bmiWeight" placeholder="e.g., 70" min="0">
                            <label for="bmiHeight">Height (cm):</label>
                            <input type="number" id="bmiHeight" placeholder="e.g., 175" min="0">
                            <button id="calculateBmiBtn">Calculate BMI</button>
                            <div id="bmiResult" class="result-area" style="display:none; text-align:center;"></div>
                        `;
                        const weightInput = container.querySelector('#bmiWeight');
                        const heightInput = container.querySelector('#bmiHeight');
                        const calculateBtn = container.querySelector('#calculateBmiBtn');
                        const resultDiv = container.querySelector('#bmiResult');


                        calculateBtn.onclick = () => {
                            const weight = parseFloat(weightInput.value);
                            const heightCm = parseFloat(heightInput.value);


                            if (isNaN(weight) || isNaN(heightCm) || weight <= 0 || heightCm <= 0) {
                                showAlert('Please enter valid positive numbers for weight and height.', 'error');
                                resultDiv.style.display = 'none';
                                return;
                            }
                            const heightM = heightCm / 100;
                            const bmi = weight / (heightM * heightM);
                            let category = '';
                            let color = 'var(--text-color)';


                            if (bmi < 18.5) { category = 'Underweight'; color = '#3498db'; } // Blue
                            else if (bmi < 24.9) { category = 'Normal weight'; color = '#2ecc71'; } // Green
                            else if (bmi < 29.9) { category = 'Overweight'; color = '#f1c40f'; } // Yellow
                            else if (bmi < 34.9) { category = 'Obesity Class I'; color = '#e67e22'; } // Orange
                            else if (bmi < 39.9) { category = 'Obesity Class II'; color = '#e74c3c'; } // Red
                            else { category = 'Obesity Class III (Severe)'; color = '#c0392b'; } // Darker Red




                            resultDiv.innerHTML = `Your BMI: <strong style="font-size:1.5em;">${bmi.toFixed(2)}</strong><br>
                                                Category: <strong style="color:${color};">${category}</strong>`;
                            resultDiv.style.display = 'block';
                            hideAlert();
                        };
                    },


                    // --- Timer / Stopwatch ---
                    timerStopwatch: (container) => {
                        container.innerHTML = `
                            <div class="tabs">
                                <button class="tab-button active" data-tab="timerTab">Timer</button>
                                <button class="tab-button" data-tab="stopwatchTab">Stopwatch</button>
                            </div>


                            <div id="timerTab" class="tab-content" style="padding-top:1rem;">
                                <h3>Timer</h3>
                                <div style="display:flex; justify-content:space-around; margin-bottom:1rem;">
                                    <div><label for="timerHours">Hours:</label><br><input type="number" id="timerHours" min="0" max="99" value="0" style="width:60px;"></div>
                                    <div><label for="timerMinutes">Mins:</label><br><input type="number" id="timerMinutes" min="0" max="59" value="5" style="width:60px;"></div>
                                    <div><label for="timerSeconds">Secs:</label><br><input type="number" id="timerSeconds" min="0" max="59" value="0" style="width:60px;"></div>
                                </div>
                                <div class="timer-display" id="timerDisplay">00:05:00</div>
                                <div style="text-align:center;">
                                    <button id="timerStart">Start</button>
                                    <button id="timerPause" disabled>Pause</button>
                                    <button id="timerReset">Reset</button>
                                </div>
                            </div>


                            <div id="stopwatchTab" class="tab-content" style="display:none; padding-top:1rem;">
                                <h3>Stopwatch</h3>
                                <div class="stopwatch-display" id="stopwatchDisplay">00:00:00.00</div>
                                <div style="text-align:center; margin-bottom:1rem;">
                                    <button id="stopwatchStart">Start</button>
                                    <button id="stopwatchStop" disabled>Stop</button>
                                    <button id="stopwatchReset" disabled>Reset</button>
                                    <button id="stopwatchLap" disabled>Lap</button>
                                </div>
                                <ul id="lapsList" class="laps-list"></ul>
                            </div>
                        `;


                        const tabButtons = container.querySelectorAll('.tab-button');
                        const tabContents = container.querySelectorAll('.tab-content');
                        tabButtons.forEach(button => {
                            button.onclick = () => {
                                tabButtons.forEach(btn => btn.classList.remove('active'));
                                button.classList.add('active');
                                tabContents.forEach(content => content.style.display = 'none');
                                container.querySelector(`#${button.dataset.tab}`).style.display = 'block';
                                if (button.dataset.tab === 'timerTab') resetTimerState(false); else resetStopwatchState(false);
                            };
                        });


                        // Timer Logic
                        const timerHoursInput = container.querySelector('#timerHours');
                        const timerMinutesInput = container.querySelector('#timerMinutes');
                        const timerSecondsInput = container.querySelector('#timerSeconds');
                        const timerDisplay = container.querySelector('#timerDisplay');
                        const timerStartBtn = container.querySelector('#timerStart');
                        const timerPauseBtn = container.querySelector('#timerPause');
                        const timerResetBtn = container.querySelector('#timerReset');
                        let timerInterval;
                        let timerTotalSeconds;
                        let timerIsRunning = false;


                        function updateTimerDisplayDOM() {
                            const h = Math.floor(timerTotalSeconds / 3600);
                            const m = Math.floor((timerTotalSeconds % 3600) / 60);
                            const s = timerTotalSeconds % 60;
                            timerDisplay.textContent = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
                        }
                        
                        function setTimerFromInputs() {
                            const h = parseInt(timerHoursInput.value) || 0;
                            const m = parseInt(timerMinutesInput.value) || 0;
                            const s = parseInt(timerSecondsInput.value) || 0;
                            timerTotalSeconds = (h * 3600) + (m * 60) + s;
                            updateTimerDisplayDOM();
                        }
                        [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(input => {
                            input.onchange = () => { if (!timerIsRunning) setTimerFromInputs(); };
                            input.onkeyup = () => { if (!timerIsRunning) setTimerFromInputs(); }; // For immediate update
                        });


                        timerStartBtn.onclick = () => {
                            if (timerIsRunning) return; // Already running
                            if (timerTotalSeconds === undefined || timerTotalSeconds === 0) setTimerFromInputs(); // Set if not set from pause or initially 0


                            if (timerTotalSeconds <= 0) {
                                showAlert('Set a duration greater than 0.', 'error');
                                return;
                            }
                            hideAlert();
                            timerIsRunning = true;
                            timerStartBtn.disabled = true;
                            timerPauseBtn.disabled = false;
                            [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = true);


                            timerInterval = setInterval(() => {
                                if (timerTotalSeconds > 0) {
                                    timerTotalSeconds--;
                                    updateTimerDisplayDOM();
                                } else {
                                    clearInterval(timerInterval);
                                    timerIsRunning = false;
                                    timerDisplay.textContent = "Time's Up!";
                                    showAlert("Timer finished!", "success");
                                    timerStartBtn.disabled = false;
                                    timerPauseBtn.disabled = true;
                                    [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false);
                                    try { new Audio('https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3').play(); } catch(e){} // Example sound
                                }
                            }, 1000);
                        };
                        timerPauseBtn.onclick = () => {
                            clearInterval(timerInterval);
                            timerIsRunning = false;
                            timerStartBtn.disabled = false;
                            timerPauseBtn.disabled = true;
                             [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false); // Re-enable inputs on pause
                        };
                        
                        function resetTimerState(showDefaultTime = true) {
                            clearInterval(timerInterval);
                            timerIsRunning = false;
                            if (showDefaultTime) {
                                timerHoursInput.value = '0';
                                timerMinutesInput.value = '5';
                                timerSecondsInput.value = '0';
                            }
                            setTimerFromInputs();
                            timerStartBtn.disabled = false;
                            timerPauseBtn.disabled = true;
                            [timerHoursInput, timerMinutesInput, timerSecondsInput].forEach(inp => inp.disabled = false);
                            hideAlert();
                            timerDisplay.textContent = "00:05:00"; // Reset visual
                            if (showDefaultTime) setTimerFromInputs(); // Recalculate timerTotalSeconds
                        }
                        timerResetBtn.onclick = () => resetTimerState(true);
                        


                        // Stopwatch Logic
                        const stopwatchDisplay = container.querySelector('#stopwatchDisplay');
                        const stopwatchStartBtn = container.querySelector('#stopwatchStart');
                        const stopwatchStopBtn = container.querySelector('#stopwatchStop');
                        const stopwatchResetBtn = container.querySelector('#stopwatchReset');
                        const stopwatchLapBtn = container.querySelector('#stopwatchLap');
                        const lapsList = container.querySelector('#lapsList');
                        let stopwatchInterval;
                        let stopwatchStartTime;
                        let stopwatchElapsedTime = 0;
                        let lapNumber = 1;
                        let stopwatchIsRunning = false;


                        function formatStopwatchTime(ms) {
                            const totalSeconds = Math.floor(ms / 1000);
                            const minutes = Math.floor(totalSeconds / 60);
                            const seconds = totalSeconds % 60;
                            const milliseconds = Math.floor((ms % 1000)/10);
                            return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(2, '0')}`;
                        }


                        stopwatchStartBtn.onclick = () => {
                            if (stopwatchIsRunning) return;
                            stopwatchIsRunning = true;
                            stopwatchStartTime = Date.now() - stopwatchElapsedTime;
                            stopwatchInterval = setInterval(() => {
                                stopwatchElapsedTime = Date.now() - stopwatchStartTime;
                                stopwatchDisplay.textContent = formatStopwatchTime(stopwatchElapsedTime);
                            }, 10);
                            stopwatchStartBtn.disabled = true;
                            stopwatchStopBtn.disabled = false;
                            stopwatchLapBtn.disabled = false;
                            stopwatchResetBtn.disabled = false; // Enable reset when running or paused
                        };
                        stopwatchStopBtn.onclick = () => {
                            clearInterval(stopwatchInterval);
                            stopwatchIsRunning = false;
                            stopwatchStartBtn.disabled = false;
                            stopwatchStopBtn.disabled = true;
                            // Lap button can be active if paused with time
                            stopwatchLapBtn.disabled = stopwatchElapsedTime === 0;
                        };
                        function resetStopwatchState(fromTabSwitch = false) {
                            clearInterval(stopwatchInterval);
                            stopwatchIsRunning = false;
                            stopwatchElapsedTime = 0;
                            lapNumber = 1;
                            stopwatchDisplay.textContent = formatStopwatchTime(0);
                            if (!fromTabSwitch) lapsList.innerHTML = ''; // Clear laps only on explicit reset
                            stopwatchStartBtn.disabled = false;
                            stopwatchStopBtn.disabled = true;
                            stopwatchLapBtn.disabled = true;
                            stopwatchResetBtn.disabled = true; // Disabled when at 00:00
                        }
                        stopwatchResetBtn.onclick = () => resetStopwatchState(false);
                        
                        stopwatchLapBtn.onclick = () => {
                            if (stopwatchElapsedTime > 0) {
                                const lapTime = stopwatchElapsedTime;
                                const li = document.createElement('li');
                                li.textContent = `Lap ${lapNumber++}: ${formatStopwatchTime(lapTime)}`;
                                lapsList.prepend(li);
                            }
                        };
                        
                        resetTimerState(true); // Initial call for timer
                        resetStopwatchState(false); // Initial call for stopwatch


                        window.currentToolCleanup = () => {
                            clearInterval(timerInterval);
                            clearInterval(stopwatchInterval);
                        };
                    }                    
                };


                // Initial call for the default active tab (Timer) in Timer/Stopwatch if it's the first tool
                // This is more robustly handled by the tab switching logic now.
                // Ensure timer tab is default and its UI is correctly initialized
                const defaultTimerTabButton = document.querySelector('.tab-button[data-tab="timerTab"]');
                if (defaultTimerTabButton) defaultTimerTabButton.click();




            });
        </script>
</body>
</html>

इस दिए हुए HTML script का इस्तेमाल कैसे करना है, वह भी मैंने मेरे video में बताया है। आप उसे ज़रूर देखें और इस तरह से आप अपने Blogger post पर tools converter website बना पाएंगे।

Conclusion

आप चाहें तो ad network platform का इस्तेमाल भी कर सकते हैं और इन tools पर आप traffic लाएं और advertisement platform की सहायता से अपने content को monetize करें, जिससे आपको revenue भी मिलेगा।

Leave a Comment

Your email address will not be published. Required fields are marked *