<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Webapps &#8211; Tech AI Connect</title>
	<atom:link href="https://techaiconnect.com/webapps/feed/" rel="self" type="application/rss+xml" />
	<link>https://techaiconnect.com</link>
	<description>All Tek Information for You</description>
	<lastBuildDate>Sat, 05 Jul 2025 14:48:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.2</generator>
	<item>
		<title>Spin the wheel</title>
		<link>https://techaiconnect.com/spin-the-wheel/</link>
					<comments>https://techaiconnect.com/spin-the-wheel/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:48:34 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4268</guid>

					<description><![CDATA[Spin the Wheel Spin the Wheel Make every decision fun! Click to spin! SPIN! Entries [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spin the Wheel</title>
    <script src="https://cdn.tailwindcss.com"></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=Inter:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        #wheel-container {
            position: relative;
            width: 100%;
            max-width: 500px;
            height: auto;
            aspect-ratio: 1 / 1;
            margin: 0 auto;
        }
        #wheel-canvas {
            width: 100%;
            height: 100%;
            transition: transform 8s cubic-bezier(0.25, 0.1, 0.25, 1);
            border-radius: 50%;
        }
        #pointer {
            position: absolute;
            top: 50%;
            right: -10px;
            transform: translateY(-50%);
            width: 0;
            height: 0;
            border-top: 20px solid transparent;
            border-bottom: 20px solid transparent;
            border-left: 30px solid #facc15; /* yellow-400 */
            z-index: 10;
        }
        #wheel-overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            font-size: 2rem;
            font-weight: 900;
            color: white;
            text-shadow: 0 0 10px rgba(0,0,0,0.5);
            pointer-events: none; /* Allows clicking through to the canvas */
            opacity: 1;
            transition: opacity 0.3s;
        }
        #wheel-overlay.hidden {
            opacity: 0;
        }
        /* Custom scrollbar for textarea */
        #entries-input::-webkit-scrollbar {
            width: 8px;
        }
        #entries-input::-webkit-scrollbar-track {
            background: #1f2937; /* gray-800 */
        }
        #entries-input::-webkit-scrollbar-thumb {
            background: #4b5563; /* gray-600 */
            border-radius: 10px;
        }
        #entries-input::-webkit-scrollbar-thumb:hover {
            background: #6b7280; /* gray-500 */
        }
    </style>
</head>
<body class="p-4 md:p-8">

    <div class="container mx-auto max-w-6xl">
        <!-- Header -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-white">Spin the Wheel</h1>
            <p class="text-gray-400 mt-2 text-lg">Make every decision fun!</p>
        </header>

        <main class="grid grid-cols-1 lg:grid-cols-2 gap-8">
            <!-- Left Column: Wheel -->
            <section class="flex flex-col items-center justify-center space-y-6">
                <div id="wheel-container">
                    <div id="pointer"></div>
                    <canvas id="wheel-canvas" width="500" height="500"></canvas>
                    <div id="wheel-overlay">
                        <p>Click to spin!</p>
                    </div>
                </div>
                <button id="spin-btn" class="bg-gradient-to-br from-yellow-400 to-orange-500 text-gray-900 font-bold py-4 px-16 rounded-full text-xl transition-all transform hover:scale-105 shadow-lg hover:shadow-yellow-400/50 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100">
                    SPIN!
                </button>
                <div id="result-display" class="mt-4 text-center h-12 text-2xl font-bold">
                    <!-- Result will be shown here -->
                </div>
            </section>

            <!-- Right Column: Customization -->
            <section class="bg-gray-800 p-6 rounded-2xl shadow-lg border border-gray-700 flex flex-col">
                <h2 class="text-2xl font-bold mb-4 text-center text-white">Entries</h2>
                
                <!-- Action Buttons -->
                <div class="flex items-center space-x-2 mb-4">
                     <button id="shuffle-btn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-semibold py-2 px-4 rounded-lg transition text-sm">Shuffle</button>
                     <button id="sort-btn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-semibold py-2 px-4 rounded-lg transition text-sm">Sort A-Z</button>
                </div>

                <!-- Custom Input -->
                <div>
                    <label for="entries-input" class="block mb-2 text-sm font-medium text-gray-400">Enter choices (one per line):</label>
                    <textarea id="entries-input" rows="12" class="block p-3 w-full text-sm text-gray-200 bg-gray-700 rounded-lg border border-gray-600 focus:ring-yellow-500 focus:border-yellow-500"></textarea>
                </div>
                
                <!-- Update Button -->
                <div class="mt-4">
                     <button id="update-wheel-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition">
                        Update Wheel
                    </button>
                </div>

                <!-- Presets -->
                <div class="mt-6 border-t border-gray-700 pt-6">
                    <label for="preset-select" class="block mb-2 text-sm font-medium text-gray-400">Or load a preset list:</label>
                    <select id="preset-select" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-yellow-500 focus:border-yellow-500 block w-full p-2.5">
                        <!-- Options will be populated by JS -->
                    </select>
                </div>
            </section>
        </main>
    </div>

    <!-- Winner Modal -->
    <div id="winner-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden">
        <div class="bg-gray-800 rounded-2xl shadow-2xl p-8 text-center max-w-md w-full border border-yellow-400 transform transition-all scale-95 opacity-0">
            <h2 class="text-2xl font-bold text-gray-400 mb-2">The Winner is...</h2>
            <p id="winner-text" class="text-5xl font-black text-yellow-400 my-6 break-words"></p>
            <button id="close-modal-btn" class="mt-4 bg-yellow-500 text-gray-900 font-bold py-2 px-8 rounded-lg">Close</button>
        </div>
    </div>


    <script>
        // --- PRESET DATA ---
        const presets = {
            '-- Custom Input --': [],
            'Lunch Ideas': ['Pizza', 'Burger & Fries', 'Sushi', 'Tacos', 'Pasta', 'Salad', 'Vietnamese Pho', 'Ramen Noodles'],
            'Pick a Color': ['Black', 'White', 'Blue', 'Gray', 'Beige', 'Brown', 'Green', 'Pastel Pink'],
            'Team Picker': ['Team A', 'Team B', 'Team C', 'Team D'],
            'Workout Routine': ['Push-ups', 'Squats', 'Plank', 'Lunges', 'Burpees', 'Jumping Jacks', 'Crunches', 'Rest Day'],
            'Movie Night Genre': ['Comedy', 'Horror', 'Sci-Fi', 'Action', 'Drama', 'Thriller', 'Documentary', 'Animation'],
            'Prize Wheel': ['10% Off', '20% Off', '50% Off', 'Buy 1 Get 1', 'Free Shipping', '$5 Voucher', '$10 Voucher', 'Try Again!'],
        };

        // --- DOM ELEMENTS ---
        const canvas = document.getElementById('wheel-canvas');
        const ctx = canvas.getContext('2d');
        const spinBtn = document.getElementById('spin-btn');
        const resultDisplay = document.getElementById('result-display');
        const presetSelect = document.getElementById('preset-select');
        const entriesInput = document.getElementById('entries-input');
        const updateWheelBtn = document.getElementById('update-wheel-btn');
        const shuffleBtn = document.getElementById('shuffle-btn');
        const sortBtn = document.getElementById('sort-btn');
        const wheelOverlay = document.getElementById('wheel-overlay');
        const winnerModal = document.getElementById('winner-modal');
        const winnerText = document.getElementById('winner-text');
        const closeModalBtn = document.getElementById('close-modal-btn');

        // --- WHEEL STATE ---
        let segments = [];
        let currentRotation = 0;
        let isSpinning = false;
        const colors = ["#ef4444", "#f97316", "#eab308", "#84cc16", "#22c55e", "#14b8a6", "#06b6d4", "#3b82f6", "#8b5cf6", "#d946ef"];

        // --- FUNCTIONS ---
        
        function drawWheel() {
            const numSegments = segments.length;
            spinBtn.disabled = numSegments < 2;

            if (numSegments === 0) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                 // Draw a placeholder wheel if empty
                ctx.beginPath();
                ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2 - 5, 0, 2 * Math.PI);
                ctx.fillStyle = '#374151'; // gray-700
                ctx.fill();
                wheelOverlay.classList.remove('hidden');
                wheelOverlay.innerHTML = `<p class="text-lg">Add entries on the right to build the wheel!</p>`;
                return;
            };
            
            wheelOverlay.classList.remove('hidden');
            wheelOverlay.innerHTML = `<p>Click to spin!</p>`;

            const anglePerSegment = (2 * Math.PI) / numSegments;
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const radius = canvas.width / 2 - 5; // small padding

            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            segments.forEach((segment, i) => {
                const startAngle = i * anglePerSegment;
                const endAngle = startAngle + anglePerSegment;

                // Draw segment
                ctx.beginPath();
                ctx.moveTo(centerX, centerY);
                ctx.arc(centerX, centerY, radius, startAngle, endAngle);
                ctx.closePath();
                ctx.fillStyle = colors[i % colors.length];
                ctx.fill();

                // Draw segment border
                ctx.save();
                ctx.strokeStyle = '#111827'; // gray-900
                ctx.lineWidth = 4;
                ctx.stroke();
                ctx.restore();

                // Draw text
                ctx.save();
                ctx.translate(centerX, centerY);
                ctx.rotate(startAngle + anglePerSegment / 2);
                ctx.textAlign = "right";
                ctx.fillStyle = "#ffffff";
                ctx.font = getFontSize(segment, radius);

                let text = segment;
                if (text.length > 20) {
                    text = text.substring(0, 18) + '...';
                }
                ctx.fillText(text, radius - 15, 10);
                ctx.restore();
            });
        }

        function getFontSize(text, radius) {
            const baseSize = 20;
            const maxLength = 10;
            if (text.length > maxLength) {
                return `bold ${Math.max(baseSize - (text.length - maxLength), 12)}px Inter`;
            }
            return `bold ${baseSize}px Inter`;
        }
        
        function populatePresets() {
            for (const key in presets) {
                const option = document.createElement('option');
                option.value = key;
                option.textContent = key;
                presetSelect.appendChild(option);
            }
        }

        function loadPreset() {
            const selectedKey = presetSelect.value;
            const items = presets[selectedKey];
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }
        
        function updateWheelFromTextarea() {
            const items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            
            if (items.length > 100) { // Limit entries to prevent performance issues
                alert("Please limit the number of entries to 100.");
                items = items.slice(0, 100);
                entriesInput.value = items.join('\n');
            }

            segments = items;
            currentRotation = 0;
            canvas.style.transform = `rotate(0deg)`;
            drawWheel();
            resultDisplay.innerHTML = '';
        }

        function spin() {
            if (isSpinning || segments.length < 2) return;
            isSpinning = true;
            spinBtn.disabled = true;
            resultDisplay.innerHTML = '';
            wheelOverlay.classList.add('hidden');

            const randomSpins = Math.floor(Math.random() * 5) + 8; // 8 to 12 full spins
            const randomAngle = Math.random() * 360;
            const totalRotation = randomSpins * 360 + randomAngle;
            const finalRotation = currentRotation + totalRotation;

            canvas.style.transform = `rotate(${finalRotation}deg)`;

            setTimeout(() => {
                isSpinning = false;
                spinBtn.disabled = false;
                currentRotation = finalRotation % 360;
                
                const numSegments = segments.length;
                const anglePerSegment = 360 / numSegments;
                
                // The pointer is at 0 degrees (right side). The wheel spins clockwise.
                // We need to find which segment is at 0 degrees.
                const winningAngle = (360 - currentRotation) % 360;
                const winningIndex = Math.floor(winningAngle / anglePerSegment);
                const winner = segments[winningIndex];

                if (winner) {
                    showWinnerModal(winner);
                }
               
            }, 8000); // Corresponds to the transition duration in CSS
        }

        function showWinnerModal(winner) {
            winnerText.textContent = winner;
            winnerModal.classList.remove('hidden');
            setTimeout(() => {
                winnerModal.querySelector('div').classList.remove('scale-95', 'opacity-0');
                winnerModal.querySelector('div').classList.add('scale-100', 'opacity-100');
            }, 10);
        }

        function hideWinnerModal() {
             winnerModal.querySelector('div').classList.add('scale-95', 'opacity-0');
             winnerModal.querySelector('div').classList.remove('scale-100', 'opacity-100');
             setTimeout(() => {
                winnerModal.classList.add('hidden');
                wheelOverlay.classList.remove('hidden');
             }, 300);
        }

        function shuffleEntries() {
            let items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            for (let i = items.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [items[i], items[j]] = [items[j], items[i]];
            }
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }

        function sortEntries() {
            let items = entriesInput.value.split('\n').map(item => item.trim()).filter(item => item !== '');
            items.sort((a, b) => a.localeCompare(b));
            entriesInput.value = items.join('\n');
            updateWheelFromTextarea();
        }

        // --- EVENT LISTENERS ---
        document.addEventListener('DOMContentLoaded', () => {
            populatePresets();
            // Set a default preset on load
            presetSelect.value = 'Lunch Ideas'; 
            loadPreset();
        });

        spinBtn.addEventListener('click', spin);
        canvas.addEventListener('click', spin); // Allow clicking the wheel itself
        updateWheelBtn.addEventListener('click', updateWheelFromTextarea);
        presetSelect.addEventListener('change', loadPreset);
        shuffleBtn.addEventListener('click', shuffleEntries);
        sortBtn.addEventListener('click', sortEntries);
        closeModalBtn.addEventListener('click', hideWinnerModal);
        winnerModal.addEventListener('click', (e) => {
            if (e.target === winnerModal) {
                hideWinnerModal();
            }
        });
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                hideWinnerModal();
            }
        });

    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/spin-the-wheel/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Boiled egg timer</title>
		<link>https://techaiconnect.com/boiled-egg-timer/</link>
					<comments>https://techaiconnect.com/boiled-egg-timer/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:33:24 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4265</guid>

					<description><![CDATA[Egg Boiling Timer - The Perfect Egg Every Time The Perfect Egg Timer Select your [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Egg Boiling Timer - The Perfect Egg Every Time</title>
    <meta name="description" content="The perfect egg boiling timer with a countdown clock. Easily boil soft, medium, or hard-boiled eggs with our preset times and simple guide.">
    <meta name="keywords" content="egg timer, boil egg, how to boil an egg, boiling time, countdown timer, soft boiled egg, hard boiled egg">
    
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Tone.js for sound -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.7.77/Tone.js"></script>
    <!-- Google Fonts: Inter & Roboto Mono -->
    <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=Inter:wght@400;500;600;700;800&family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        .timer-display {
            font-family: 'Roboto Mono', monospace;
        }
        .preset-btn {
            transition: all 0.2s ease-in-out;
            border: 2px solid transparent;
        }
        .preset-btn.selected {
            border-color: #3b82f6; /* blue-500 */
            transform: scale(1.05);
            box-shadow: 0 0 15px rgba(59, 130, 246, 0.4);
            background-color: #3b82f6; /* blue-500 */
            color: white;
        }
        .progress-ring__circle {
            transition: stroke-dashoffset 1s linear;
            transform: rotate(-90deg);
            transform-origin: 50% 50%;
        }
        .prose {
            max-width: 65ch;
            margin-left: auto;
            margin-right: auto;
        }
    </style>
</head>
<body class="text-gray-300">

    <div class="container mx-auto p-4 sm:p-8 max-w-4xl">
        <!-- Header -->
        <header class="text-center mb-10">
            <h1 class="text-3xl sm:text-5xl font-extrabold text-white">The Perfect Egg Timer</h1>
            <p class="text-gray-400 mt-2 text-lg">Select your desired doneness and start the timer!</p>
        </header>

        <!-- Main Timer Section -->
        <div class="bg-gray-800 p-6 sm:p-8 rounded-2xl shadow-2xl mb-12">
            <!-- Timer Display -->
            <div class="relative w-48 h-48 sm:w-64 sm:h-64 mx-auto mb-6">
                <svg class="w-full h-full" viewBox="0 0 120 120">
                    <circle class="text-gray-700" stroke-width="8" stroke="currentColor" fill="transparent" r="52" cx="60" cy="60" />
                    <circle id="progress-ring" class="progress-ring__circle text-blue-500" stroke-width="8" stroke-linecap="round" stroke="currentColor" fill="transparent" r="52" cx="60" cy="60" />
                </svg>
                <div id="timer-display" class="absolute inset-0 flex items-center justify-center timer-display text-4xl sm:text-6xl font-bold text-white">00:00</div>
            </div>

            <!-- Preset Buttons -->
            <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="240">
                    <span class="font-bold text-lg">4 mins</span>
                    <span class="block text-sm">Soft Boiled</span>
                </button>
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="360">
                    <span class="font-bold text-lg">6 mins</span>
                    <span class="block text-sm">Medium Boiled</span>
                </button>
                <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="480">
                    <span class="font-bold text-lg">8 mins</span>
                    <span class="block text-sm">Hard Boiled</span>
                </button>
                 <button class="preset-btn bg-gray-700 hover:bg-gray-600 p-4 rounded-lg" data-time="600">
                    <span class="font-bold text-lg">10 mins</span>
                    <span class="block text-sm">Very Hard Boiled</span>
                </button>
            </div>

            <!-- Control Buttons -->
            <div class="grid grid-cols-2 gap-4">
                <button id="start-pause-btn" class="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg text-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors">Start</button>
                <button id="reset-btn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg text-lg transition-colors">Reset</button>
            </div>
        </div>

        <!-- SEO Content & Instructions -->
        <article class="prose prose-lg prose-invert">
            <h2 class="text-white">How to Boil an Egg: Your Ultimate Guide</h2>
            <p>Boiled eggs are a quick and easy way to bulk up any meal, particularly for vegetarians. But many of us still struggle with how to make the perfect boiled egg. Timing is everything, whether you prefer a soft, runny yolk for dipping or a firm, hard-boiled egg for salads.</p>

            <h3>How Long to Boil an Egg?</h3>
            <p>Use our timer above or follow this guide for perfect eggs every time. The timings are for large eggs starting in cold water.</p>
            <ul>
                <li><strong>3 minutes</strong> for a very soft boiled yolk and set white.</li>
                <li><strong>4 minutes</strong> for a slightly set, runny yolk and set white.</li>
                <li><strong>5 minutes</strong> for a medium cooked, firmer yolk and white.</li>
                <li><strong>6 minutes</strong> for hard boiled with a slightly soft yolk.</li>
                <li><strong>8 minutes</strong> for a firmly hard-boiled egg.</li>
            </ul>

            <h3>Ingredients</h3>
            <ul>
                <li>2 large eggs</li>
                <li>Water for boiling</li>
                <li>Pinch of salt</li>
                <li>Buttered toast cut into soldiers to serve (optional)</li>
            </ul>

            <h3>Method</h3>
            <ol>
                <li>Place the large eggs in a small saucepan. Cover with at least 1 inch (2.5cm) of cold water.</li>
                <li>Add a pinch of salt and place the pan on high heat.</li>
                <li>When the water comes to a rolling boil, reduce the heat slightly to maintain a steady simmer. Start your timer now.</li>
                <li>Once the cooking time is complete, remove the eggs from the pan with a slotted spoon.</li>
                <li>Immediately place the eggs in a bowl of ice-cold water for at least 5 minutes. This stops the cooking process and makes them easier to peel.</li>
                <li>Serve immediately in an egg cup with toast soldiers or peel for use in other recipes.</li>
            </ol>

            <h3>Cook's Tips</h3>
            <ul>
                <li><strong>Prevent Cracking:</strong> To prevent the shell from cracking during boiling, you can make a small pinprick in the rounded end of the shell to allow steam to escape.</li>
                <li><strong>Hot or Cold Water Start?</strong> While there's much debate, starting with cold water and bringing it to a boil with the eggs inside allows for more even cooking and can help prevent cracking.</li>
                <li><strong>Easy Peeling:</strong> Slightly older eggs (about a week old) are often easier to peel than very fresh ones. The cold water shock after boiling is the most crucial step for easy peeling.</li>
            </ul>

            <h3>How to Peel a Hard-Boiled Egg</h3>
            <ol>
                <li>Gently tap both ends of the egg on a hard surface until they crack. This helps release the pressure.</li>
                <li>Roll the egg gently on the counter to create more cracks all over the shell.</li>
                <li>Start peeling from the wider bottom end of the egg, where the air pocket is. The shell and the thin membrane should come off more easily.</li>
            </ol>
        </article>
    </div>

    <script>
        // DOM Elements
        const timerDisplay = document.getElementById('timer-display');
        const progressRing = document.getElementById('progress-ring');
        const startPauseBtn = document.getElementById('start-pause-btn');
        const resetBtn = document.getElementById('reset-btn');
        const presetBtns = document.querySelectorAll('.preset-btn');
        
        // Timer state
        let timerInterval;
        let totalSeconds = 0;
        let remainingSeconds = 0;
        let isPaused = false;
        let isRunning = false;
        
        // Progress Ring setup
        const radius = progressRing.r.baseVal.value;
        const circumference = radius * 2 * Math.PI;
        progressRing.style.strokeDasharray = `${circumference} ${circumference}`;
        progressRing.style.strokeDashoffset = circumference;

        // Function to update the progress ring
        function setProgress(percent) {
            const offset = circumference - (percent / 100) * circumference;
            progressRing.style.strokeDashoffset = offset;
        }

        // Function to format time as MM:SS
        function formatTime(seconds) {
            const mins = Math.floor(seconds / 60);
            const secs = seconds % 60;
            return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
        }
        
        // Function to update the display
        function updateDisplay() {
            timerDisplay.textContent = formatTime(remainingSeconds);
            if (totalSeconds > 0) {
                const percent = ((totalSeconds - remainingSeconds) / totalSeconds) * 100;
                setProgress(percent);
            } else {
                setProgress(0);
            }
        }

        // Function to start the timer
        function startTimer() {
            if (isRunning && !isPaused) return;

            isRunning = true;
            isPaused = false;
            startPauseBtn.textContent = 'Pause';
            
            const endTime = Date.now() + remainingSeconds * 1000;

            timerInterval = setInterval(() => {
                remainingSeconds = Math.round((endTime - Date.now()) / 1000);
                
                if (remainingSeconds < 0) {
                    finishTimer();
                    return;
                }
                updateDisplay();
            }, 500); // Update twice a second for smoother display
        }
        
        // Function to pause the timer
        function pauseTimer() {
            clearInterval(timerInterval);
            isPaused = true;
            startPauseBtn.textContent = 'Resume';
        }

        // Function to reset the timer
        function resetTimer() {
            clearInterval(timerInterval);
            isRunning = false;
            isPaused = false;
            remainingSeconds = totalSeconds;
            startPauseBtn.textContent = 'Start';
            startPauseBtn.disabled = totalSeconds === 0;
            updateDisplay();
        }
        
        // Function to handle timer completion
        function finishTimer() {
             clearInterval(timerInterval);
             isRunning = false;
             isPaused = false;
             timerDisplay.textContent = "Done!";
             startPauseBtn.textContent = 'Start';
             startPauseBtn.disabled = true;
             setProgress(100);
             playSound();
        }

        // Function to play a notification sound
        function playSound() {
            // Use Tone.js to play a simple melody
            const synth = new Tone.Synth().toDestination();
            const now = Tone.now();
            synth.triggerAttackRelease("C5", "8n", now);
            synth.triggerAttackRelease("G5", "8n", now + 0.2);
            synth.triggerAttackRelease("C6", "8n", now + 0.4);
        }

        // --- Event Listeners ---

        // Preset buttons
        presetBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                // Remove 'selected' class from all buttons
                presetBtns.forEach(b => b.classList.remove('selected'));
                // Add 'selected' class to the clicked button
                btn.classList.add('selected');
                
                // Set the timer duration
                totalSeconds = parseInt(btn.dataset.time, 10);
                resetTimer(); // Reset to the new time
                startPauseBtn.disabled = false; // Enable start button
            });
        });

        // Start/Pause button
        startPauseBtn.addEventListener('click', () => {
            if (totalSeconds === 0) return;
            if (isRunning && !isPaused) {
                pauseTimer();
            } else {
                startTimer();
            }
        });

        // Reset button
        resetBtn.addEventListener('click', () => {
            presetBtns.forEach(b => b.classList.remove('selected'));
            totalSeconds = 0;
            resetTimer();
            timerDisplay.textContent = '00:00';
            setProgress(0);
        });
        
        // Set initial state on page load
        setProgress(0);

    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/boiled-egg-timer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Study stopwatch</title>
		<link>https://techaiconnect.com/study-stopwatch/</link>
					<comments>https://techaiconnect.com/study-stopwatch/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:20:59 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4261</guid>

					<description><![CDATA[Online Stopwatch - Time Tracking Tool for Studying Study Stopwatch The perfect tool to manage [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Online Stopwatch - Time Tracking Tool for Studying</title>
    <meta name="description" content="An accurate and easy-to-use online stopwatch. The perfect tool for timing study sessions, work, or workouts. Features lap recording and data export.">
    <meta name="keywords" content="stopwatch, online stopwatch, time tracker, study stopwatch, stopwatch online">
    
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Google Fonts: Inter & Roboto Mono -->
    <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=Inter:wght@400;500;600;700;800&family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        .timer-display {
            font-family: 'Roboto Mono', monospace;
        }
        .btn {
            transition: all 0.2s ease-in-out;
            border-radius: 0.5rem;
            padding: 0.75rem 1rem;
            font-weight: bold;
            font-size: 1.125rem;
            color: white;
        }
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
        }
        .btn-outline {
            background-color: transparent;
            border-width: 1px;
        }
        .prose-invert ul > li::before {
            background-color: #4ade80; /* green-400 */
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 sm:p-8 max-w-4xl">
        <!-- Header -->
        <header class="text-center mb-10">
            <h1 class="text-4xl sm:text-5xl font-extrabold text-white">Study Stopwatch</h1>
            <p class="text-gray-400 mt-2">The perfect tool to manage your time and boost productivity.</p>
        </header>

        <!-- Stopwatch Display -->
        <div class="bg-gray-800 p-6 sm:p-8 rounded-2xl shadow-2xl mb-8">
            <div class="text-center tabular-nums">
                <!-- Main timer display -->
                <div id="main-timer" class="timer-display text-6xl sm:text-8xl md:text-9xl font-bold text-white tracking-wider">00:00:00</div>
                <!-- Lap timer display -->
                <div id="lap-timer" class="timer-display text-2xl text-green-400 mt-2 h-8"></div>
            </div>
            <!-- Action Buttons -->
            <div class="mt-8 grid grid-cols-2 sm:grid-cols-4 gap-4">
                <button id="start-btn" class="btn bg-blue-600 hover:bg-blue-500 col-span-2 sm:col-span-1">Start</button>
                <button id="pause-btn" class="btn bg-yellow-500 hover:bg-yellow-400 hidden col-span-2 sm:col-span-1">Pause</button>
                <button id="lap-btn" class="btn bg-gray-600 hover:bg-gray-500 hidden">Lap</button>
                <button id="reset-btn" class="btn bg-red-600 hover:bg-red-500 hidden">Reset</button>
            </div>
        </div>

        <!-- Laps Table -->
        <div id="laps-container" class="hidden">
            <h2 class="text-2xl font-bold text-white mb-4">Lap History</h2>
            <div class="bg-gray-800 rounded-lg shadow-lg max-h-80 overflow-y-auto">
                <table class="w-full text-sm text-left">
                    <thead class="text-xs text-gray-300 uppercase bg-gray-700 sticky top-0">
                        <tr>
                            <th scope="col" class="px-6 py-3">Lap</th>
                            <th scope="col" class="px-6 py-3">Lap Time</th>
                            <th scope="col" class="px-6 py-3">Total Time</th>
                        </tr>
                    </thead>
                    <tbody id="laps-body">
                        <!-- Lap rows will be dynamically inserted here by JavaScript -->
                    </tbody>
                </table>
            </div>
            <!-- Lap Actions -->
             <div class="mt-6 flex justify-center gap-x-4 w-full">
                <button id="export-csv-btn" class="btn btn-outline border-green-500 text-green-400 hover:bg-green-500 hover:text-white flex-grow">Export CSV</button>
                <button id="clear-laps-btn" class="btn btn-outline border-red-500 text-red-400 hover:bg-red-500 hover:text-white flex-grow">Clear History</button>
            </div>
        </div>
        
        <!-- SEO & Help Content -->
        <article class="prose prose-invert max-w-full mt-12 text-gray-400">
            <h2>What is an Online Stopwatch?</h2>
            <p>An online stopwatch is a user-friendly application that allows you to accurately measure elapsed time. It functions as a digital stopwatch directly in your computer or phone browser, ideal for timing study sessions, work, sports training, or any activity that requires precise timekeeping.</p>
            
            <h2>How to Use the Stopwatch?</h2>
            <p>Using this tool is very simple:</p>
            <ul>
                <li>Press the <strong>Start</strong> button to begin the timer.</li>
                <li>Press the <strong>Pause</strong> button to stop the timer when you need a break. The <strong>Start</strong> button will then become a <strong>Resume</strong> button.</li>
                <li>Press the <strong>Lap</strong> button to record a time marker (e.g., finishing a book chapter, a workout set). The lap time and total time will be saved in the "Lap History" table.</li>
                <li>Press the <strong>Reset</strong> button to clear the entire session and reset the timer to zero.</li>
            </ul>
            <p>The lap data can be exported to a CSV file for you to store and analyze your performance by clicking the <strong>Export CSV</strong> button.</p>
            
            <h2>Does the Online Stopwatch work if the browser is closed?</h2>
            <p>No. The stopwatch will stop if you close the browser tab or window. However, it will continue to run if you switch to another tab or use a different application on your computer.</p>
        </article>
    </div>

    <script>
        // Get references to all necessary DOM elements
        const mainTimerEl = document.getElementById('main-timer');
        const lapTimerEl = document.getElementById('lap-timer');
        const startBtn = document.getElementById('start-btn');
        const pauseBtn = document.getElementById('pause-btn');
        const lapBtn = document.getElementById('lap-btn');
        const resetBtn = document.getElementById('reset-btn');
        const lapsContainer = document.getElementById('laps-container');
        const lapsBody = document.getElementById('laps-body');
        const exportCsvBtn = document.getElementById('export-csv-btn');
        const clearLapsBtn = document.getElementById('clear-laps-btn');

        // State variables to manage the stopwatch's logic
        let startTime = 0;
        let elapsedTime = 0;
        let lapStartTime = 0;
        let timerInterval; // To hold the setInterval ID
        let laps = []; // Array to store lap data
        let lapCounter = 0;

        /**
         * Formats a time in milliseconds to HH:MM:SS format.
         * @param {number} time - The time in milliseconds.
         * @returns {string} The formatted time string.
         */
        function formatTime(time) {
            const date = new Date(time);
            const hours = String(date.getUTCHours()).padStart(2, '0');
            const minutes = String(date.getUTCMinutes()).padStart(2, '0');
            const seconds = String(date.getUTCSeconds()).padStart(2, '0');
            return `${hours}:${minutes}:${seconds}`;
        }

        /**
         * Updates the main timer and the current lap timer displays.
         */
        function updateTimers() {
            const currentTime = Date.now();
            elapsedTime = currentTime - startTime;
            const lapTime = currentTime - lapStartTime;
            mainTimerEl.textContent = formatTime(elapsedTime);
            lapTimerEl.textContent = formatTime(lapTime);
        }

        /**
         * Starts or resumes the timer.
         */
        function startTimer() {
            // Adjust start time to account for paused duration
            startTime = Date.now() - elapsedTime;
            lapStartTime = Date.now() - (elapsedTime - (laps[0]?.totalTime || 0));
            timerInterval = setInterval(updateTimers, 100);
            
            // Update button visibility and text
            startBtn.classList.add('hidden');
            startBtn.textContent = 'Resume'; // Change text for when it's paused and resumed
            pauseBtn.classList.remove('hidden');
            lapBtn.classList.remove('hidden');
            resetBtn.classList.remove('hidden');
        }

        /**
         * Pauses the timer.
         */
        function pauseTimer() {
            clearInterval(timerInterval);
            
            // Update button visibility
            pauseBtn.classList.add('hidden');
            startBtn.classList.remove('hidden');
        }

        /**
         * Resets the entire stopwatch to its initial state.
         */
        function resetTimer() {
            clearInterval(timerInterval);
            // Reset all state variables
            startTime = 0;
            elapsedTime = 0;
            lapStartTime = 0;
            laps = [];
            lapCounter = 0;

            // Reset display elements
            mainTimerEl.textContent = '00:00:00';
            lapTimerEl.textContent = '';
            lapsBody.innerHTML = '';
            
            // Reset button states
            startBtn.textContent = 'Start';
            startBtn.classList.remove('hidden');
            pauseBtn.classList.add('hidden');
            lapBtn.classList.add('hidden');
            resetBtn.classList.add('hidden');
            lapsContainer.classList.add('hidden');
        }

        /**
         * Records a new lap.
         */
        function recordLap() {
            lapCounter++;
            const currentTime = Date.now();
            const totalTime = currentTime - startTime;
            // The time for this specific lap is the total elapsed time minus the total time of the previous lap.
            const previousTotalTime = laps.length > 0 ? laps[0].totalTime : 0;
            const lapTime = totalTime - previousTotalTime;

            // Add new lap to the beginning of the array
            laps.unshift({
                id: lapCounter,
                lapTime: lapTime,
                totalTime: totalTime
            });

            renderLaps();
            lapsContainer.classList.remove('hidden');
            // Reset the lap timer display for the new lap
            lapStartTime = currentTime;
        }

        /**
         * Renders the list of laps in the history table.
         */
        function renderLaps() {
            lapsBody.innerHTML = ''; // Clear existing rows
            laps.forEach(lap => {
                const row = document.createElement('tr');
                row.className = 'bg-gray-800 border-b border-gray-700';
                row.innerHTML = `
                    <td class="px-6 py-4 font-medium text-white">${lap.id}</td>
                    <td class="px-6 py-4">${formatTime(lap.lapTime)}</td>
                    <td class="px-6 py-4">${formatTime(lap.totalTime)}</td>
                `;
                lapsBody.appendChild(row);
            });
        }

        /**
         * Exports the lap history to a CSV file.
         */
        function exportToCsv() {
            if (laps.length === 0) return; // Don't export if there's no data
            let csvContent = "data:text/csv;charset=utf-8,Lap,Lap Time,Total Time\n";
            // Reverse the laps array to export in chronological order (1, 2, 3...)
            const reversedLaps = [...laps].reverse(); 
            reversedLaps.forEach(lap => {
                const row = [lap.id, formatTime(lap.lapTime), formatTime(lap.totalTime)].join(",");
                csvContent += row + "\n";
            });

            // Create a temporary link to trigger the download
            const encodedUri = encodeURI(csvContent);
            const link = document.createElement("a");
            link.setAttribute("href", encodedUri);
            link.setAttribute("download", "stopwatch_history.csv");
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
        
        /**
         * Clears the lap history without resetting the main timer.
         */
        function clearLaps() {
            laps = [];
            lapCounter = 0;
            lapsBody.innerHTML = '';
            lapsContainer.classList.add('hidden');
            // Reset the lap timer display and its start time
            lapTimerEl.textContent = '';
            lapStartTime = startTime ? Date.now() : 0;
        }

        // --- Event Listeners ---
        startBtn.addEventListener('click', startTimer);
        pauseBtn.addEventListener('click', pauseTimer);
        resetBtn.addEventListener('click', resetTimer);
        lapBtn.addEventListener('click', recordLap);
        exportCsvBtn.addEventListener('click', exportToCsv);
        clearLapsBtn.addEventListener('click', clearLaps);

    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/study-stopwatch/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Typing speed test</title>
		<link>https://techaiconnect.com/typing-speed-test/</link>
					<comments>https://techaiconnect.com/typing-speed-test/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 14:10:41 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4257</guid>

					<description><![CDATA[Typing Speed Test Typing Test Typing Test Practice Leaderboard Typing Speed Test Select your test [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Typing Speed Test</title>
    <script src="https://cdn.tailwindcss.com"></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=Inter:wght@400;500;700&display=swap" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/heroicons/2.1.3/24/solid/styles.min.css" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* gray-900 */
            color: #d1d5db; /* gray-300 */
        }
        /* Custom scrollbar for the text display area */
        #text-display::-webkit-scrollbar, #leaderboard-list::-webkit-scrollbar, #practice-area::-webkit-scrollbar {
            width: 8px;
        }
        #text-display::-webkit-scrollbar-track, #leaderboard-list::-webkit-scrollbar-track, #practice-area::-webkit-scrollbar-track {
            background: #1f2937; /* gray-800 */
        }
        #text-display::-webkit-scrollbar-thumb, #leaderboard-list::-webkit-scrollbar-thumb, #practice-area::-webkit-scrollbar-thumb {
            background: #4b5563; /* gray-600 */
            border-radius: 4px;
        }
        #text-display::-webkit-scrollbar-thumb:hover, #leaderboard-list::-webkit-scrollbar-thumb:hover, #practice-area::-webkit-scrollbar-thumb:hover {
            background: #6b7280; /* gray-500 */
        }
        .correct {
            color: #a3e635; /* lime-400 */
        }
        .incorrect {
            color: #ef4444; /* red-500 */
            text-decoration: underline;
            text-decoration-color: #ef4444;
        }
        .cursor {
            display: inline-block;
            width: 2px;
            height: 1.75rem; /* h-7 */
            background-color: #38bdf8; /* sky-400 */
            animation: blink 1s infinite;
            margin-left: 1px;
            border-radius: 1px;
            vertical-align: middle;
        }
        @keyframes blink {
            50% { opacity: 0; }
        }
        .test-inactive .cursor {
            display: none;
        }
        #text-input {
            position: absolute;
            top: -9999px;
            left: -9999px;
            opacity: 0;
            width: 0;
            height: 0;
        }
        /* Sidebar active link style */
        .sidebar-link.active {
            background-color: #0ea5e920; /* sky-500/20 */
            border-color: #0ea5e950; /* sky-500/30 */
            color: #ffffff;
        }
    </style>
</head>
<body class="flex items-center justify-center min-h-screen">

    <div class="w-full max-w-6xl mx-auto p-4 sm:p-6 lg:p-8">
        <div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
            <!-- Sidebar -->
            <aside class="lg:col-span-1">
                <div class="bg-gray-800 p-6 rounded-2xl border border-gray-700 h-full">
                    <div class="flex items-center gap-3 mb-6">
                         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8 text-sky-400">
                            <path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm.53 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v5.69a.75.75 0 0 0 1.5 0v-5.69l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" clip-rule="evenodd" />
                        </svg>
                        <h1 class="text-2xl font-bold text-white">Typing Test</h1>
                    </div>
                    <nav class="space-y-2">
                        <a href="#" id="typing-test-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path d="M10 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" /><path fill-rule="evenodd" d="M10 2a.75.75 0 0 1 .75.75v.5a.75.75 0 0 1-1.5 0v-.5A.75.75 0 0 1 10 2ZM5.28 4.22a.75.75 0 0 1 0 1.06l-.5.5a.75.75 0 1 1-1.06-1.06l.5-.5a.75.75 0 0 1 1.06 0ZM15.78 5.28a.75.75 0 0 1-1.06 0l-.5-.5a.75.75 0 0 1 1.06-1.06l.5.5a.75.75 0 0 1 0 1.06ZM17 10a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1 0-1.5h.5A.75.75 0 0 1 17 10ZM4.25 10.75a.75.75 0 0 1 0-1.5h-.5a.75.75 0 0 1 0 1.5h.5ZM14.72 14.72a.75.75 0 0 1 0-1.06l.5-.5a.75.75 0 1 1 1.06 1.06l-.5.5a.75.75 0 0 1-1.06 0ZM5.28 15.78a.75.75 0 0 1-1.06 0l-.5.5a.75.75 0 0 1-1.06-1.06l.5-.5a.75.75 0 0 1 1.06 0ZM10 17a.75.75 0 0 1 .75.75v.5a.75.75 0 0 1-1.5 0v-.5A.75.75 0 0 1 10 17ZM10 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4Z" clip-rule="evenodd" /></svg>
                            <span>Typing Test</span>
                        </a>
                        <a href="#" id="practice-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                           <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path d="M11.983 1.904a.75.75 0 0 0-1.292-.782l-5.015 8.7a.75.75 0 0 0 1.292.782l5.015-8.7Z" /><path d="m13.844 12.214.339.587a.75.75 0 0 1-1.292.782l-1.37-2.374a.75.75 0 0 1 1.292-.782l1.031 1.787Z" /><path d="M12.25 10.25a2 2 0 1 0 4 0 2 2 0 0 0-4 0Z" /><path d="M15.5 10.25a3.25 3.25 0 1 0-6.5 0 3.25 3.25 0 0 0 6.5 0Z" /><path d="M8.283 6.034a.75.75 0 0 0-1.292-.782l-1.37 2.374a.75.75 0 1 0 1.292.782l1.37-2.374Z" /><path d="m.895 10.03-.98 1.697a.75.75 0 1 0 1.299.75l.98-1.697a.75.75 0 1 0-1.3-.75Z" /><path d="M6.25 15.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" /><path d="M4.75 18.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Z" /><path d="M9 14a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z" /></svg>
                            <span>Practice</span>
                        </a>
                        <a href="#" id="leaderboard-link" class="sidebar-link flex items-center gap-3 px-4 py-2 text-gray-400 hover:bg-gray-700/50 rounded-lg transition-colors border border-transparent">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path fill-rule="evenodd" d="M6 3.75A2.75 2.75 0 0 1 8.75 1h2.5A2.75 2.75 0 0 1 14 3.75v.443c.57.166 1.111.418 1.605.748a.75.75 0 1 1-.81 1.282A11.96 11.96 0 0 0 14 6.017V11a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V6.017c-1.141.213-2.22.62-3.205 1.214a.75.75 0 0 1-.81-1.282A13.443 13.443 0 0 1 6 4.193V3.75Z" clip-rule="evenodd" /><path d="M10 14a1 1 0 0 1 1 1v1.5a1.5 1.5 0 0 1-3 0V15a1 1 0 0 1 1-1Z" /></svg>
                            <span>Leaderboard</span>
                        </a>
                    </nav>
                </div>
            </aside>

            <!-- Main Content -->
            <main class="lg:col-span-3">
                <div id="test-setup" class="main-view bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col items-center justify-center h-full">
                    <h2 class="text-3xl font-bold text-white mb-2">Typing Speed Test</h2>
                    <p class="text-gray-400 mb-8">Select your test options and start typing!</p>
                    <div class="flex flex-col sm:flex-row gap-4 mb-8">
                        <div class="w-full sm:w-48">
                            <label for="time-select" class="block text-sm font-medium text-gray-400 mb-2">Time</label>
                            <select id="time-select" class="w-full bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-sky-500 focus:border-sky-500 p-2.5">
                                <option value="60" selected>1 minute</option>
                                <option value="120">2 minutes</option>
                                <option value="300">5 minutes</option>
                            </select>
                        </div>
                    </div>
                    <button id="start-btn" class="bg-sky-500 hover:bg-sky-600 text-white font-bold py-3 px-10 rounded-lg transition-colors text-lg">
                        Start Test
                    </button>
                </div>

                <div id="test-area" class="main-view hidden">
                    <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">Time</p><p id="timer" class="text-2xl font-bold text-white">60</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">WPM</p><p id="wpm" class="text-2xl font-bold text-white">0</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">Accuracy</p><p id="accuracy" class="text-2xl font-bold text-white">100%</p></div>
                        <div class="bg-gray-800 p-4 rounded-xl border border-gray-700 text-center"><p class="text-sm text-gray-400">Errors</p><p id="errors" class="text-2xl font-bold text-red-500">0</p></div>
                    </div>
                    <div id="text-container" class="relative bg-gray-800 p-6 rounded-2xl border border-gray-700 mb-6 test-inactive" onclick="document.getElementById('text-input').focus()">
                        <div id="text-display" class="text-2xl font-mono tracking-wide leading-relaxed h-48 overflow-y-auto"></div>
                        <input type="text" id="text-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
                    </div>
                    <div class="flex justify-center">
                        <button id="reset-btn" class="bg-gray-600 hover:bg-gray-500 text-white font-bold py-3 px-8 rounded-lg transition-colors flex items-center gap-2">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"><path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201-4.42 5.5 5.5 0 0 1 10.89 2.166l.255-.256a.75.75 0 0 1 1.06 1.06l-2.5 2.5a.75.75 0 0 1-1.06 0l-2.5-2.5a.75.75 0 1 1 1.06-1.06l.64.641a4 4 0 0 0-7.143-2.957 4 4 0 0 0 7.83 1.626l.216.432a.75.75 0 0 1-.941 1.055l-.216-.432a2.5 2.5 0 0 1-4.89-1.015c.34-.09.688-.14 1.038-.14a2.5 2.5 0 0 1 2.212 1.288l-.352.353a.75.75 0 0 1-1.06-1.06l.352-.353a1 1 0 0 0-.885-.514H8.25a.75.75 0 0 1 0-1.5h2.263a2.5 2.5 0 0 1 4.799 1.076Z" clip-rule="evenodd" /></svg>
                            <span>Reset</span>
                        </button>
                    </div>
                </div>
                
                <div id="results-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col items-center justify-center h-full">
                     <h2 class="text-3xl font-bold text-white mb-2">Test Complete!</h2>
                     <p class="text-gray-400 mb-8">Here are your results:</p>
                     <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8 w-full max-w-2xl">
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">WPM</p><p id="results-wpm" class="text-4xl font-bold text-white">0</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">Accuracy</p><p id="results-accuracy" class="text-4xl font-bold text-white">0%</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">Characters</p><p id="results-chars" class="text-4xl font-bold text-white">0/0</p></div>
                        <div class="bg-gray-900/50 p-4 rounded-xl text-center"><p class="text-sm text-sky-400">Errors</p><p id="results-errors" class="text-4xl font-bold text-white">0</p></div>
                     </div>
                     <button id="try-again-btn" class="bg-sky-500 hover:bg-sky-600 text-white font-bold py-3 px-10 rounded-lg transition-colors text-lg">
                        Try Again
                    </button>
                </div>

                <div id="practice-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col h-full">
                    <h2 class="text-3xl font-bold text-white mb-6 text-center">Practice Lessons</h2>
                    <div id="practice-lessons-grid" class="grid grid-cols-1 md:grid-cols-2 gap-6 flex-grow overflow-y-auto pr-2">
                        <!-- Practice lessons will be generated here -->
                    </div>
                </div>

                <div id="leaderboard-area" class="main-view hidden bg-gray-800 p-8 rounded-2xl border border-gray-700 flex flex-col h-full">
                    <h2 class="text-3xl font-bold text-white mb-6 text-center">Top 10 Scores</h2>
                    <div class="flex-grow overflow-y-auto">
                        <ol id="leaderboard-list" class="space-y-3 pr-2"></ol>
                    </div>
                </div>

            </main>
        </div>
    </div>

    <script>
    document.addEventListener('DOMContentLoaded', () => {
        // --- DOM Elements ---
        const mainViews = document.querySelectorAll('.main-view');
        const sidebarLinks = document.querySelectorAll('.sidebar-link');
        
        const testSetupEl = document.getElementById('test-setup');
        const testAreaEl = document.getElementById('test-area');
        const resultsAreaEl = document.getElementById('results-area');
        const practiceAreaEl = document.getElementById('practice-area');
        const leaderboardAreaEl = document.getElementById('leaderboard-area');

        const typingTestLink = document.getElementById('typing-test-link');
        const practiceLink = document.getElementById('practice-link');
        const leaderboardLink = document.getElementById('leaderboard-link');

        const startBtn = document.getElementById('start-btn');
        const timeSelect = document.getElementById('time-select');
        const timerEl = document.getElementById('timer');
        const wpmEl = document.getElementById('wpm');
        const accuracyEl = document.getElementById('accuracy');
        const errorsEl = document.getElementById('errors');
        const textContainer = document.getElementById('text-container');
        const textDisplayEl = document.getElementById('text-display');
        const textInput = document.getElementById('text-input');
        const resetBtn = document.getElementById('reset-btn');
        const tryAgainBtn = document.getElementById('try-again-btn');

        const resultsWpm = document.getElementById('results-wpm');
        const resultsAccuracy = document.getElementById('results-accuracy');
        const resultsChars = document.getElementById('results-chars');
        const resultsErrors = document.getElementById('results-errors');
        
        const leaderboardList = document.getElementById('leaderboard-list');
        const practiceLessonsGrid = document.getElementById('practice-lessons-grid');

        // --- Data ---
        const textPassages = [
            "The quick brown fox jumps over the lazy dog. This sentence contains all the letters of the alphabet. Learning to type quickly and accurately is a valuable skill in today's digital world. Practice regularly to improve your speed.",
            "Technology has revolutionized the way we live and work. From smartphones to artificial intelligence, innovation continues to shape our future. Understanding these changes is crucial for staying ahead in a fast-paced environment.",
            "To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment. Believe in your potential and never stop learning. The journey of a thousand miles begins with a single step.",
            "The sun is a star at the center of the Solar System. It is a nearly perfect sphere of hot plasma, with internal convective motion that generates a magnetic field via a dynamo process. It is by far the most important source of energy for life on Earth.",
            "Never give up on a dream just because of the time it will take to accomplish it. The time will pass anyway. Success is not final, failure is not fatal: it is the courage to continue that counts. Your limitation is only your imagination."
        ];
        
        const practiceLessons = [
            { title: 'Home Row', description: 'Practice the home row keys.', text: 'asdf jkl; asdf jkl; asdf jkl; sad lad fall; all ask; a lass; a sad lad; fall fall ask ask' },
            { title: 'Top Row', description: 'Practice the top row keys.', text: 'qwerty uiop qwerty uiop qwerty uiop wet pot true quit; your tie; we were quiet; you quit;' },
            { title: 'Bottom Row', description: 'Practice the bottom row keys.', text: 'zxcvbnm zxcvbnm zxcvbnm man can; van box; my cat; can my van; a man; a box' },
            { title: 'Number Row', description: 'Practice the number keys.', text: '12345 67890 12345 67890 19 28 37 46 50 17 26 35 48 90' },
            { title: 'Common Words', description: 'Practice with 100 common English words.', text: 'the be to of and a in that have I it for not on with he as you do at this but his by from they we say her she or an will my one all would there their what so up out if about who get which go me' },
            { title: 'Punctuation', description: 'Practice common punctuation.', text: 'He said, "Hello, world!" Is that right? Yes, it is. The dog\'s toy is over there. We need milk, eggs, and bread. What a day! (A great one.)' }
        ];

        // --- State ---
        let state = {
            text: '',
            timeLimit: 60,
            timeLeft: 60,
            timerInterval: null,
            testActive: false,
            charsTyped: 0,
            correctChars: 0,
            errors: 0,
            wpm: 0,
            accuracy: 100,
            currentMode: 'test', // 'test' or 'practice'
            currentPracticeText: null,
        };

        // --- Functions ---
        
        function showView(viewId) {
            mainViews.forEach(view => view.classList.add('hidden'));
            document.getElementById(viewId).classList.remove('hidden');

            let activeLink;
            if (['test-setup', 'test-area', 'results-area'].includes(viewId)) {
                activeLink = typingTestLink;
            } else if (viewId === 'practice-area') {
                activeLink = practiceLink;
                renderPracticeLessons();
            } else if (viewId === 'leaderboard-area') {
                activeLink = leaderboardLink;
                renderLeaderboard();
            }

            sidebarLinks.forEach(link => link.classList.remove('active'));
            if(activeLink) activeLink.classList.add('active');
        }

        function prepareTest(textToPractice) {
            state.text = textToPractice;
            textDisplayEl.innerHTML = '';
            state.text.split('').forEach(char => {
                const charSpan = document.createElement('span');
                charSpan.textContent = char;
                textDisplayEl.appendChild(charSpan);
            });
            const cursorSpan = document.createElement('span');
            cursorSpan.className = 'cursor';
            textDisplayEl.insertBefore(cursorSpan, textDisplayEl.firstChild);
        }

        function resetState() {
            clearInterval(state.timerInterval);
            state.timeLimit = parseInt(timeSelect.value, 10);
            state.timeLeft = state.timeLimit;
            state.testActive = false;
            state.charsTyped = 0;
            state.correctChars = 0;
            state.errors = 0;
            state.wpm = 0;
            state.accuracy = 100;
            
            timerEl.textContent = state.timeLimit;
            wpmEl.textContent = '0';
            accuracyEl.textContent = '100%';
            errorsEl.textContent = '0';
            textInput.value = '';
            textInput.disabled = false;
            textContainer.classList.add('test-inactive');
            textContainer.classList.remove('test-active');
        }

        function startTest() {
            state.currentMode = 'test';
            state.currentPracticeText = null;
            resetState();
            prepareTest(textPassages[Math.floor(Math.random() * textPassages.length)]);
            showView('test-area');
            textInput.focus();
        }
        
        function startPractice(lessonText) {
            state.currentMode = 'practice';
            state.currentPracticeText = lessonText;
            resetState();
            prepareTest(lessonText);
            showView('test-area');
            textInput.focus();
        }

        function handleInput() {
            const typedText = textInput.value;
            const typedLength = typedText.length;

            if (!state.testActive && typedLength > 0) {
                state.testActive = true;
                textContainer.classList.remove('test-inactive');
                textContainer.classList.add('test-active');
                state.timerInterval = setInterval(updateTimer, 1000);
            }
            if (!state.testActive) return;

            state.charsTyped = typedLength;
            let currentErrors = 0;
            let currentCorrectChars = 0;
            const textSpans = textDisplayEl.querySelectorAll('span:not(.cursor)');
            
            for (let i = 0; i < state.text.length; i++) {
                const charSpan = textSpans[i];
                if (i < typedLength) {
                    if (typedText[i] === state.text[i]) {
                        charSpan.className = 'correct';
                        currentCorrectChars++;
                    } else {
                        charSpan.className = 'incorrect';
                        currentErrors++;
                    }
                } else {
                    charSpan.className = '';
                }
            }
            
            const cursor = textDisplayEl.querySelector('.cursor');
            const nextCharSpan = textSpans[typedLength];
            textDisplayEl.insertBefore(cursor, nextCharSpan || null);

            state.errors = currentErrors;
            state.correctChars = currentCorrectChars;

            const timeElapsed = state.timeLimit - state.timeLeft;
            if (timeElapsed > 0) {
                state.wpm = Math.round((state.correctChars / 5) / (timeElapsed / 60));
            }
            state.accuracy = state.charsTyped > 0 ? Math.round((state.correctChars / state.charsTyped) * 100) : 100;

            wpmEl.textContent = state.wpm;
            errorsEl.textContent = state.errors;
            accuracyEl.textContent = `${state.accuracy}%`;

            if (typedLength === state.text.length && state.errors === 0) {
                endTest();
            }
        }

        function updateTimer() {
            if (state.timeLeft > 0) {
                state.timeLeft--;
                timerEl.textContent = state.timeLeft;
            } else {
                endTest();
            }
        }

        function endTest() {
            clearInterval(state.timerInterval);
            state.testActive = false;
            textInput.disabled = true;
            textContainer.classList.add('test-inactive');
            textContainer.classList.remove('test-active');

            const timeElapsedInMinutes = (state.timeLimit - state.timeLeft) / 60;
            const finalWpm = timeElapsedInMinutes > 0 ? Math.round((state.correctChars / 5) / timeElapsedInMinutes) : 0;
            const finalAccuracy = state.charsTyped > 0 ? Math.round((state.correctChars / state.charsTyped) * 100) : 100;
            
            if (state.currentMode === 'test') {
                saveScore({ wpm: finalWpm, accuracy: finalAccuracy, date: new Date() });
            }

            resultsWpm.textContent = finalWpm;
            resultsAccuracy.textContent = `${finalAccuracy}%`;
            resultsChars.textContent = `${state.correctChars}/${state.charsTyped}`;
            resultsErrors.textContent = state.errors;

            showView('results-area');
        }
        
        function resetToSetup() {
            state.currentMode = 'test';
            resetState();
            showView('test-setup');
        }
        
        function handleReset() {
            if (state.currentMode === 'practice') {
                startPractice(state.currentPracticeText);
            } else {
                startTest();
            }
        }
        
        function handleTryAgain() {
            if (state.currentMode === 'practice') {
                showView('practice-area');
            } else {
                resetToSetup();
            }
        }

        // --- Leaderboard Functions ---
        function saveScore(newScore) {
            if (newScore.wpm === 0) return;
            const scores = JSON.parse(localStorage.getItem('typingScores')) || [];
            scores.push(newScore);
            scores.sort((a, b) => b.wpm - a.wpm);
            const top10 = scores.slice(0, 10);
            localStorage.setItem('typingScores', JSON.stringify(top10));
        }

        function renderLeaderboard() {
            const scores = JSON.parse(localStorage.getItem('typingScores')) || [];
            leaderboardList.innerHTML = '';

            if (scores.length === 0) {
                leaderboardList.innerHTML = '<li class="text-center text-gray-400">No scores yet. Complete a test to see your name in lights!</li>';
                return;
            }

            scores.forEach((score, index) => {
                const li = document.createElement('li');
                li.className = 'bg-gray-700/50 p-4 rounded-lg flex items-center justify-between gap-4';
                li.innerHTML = `
                    <div class="flex items-center gap-4">
                        <span class="text-xl font-bold text-gray-400 w-8 text-center">${index + 1}</span>
                        <div>
                            <p class="text-lg font-bold text-white">${score.wpm} WPM</p>
                            <p class="text-sm text-gray-400">Accuracy: ${score.accuracy}%</p>
                        </div>
                    </div>
                    <p class="text-sm text-gray-500">${new Date(score.date).toLocaleDateString()}</p>
                `;
                leaderboardList.appendChild(li);
            });
        }
        
        // --- Practice Lessons ---
        function renderPracticeLessons() {
            practiceLessonsGrid.innerHTML = '';
            practiceLessons.forEach(lesson => {
                const card = document.createElement('div');
                card.className = 'bg-gray-700/50 p-6 rounded-lg flex flex-col';
                card.innerHTML = `
                    <h3 class="text-xl font-bold text-white mb-2">${lesson.title}</h3>
                    <p class="text-gray-400 mb-4 flex-grow">${lesson.description}</p>
                    <button class="start-practice-btn mt-auto bg-sky-600 hover:bg-sky-700 text-white font-semibold py-2 px-4 rounded-lg transition-colors w-full">
                        Start
                    </button>
                `;
                card.querySelector('.start-practice-btn').addEventListener('click', () => startPractice(lesson.text));
                practiceLessonsGrid.appendChild(card);
            });
        }

        // --- Event Listeners ---
        startBtn.addEventListener('click', startTest);
        resetBtn.addEventListener('click', handleReset);
        tryAgainBtn.addEventListener('click', handleTryAgain);
        textInput.addEventListener('input', handleInput);
        
        textContainer.addEventListener('click', () => {
            if (state.testActive || (!state.testActive && state.timeLeft === state.timeLimit)) {
                 textInput.focus();
            }
        });

        typingTestLink.addEventListener('click', (e) => { e.preventDefault(); resetToSetup(); });
        practiceLink.addEventListener('click', (e) => { e.preventDefault(); showView('practice-area'); });
        leaderboardLink.addEventListener('click', (e) => { e.preventDefault(); showView('leaderboard-area'); });
        
        // --- Initial Setup ---
        showView('test-setup');
    });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/typing-speed-test/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Periodic Table of Elements</title>
		<link>https://techaiconnect.com/periodic-table-of-elements/</link>
					<comments>https://techaiconnect.com/periodic-table-of-elements/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 13:51:19 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4253</guid>

					<description><![CDATA[Periodic Table of Elements Periodic Table of Elements Click on an element to see its [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Periodic Table of Elements</title>
    <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=Inter:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        /* --- Base Styles & Dark Theme --- */
        :root {
            --bg-color: #121212;
            --primary-text-color: #E0E0E0;
            --secondary-text-color: #B0B0B0;
            --border-color: #333333;
            --element-bg-color: #1E1E1E;
            --element-hover-bg-color: #2a2a2a;
            --modal-bg-color: #252525;
            
            /* Element Group Colors */
            --diatomic-nonmetal: #3a5e8c;
            --noble-gas: #7e3a8c;
            --alkali-metal: #8c3a3a;
            --alkaline-earth-metal: #8c623a;
            --metalloid: #3a8c8c;
            --halogen: #6a3a8c;
            --post-transition-metal: #3a8c5e;
            --transition-metal: #3a6a8c;
            --lanthanide: #8c3a75;
            --actinide: #8c3a5e;
            --unknown: #444444;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Inter', sans-serif;
            background-color: var(--bg-color);
            color: var(--primary-text-color);
            padding: 1rem;
            line-height: 1.6;
        }

        h1 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 0.5rem;
            font-weight: 700;
        }
        
        .subtitle {
            text-align: center;
            font-size: 1rem;
            color: var(--secondary-text-color);
            margin-bottom: 2rem;
        }

        /* --- Periodic Table Grid --- */
        #periodic-table-container {
            display: flex;
            justify-content: center;
            align-items: center;
            width: 100%;
            overflow-x: auto; /* Allows horizontal scrolling on small screens */
        }

        #periodic-table {
            display: grid;
            grid-template-columns: repeat(18, minmax(60px, 1fr));
            grid-template-rows: repeat(10, minmax(60px, 1fr));
            gap: 5px;
            max-width: 1200px;
            margin: 0 auto;
        }

        /* --- Element Tile Styles --- */
        .element {
            background-color: var(--element-bg-color);
            border: 1px solid var(--border-color);
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.2s ease-in-out;
            padding: 4px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            text-align: center;
            color: var(--primary-text-color);
        }

        .element:hover {
            transform: scale(1.1);
            background-color: var(--element-hover-bg-color);
            border-color: var(--primary-text-color);
            z-index: 10;
            position: relative;
        }

        .element .atomic-number {
            font-size: 0.6rem;
            align-self: flex-start;
            padding-left: 2px;
            color: var(--secondary-text-color);
        }

        .element .symbol {
            font-size: 1.2rem;
            font-weight: 700;
        }

        .element .name {
            font-size: 0.6rem;
            word-break: break-all;
        }
        
        .element .atomic-mass {
            font-size: 0.5rem;
            color: var(--secondary-text-color);
        }

        /* --- Element Category Colors --- */
        .diatomic.nonmetal { background-color: var(--diatomic-nonmetal); }
        .noble.gas { background-color: var(--noble-gas); }
        .alkali.metal { background-color: var(--alkali-metal); }
        .alkaline.earth.metal { background-color: var(--alkaline-earth-metal); }
        .metalloid { background-color: var(--metalloid); }
        .halogen { background-color: var(--halogen); }
        .post-transition.metal { background-color: var(--post-transition-metal); }
        .transition.metal { background-color: var(--transition-metal); }
        .lanthanide { background-color: var(--lanthanide); }
        .actinide { background-color: var(--actinide); }
        .unknown { background-color: var(--unknown); }
        
        /* --- Legend Styles --- */
        #legend {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 15px;
            margin: 2rem auto;
            max-width: 1000px;
        }
        .legend-item {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .legend-color {
            width: 20px;
            height: 20px;
            border-radius: 4px;
            border: 1px solid var(--border-color);
        }
        .legend-item span {
            font-size: 0.9rem;
        }

        /* --- Modal Styles --- */
        #element-modal {
            display: none; /* Hidden by default */
            position: fixed;
            z-index: 100;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            overflow: auto;
            background-color: rgba(0, 0, 0, 0.7);
            align-items: center;
            justify-content: center;
        }

        .modal-content {
            background-color: var(--modal-bg-color);
            margin: auto;
            padding: 30px;
            border: 1px solid var(--border-color);
            width: 90%;
            max-width: 500px;
            border-radius: 10px;
            position: relative;
            box-shadow: 0 5px 25px rgba(0,0,0,0.5);
        }

        .modal-close {
            color: var(--secondary-text-color);
            position: absolute;
            top: 10px;
            right: 20px;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
            transition: color 0.2s;
        }

        .modal-close:hover {
            color: var(--primary-text-color);
        }
        
        #modal-title {
            margin-top: 0;
            margin-bottom: 1rem;
            font-size: 2rem;
            font-weight: 700;
        }
        
        #modal-summary {
            margin-top: 1rem;
            font-size: 0.95rem;
            color: var(--secondary-text-color);
        }
        
        .modal-details {
            list-style: none;
            padding: 0;
            margin-top: 1.5rem;
        }
        
        .modal-details li {
            padding: 5px 0;
            border-bottom: 1px solid var(--border-color);
        }
        
        .modal-details li:last-child {
            border-bottom: none;
        }
        
        .modal-details strong {
            color: var(--primary-text-color);
        }

        /* --- Responsive Design --- */
        @media (max-width: 768px) {
            h1 {
                font-size: 2rem;
            }
            #periodic-table {
                gap: 3px;
                grid-template-columns: repeat(18, 50px);
                grid-template-rows: repeat(10, 50px);
            }
            .element .symbol { font-size: 1rem; }
            .element .name, .element .atomic-mass { display: none; }
        }
        
        @media (max-width: 480px) {
            body { padding: 0.5rem; }
            h1 { font-size: 1.5rem; }
            .subtitle { font-size: 0.9rem; margin-bottom: 1rem; }
            #periodic-table {
                grid-template-columns: repeat(18, 40px);
                grid-template-rows: repeat(10, 40px);
            }
             .element .atomic-number { font-size: 0.5rem; }
             .element .symbol { font-size: 0.8rem; }
        }
    </style>
</head>
<body>

    <h1>Periodic Table of Elements</h1>
    <p class="subtitle">Click on an element to see its details.</p>

    <div id="periodic-table-container">
        <div id="periodic-table">
            <!-- Elements will be dynamically inserted here by JavaScript -->
        </div>
    </div>
    
    <div id="legend">
        <!-- Legend will be dynamically inserted here by JavaScript -->
    </div>

    <!-- The Modal -->
    <div id="element-modal">
        <div class="modal-content">
            <span class="modal-close">&times;</span>
            <h2 id="modal-title">Element Name</h2>
            <ul class="modal-details">
                <li id="modal-number"></li>
                <li id="modal-symbol"></li>
                <li id="modal-mass"></li>
                <li id="modal-category"></li>
                <li id="modal-discovered-by"></li>
                <li id="modal-phase"></li>
            </ul>
            <p id="modal-summary"></p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // --- DATA: Fetch and process element data ---
            const elementsDataUrl = 'https://raw.githubusercontent.com/Bowserinator/Periodic-Table-JSON/master/PeriodicTableJSON.json';
            
            // --- DOM Element References ---
            const table = document.getElementById('periodic-table');
            const modal = document.getElementById('element-modal');
            const closeModal = document.querySelector('.modal-close');
            const legendContainer = document.getElementById('legend');

            // --- Main function to fetch data and build the table ---
            async function initializePeriodicTable() {
                try {
                    const response = await fetch(elementsDataUrl);
                    if (!response.ok) {
                        throw new Error(`HTTP error! Status: ${response.status}`);
                    }
                    const data = await response.json();
                    buildTable(data.elements);
                    buildLegend(data.elements);
                } catch (error) {
                    console.error("Could not fetch or process periodic table data:", error);
                    table.innerHTML = '<p style="color: red; grid-column: 1 / -1; text-align: center;">Failed to load element data. Please try again later.</p>';
                }
            }

            // --- Function to build the table grid ---
            function buildTable(elements) {
                elements.forEach(element => {
                    const elementTile = document.createElement('div');
                    elementTile.className = 'element';
                    
                    // Add category classes for color coding
                    const categoryClass = element.category.replace(/\s+/g, '.').toLowerCase();
                    elementTile.classList.add(...categoryClass.split('.'));

                    // Set grid position
                    elementTile.style.gridColumn = element.xpos;
                    elementTile.style.gridRow = element.ypos;
                    
                    // Populate tile with data
                    elementTile.innerHTML = `
                        <div class="atomic-number">${element.number}</div>
                        <div class="symbol">${element.symbol}</div>
                        <div class="name">${element.name}</div>
                        <div class="atomic-mass">${(element.atomic_mass || 0).toFixed(3)}</div>
                    `;
                    
                    // Add click event listener to show modal
                    elementTile.addEventListener('click', () => showModal(element));
                    
                    table.appendChild(elementTile);
                });
            }

            // --- Function to build the color legend ---
            function buildLegend(elements) {
                const categories = {};
                elements.forEach(el => {
                    if (!categories[el.category]) {
                        categories[el.category] = el.category.replace(/\s+/g, '.').toLowerCase();
                    }
                });

                // Order for legend display
                const legendOrder = [
                    'diatomic nonmetal', 'noble gas', 'alkali metal', 'alkaline earth metal',
                    'metalloid', 'halogen', 'post-transition metal', 'transition metal',
                    'lanthanide', 'actinide', 'unknown, probably transition metal', 'unknown, probably post-transition metal', 'unknown, probably metalloid'
                ];
                
                // Remap long unknown names
                const categoryNameMap = {
                    'unknown, probably transition metal': 'Unknown',
                    'unknown, probably post-transition metal': 'Unknown',
                    'unknown, probably metalloid': 'Unknown'
                };

                const uniqueCategories = [...new Set(legendOrder.map(cat => categoryNameMap[cat] || cat))];

                uniqueCategories.forEach(categoryName => {
                    // Find the original category key to get the class
                    const originalCategory = Object.keys(categories).find(key => (categoryNameMap[key] || key) === categoryName);
                    if (!originalCategory) return;
                    
                    const categoryClass = categories[originalCategory];

                    const legendItem = document.createElement('div');
                    legendItem.className = 'legend-item';
                    
                    const colorBox = document.createElement('div');
                    colorBox.className = 'legend-color';
                    // Apply the same background color as the elements
                    const primaryClass = categoryClass.split('.')[0];
                    const secondaryClass = categoryClass.split('.')[1] || '';
                    colorBox.style.backgroundColor = `var(--${primaryClass}-${secondaryClass}`.replace(/--$/, '');
                    if (categoryName === 'Unknown') colorBox.style.backgroundColor = 'var(--unknown)';


                    const text = document.createElement('span');
                    text.textContent = categoryName.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
                    
                    legendItem.appendChild(colorBox);
                    legendItem.appendChild(text);
                    legendContainer.appendChild(legendItem);
                });
            }

            // --- Modal Functions ---
            function showModal(element) {
                document.getElementById('modal-title').textContent = `${element.name} (${element.symbol})`;
                document.getElementById('modal-number').innerHTML = `<strong>Atomic Number:</strong> ${element.number}`;
                document.getElementById('modal-symbol').innerHTML = `<strong>Symbol:</strong> ${element.symbol}`;
                document.getElementById('modal-mass').innerHTML = `<strong>Atomic Mass:</strong> ${element.atomic_mass}`;
                document.getElementById('modal-category').innerHTML = `<strong>Category:</strong> <span style="text-transform: capitalize;">${element.category}</span>`;
                document.getElementById('modal-discovered-by').innerHTML = `<strong>Discovered by:</strong> ${element.discovered_by || 'Unknown'}`;
                document.getElementById('modal-phase').innerHTML = `<strong>Phase at STP:</strong> <span style="text-transform: capitalize;">${element.phase}</span>`;
                document.getElementById('modal-summary').textContent = element.summary;
                
                modal.style.display = 'flex';
            }

            closeModal.onclick = () => {
                modal.style.display = 'none';
            }

            window.onclick = (event) => {
                if (event.target == modal) {
                    modal.style.display = 'none';
                }
            }

            // --- Initialize ---
            initializePeriodicTable();
        });
    </script>

</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/periodic-table-of-elements/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Percentage Calculator</title>
		<link>https://techaiconnect.com/percentage-calculator/</link>
					<comments>https://techaiconnect.com/percentage-calculator/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 13:39:39 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4249</guid>

					<description><![CDATA[Percentage Calculator Percentage Calculator A versatile tool for all your percentage calculation needs. General Calculator [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Percentage Calculator</title>
    
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Google Fonts: Inter -->
    <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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">

    <style>
        /* Custom styles to complement Tailwind */
        body {
            font-family: 'Inter', sans-serif;
        }
        /* Style for input number spinners */
        input[type=number]::-webkit-inner-spin-button, 
        input[type=number]::-webkit-outer-spin-button { 
          -webkit-appearance: none; 
          margin: 0; 
        }
        input[type=number] {
          -moz-appearance: textfield;
        }
        .result-field {
            background-color: #1f2937; /* gray-800 */
            color: #d1d5db; /* gray-300 */
            font-weight: bold;
        }
    </style>
</head>
<body class="bg-gray-900 text-gray-200">

    <div class="container mx-auto p-4 md:p-8 max-w-4xl">

        <!-- Header -->
        <header class="text-center mb-8 md:mb-12">
            <h1 class="text-4xl md:text-5xl font-bold text-white">Percentage Calculator</h1>
            <p class="text-gray-400 mt-2">A versatile tool for all your percentage calculation needs.</p>
        </header>

        <!-- Main Calculator Grid -->
        <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
            
            <!-- General Percentage Calculator -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg col-span-1 md:col-span-2" id="general-calculator">
                <h2 class="text-2xl font-semibold text-white mb-4 border-b border-gray-700 pb-2">General Calculator</h2>
                <p class="text-gray-400 mb-6">Enter any two values to find the third. Leave the field you want to calculate blank.</p>
                <form id="form-general">
                    <div class="flex flex-col sm:flex-row items-center justify-center gap-4 text-xl">
                        <input type="number" id="gen-val1" placeholder="Value" class="w-full sm:w-1/3 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition">
                        <span class="text-gray-400 font-medium">is what % of</span>
                        <input type="number" id="gen-val2" placeholder="Value" class="w-full sm:w-1/3 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition">
                    </div>
                     <div class="flex items-center justify-center gap-4 text-xl mt-4">
                        <span class="text-gray-400 font-medium">=</span>
                        <input type="number" id="gen-result" placeholder="Result (%)" class="w-full sm:w-1/3 p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition">
                    </div>
                    <div class="flex justify-center gap-4 mt-6">
                        <button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-6 rounded-lg transition">Calculate</button>
                        <button type="button" id="clear-general" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded-lg transition">Clear</button>
                    </div>
                </form>
            </div>

            <!-- Percentage Change Calculator -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg" id="change-calculator">
                <h2 class="text-2xl font-semibold text-white mb-4 border-b border-gray-700 pb-2">Percentage Change</h2>
                <p class="text-gray-400 mb-6">Calculate the result after an increase or decrease.</p>
                <form id="form-change">
                    <div class="space-y-4">
                        <div class="flex items-center gap-2">
                           <label for="change-from" class="w-24 text-gray-400">From</label>
                           <input type="number" id="change-from" placeholder="Initial Value" class="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" required>
                        </div>
                         <div class="flex items-center gap-2">
                           <label for="change-by" class="w-24 text-gray-400">Change By</label>
                           <div class="flex w-full">
                               <select id="change-type" class="p-3 rounded-l-md bg-gray-600 border border-gray-600 text-white outline-none">
                                   <option value="increase">Increase</option>
                                   <option value="decrease">Decrease</option>
                               </select>
                               <input type="number" id="change-by" placeholder="Percent (%)" class="w-full p-3 rounded-r-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" required>
                           </div>
                        </div>
                         <div class="flex items-center gap-2">
                           <label for="change-to" class="w-24 text-gray-400 font-bold">Result</label>
                           <input type="text" id="change-to" placeholder="Final Value" class="w-full p-3 rounded-md border border-gray-600 outline-none transition result-field" readonly>
                        </div>
                    </div>
                    <div class="flex justify-center gap-4 mt-6">
                        <button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-6 rounded-lg transition">Calculate</button>
                        <button type="button" id="clear-change" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded-lg transition">Clear</button>
                    </div>
                </form>
            </div>

            <!-- Percentage Difference Calculator -->
            <div class="bg-gray-800 p-6 rounded-xl shadow-lg" id="difference-calculator">
                <h2 class="text-2xl font-semibold text-white mb-4 border-b border-gray-700 pb-2">Percentage Difference</h2>
                 <p class="text-gray-400 mb-6">Calculate the percentage difference between two numbers.</p>
                <form id="form-difference">
                     <div class="space-y-4">
                        <div class="flex items-center gap-2">
                           <label for="diff-val1" class="w-24 text-gray-400">Value 1</label>
                           <input type="number" id="diff-val1" placeholder="First Value" class="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" required>
                        </div>
                         <div class="flex items-center gap-2">
                           <label for="diff-val2" class="w-24 text-gray-400">Value 2</label>
                           <input type="number" id="diff-val2" placeholder="Second Value" class="w-full p-3 rounded-md bg-gray-700 border border-gray-600 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" required>
                        </div>
                         <div class="flex items-center gap-2">
                           <label for="diff-result" class="w-24 text-gray-400 font-bold">Difference</label>
                           <input type="text" id="diff-result" placeholder="Result (%)" class="w-full p-3 rounded-md border border-gray-600 outline-none transition result-field" readonly>
                        </div>
                    </div>
                    <div class="flex justify-center gap-4 mt-6">
                        <button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-6 rounded-lg transition">Calculate</button>
                        <button type="button" id="clear-difference" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded-lg transition">Clear</button>
                    </div>
                </form>
            </div>
        </div>

        <!-- Explanations Section -->
        <div class="mt-12 md:mt-16 bg-gray-800 p-6 md:p-8 rounded-xl shadow-lg">
            <h2 class="text-3xl font-bold text-white mb-6">Understanding Percentages</h2>
            <div class="prose prose-invert max-w-none text-gray-300 space-y-4">
                <h3 class="text-xl font-semibold text-white">What is a percentage?</h3>
                <p>In mathematics, a percentage is a number or ratio that represents a fraction of 100. It is one of the ways to represent a dimensionless relationship between two numbers; other methods include ratios, fractions, and decimals. Percentages are often denoted by the symbol "%" written after the number. For example, 35% is equivalent to the decimal 0.35, or the fraction 35/100.</p>
                
                <h3 class="text-xl font-semibold text-white">Percentage Formula</h3>
                <p>Although the percentage formula can be written in different forms, it is essentially an algebraic equation involving three values: <code class="bg-gray-700 rounded px-2 py-1">P &times; V<sub>1</sub> = V<sub>2</sub></code>. Here, P is the percentage in decimal form, V<sub>1</sub> is the base value, and V<sub>2</sub> is the result.</p>

                <h3 class="text-xl font-semibold text-white">Percentage Difference Formula</h3>
                <p>The percentage difference between two values is calculated by dividing the absolute value of the difference between them by their average, then multiplying by 100. Formula: <code class="bg-gray-700 rounded px-2 py-1">|V<sub>1</sub> - V<sub>2</sub>| / ((V<sub>1</sub> + V<sub>2</sub>)/2) &times; 100</code>.</p>

                <h3 class="text-xl font-semibold text-white">Percentage Change Formula</h3>
                <p>Percentage change is calculated by dividing the difference between the new and old values by the old value. For an increase from 500 by 10%, the calculation is <code class="bg-gray-700 rounded px-2 py-1">500 &times; (1 + 0.10) = 550</code>. For a decrease, it's <code class="bg-gray-700 rounded px-2 py-1">500 &times; (1 - 0.10) = 450</code>.</p>
            </div>
        </div>

    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {

            // Helper function to parse float from input value
            const getFloat = (id) => {
                const value = document.getElementById(id).value;
                return value ? parseFloat(value) : null;
            };

            // Helper function to format the output
            const formatOutput = (value) => {
                // Avoid scientific notation for very small or large numbers
                if (Math.abs(value) < 1e-6 || Math.abs(value) > 1e15) {
                    return value.toExponential(4);
                }
                // Format to a reasonable number of decimal places
                return parseFloat(value.toFixed(10));
            };

            // --- General Calculator Logic ---
            const formGeneral = document.getElementById('form-general');
            const genVal1 = document.getElementById('gen-val1');
            const genVal2 = document.getElementById('gen-val2');
            const genResult = document.getElementById('gen-result');

            formGeneral.addEventListener('submit', (e) => {
                e.preventDefault();
                
                const v1 = getFloat('gen-val1');
                const v2 = getFloat('gen-val2');
                const res = getFloat('gen-result');

                const inputs = [v1, v2, res].filter(v => v !== null);

                if (inputs.length !== 2) {
                    alert('Please provide exactly two values.');
                    return;
                }
                
                try {
                    if (v1 !== null && v2 !== null) { // Calculate percentage
                        if (v2 === 0) throw new Error("Cannot divide by zero.");
                        genResult.value = formatOutput((v1 / v2) * 100);
                    } else if (v1 !== null && res !== null) { // Calculate 'of what' value
                        if (res === 0) throw new Error("Cannot divide by zero for percentage.");
                        genVal2.value = formatOutput(v1 / (res / 100));
                    } else if (v2 !== null && res !== null) { // Calculate 'what is' value
                        genVal1.value = formatOutput((res / 100) * v2);
                    }
                } catch(error) {
                    alert(error.message);
                }
            });

            document.getElementById('clear-general').addEventListener('click', () => {
                formGeneral.reset();
            });


            // --- Percentage Change Calculator Logic ---
            const formChange = document.getElementById('form-change');
            formChange.addEventListener('submit', (e) => {
                e.preventDefault();
                const from = getFloat('change-from');
                const by = getFloat('change-by');
                const type = document.getElementById('change-type').value;

                if (from === null || by === null) {
                    alert('Please fill in both "From" and "Change By" fields.');
                    return;
                }
                
                let result;
                if (type === 'increase') {
                    result = from * (1 + by / 100);
                } else {
                    result = from * (1 - by / 100);
                }
                document.getElementById('change-to').value = formatOutput(result);
            });

            document.getElementById('clear-change').addEventListener('click', () => {
                formChange.reset();
                document.getElementById('change-to').value = '';
            });


            // --- Percentage Difference Calculator Logic ---
            const formDifference = document.getElementById('form-difference');
            formDifference.addEventListener('submit', (e) => {
                e.preventDefault();
                const val1 = getFloat('diff-val1');
                const val2 = getFloat('diff-val2');

                if (val1 === null || val2 === null) {
                    alert('Please fill in both Value 1 and Value 2.');
                    return;
                }

                if (val1 + val2 === 0) {
                    alert("The sum of the values cannot be zero, as it leads to division by zero.");
                    return;
                }

                const difference = Math.abs(val1 - val2);
                const average = (val1 + val2) / 2;
                const result = (difference / Math.abs(average)) * 100;

                document.getElementById('diff-result').value = `${formatOutput(result)}%`;
            });

            document.getElementById('clear-difference').addEventListener('click', () => {
                formDifference.reset();
                document.getElementById('diff-result').value = '';
            });

        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/percentage-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Salary Paycheck Calculator – Calculate Net Income</title>
		<link>https://techaiconnect.com/salary-paycheck-calculator/</link>
					<comments>https://techaiconnect.com/salary-paycheck-calculator/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 12:43:34 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4246</guid>

					<description><![CDATA[Advanced Salary Paycheck Calculator Advanced Salary Paycheck Calculator Get a detailed estimate of your take-home [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Advanced Salary Paycheck Calculator</title>
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Google Fonts: Inter -->
    <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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        /* Custom styles for a better dark mode experience */
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* bg-gray-900 */
            color: #d1d5db; /* text-gray-300 */
        }
        /* Custom focus ring color */
        .form-input:focus, .form-select:focus {
            outline: none;
            border-color: #3b82f6; /* border-blue-500 */
            box-shadow: 0 0 0 2px #1e40af; /* ring-blue-500/50 */
        }
        .form-input, .form-select {
            background-color: #1f2937; /* bg-gray-800 */
            border-color: #4b5563; /* border-gray-600 */
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
        }
        .form-select {
             background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
            background-position: right 0.5rem center;
            background-repeat: no-repeat;
            background-size: 1.5em 1.5em;
            padding-right: 2.5rem;
        }
        .result-card {
            background-color: #1f2937; /* bg-gray-800 */
            border: 1px solid #4b5563; /* border-gray-600 */
        }
        fieldset {
            border-color: #4b5563;
        }
        legend {
            color: #93c5fd; /* text-blue-300 */
        }
        .seo-content h2 {
            font-size: 1.875rem; /* text-3xl */
            font-weight: 700; /* font-bold */
            margin-top: 2rem; /* mt-8 */
            margin-bottom: 1rem; /* mb-4 */
            color: #ffffff;
            border-bottom: 2px solid #3b82f6; /* border-blue-500 */
            padding-bottom: 0.5rem;
        }
        .seo-content h3 {
            font-size: 1.5rem; /* text-2xl */
            font-weight: 600; /* font-semibold */
            margin-top: 1.5rem; /* mt-6 */
            margin-bottom: 0.75rem; /* mb-3 */
            color: #e5e7eb; /* text-gray-200 */
        }
        .seo-content p, .seo-content li {
            line-height: 1.75;
            margin-bottom: 1rem;
        }
        .seo-content ul {
            list-style-type: disc;
            padding-left: 1.5rem;
        }
        .seo-content ol {
            list-style-type: decimal;
            padding-left: 1.5rem;
        }
        .seo-content a {
            color: #60a5fa; /* text-blue-400 */
            text-decoration: underline;
        }
        .seo-content a:hover {
            color: #93c5fd; /* text-blue-300 */
        }
    </style>
</head>
<body class="antialiased">

    <div class="max-w-7xl mx-auto p-4 sm:p-6 lg:p-8">
        
        <!-- Calculator Section -->
        <div class="bg-gray-800 rounded-2xl shadow-2xl p-6 md:p-8">
            <h1 class="text-3xl md:text-4xl font-bold text-white text-center mb-2">Advanced Salary Paycheck Calculator</h1>
            <p class="text-center text-gray-400 mb-8">Get a detailed estimate of your take-home pay.</p>

            <div class="grid grid-cols-1 lg:grid-cols-5 gap-8">
                
                <!-- Input Form -->
                <div class="lg:col-span-3 space-y-6">
                    
                    <!-- Earnings Section -->
                    <fieldset class="border rounded-lg p-4">
                        <legend class="text-lg font-medium px-2">Earnings (per pay period)</legend>
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-6 mt-4">
                            <div>
                                <label for="regular-pay" class="block text-sm font-medium text-gray-300 mb-1">Regular Pay</label>
                                <input type="number" id="regular-pay" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 2500">
                            </div>
                            <div>
                                <label for="pay-frequency" class="block text-sm font-medium text-gray-300 mb-1">Pay Frequency</label>
                                <select id="pay-frequency" class="form-select w-full rounded-md shadow-sm text-white">
                                    <option value="weekly">Weekly</option>
                                    <option value="bi-weekly" selected>Bi-Weekly</option>
                                    <option value="semi-monthly">Semi-Monthly</option>
                                    <option value="monthly">Monthly</option>
                                    <option value="annually">Annually</option>
                                </select>
                            </div>
                             <div>
                                <label for="overtime-pay" class="block text-sm font-medium text-gray-300 mb-1">Overtime Pay</label>
                                <input type="number" id="overtime-pay" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 200">
                            </div>
                             <div>
                                <label for="bonus-pay" class="block text-sm font-medium text-gray-300 mb-1">Bonus</label>
                                <input type="number" id="bonus-pay" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 500">
                            </div>
                        </div>
                    </fieldset>

                    <!-- Federal Taxes Section -->
                    <fieldset class="border rounded-lg p-4">
                        <legend class="text-lg font-medium px-2">Federal Taxes</legend>
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-6 mt-4">
                            <div>
                                <label for="filing-status" class="block text-sm font-medium text-gray-300 mb-1">Filing Status</label>
                                <select id="filing-status" class="form-select w-full rounded-md shadow-sm text-white">
                                    <option value="single">Single</option>
                                    <option value="married">Married Filing Jointly</option>
                                    <option value="hoh">Head of Household</option>
                                </select>
                            </div>
                            <div>
                                <label for="dependents" class="block text-sm font-medium text-gray-300 mb-1">Number of Dependents</label>
                                <input type="number" id="dependents" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 2">
                            </div>
                        </div>
                    </fieldset>

                    <!-- State Taxes Section -->
                    <fieldset class="border rounded-lg p-4">
                        <legend class="text-lg font-medium px-2">State & Local Taxes</legend>
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-6 mt-4">
                             <div>
                                <label for="state" class="block text-sm font-medium text-gray-300 mb-1">State or Territory</label>
                                <select id="state" class="form-select w-full rounded-md shadow-sm text-white"></select>
                            </div>
                             <div>
                                <label for="local-tax" class="block text-sm font-medium text-gray-300 mb-1">Local Income Tax (%)</label>
                                <input type="number" id="local-tax" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 1.5">
                            </div>
                        </div>
                    </fieldset>

                    <!-- Benefits Section -->
                    <fieldset class="border rounded-lg p-4">
                        <legend class="text-lg font-medium px-2">Benefits & Deductions (per pay period)</legend>
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-6 mt-4">
                            <div>
                                <label for="retirement-401k" class="block text-sm font-medium text-gray-300 mb-1">401(k) / Retirement (%)</label>
                                <input type="number" id="retirement-401k" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 5">
                            </div>
                             <div>
                                <label for="health-insurance" class="block text-sm font-medium text-gray-300 mb-1">Health Insurance ($)</label>
                                <input type="number" id="health-insurance" class="form-input w-full rounded-md shadow-sm text-white" placeholder="e.g., 150">
                            </div>
                            <div>
                                <label for="other-pre-tax" class="block text-sm font-medium text-gray-300 mb-1">Other Pre-Tax ($)</label>
                                <input type="number" id="other-pre-tax" class="form-input w-full rounded-md shadow-sm text-white" placeholder="HSA, FSA">
                            </div>
                            <div>
                                <label for="post-tax-deductions" class="block text-sm font-medium text-gray-300 mb-1">Post-Tax Deductions ($)</label>
                                <input type="number" id="post-tax-deductions" class="form-input w-full rounded-md shadow-sm text-white" placeholder="Roth, Garnishments">
                            </div>
                        </div>
                    </fieldset>
                    
                    <button id="calculate-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105">
                        Calculate Paycheck
                    </button>
                </div>

                <!-- Results Display -->
                <div class="lg:col-span-2 result-card rounded-2xl p-6 flex flex-col">
                    <h2 class="text-2xl font-bold text-white mb-4 text-center">Your Estimated Paycheck</h2>
                    <div id="results-summary" class="space-y-3 flex-grow">
                        <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">Gross Pay</span>
                            <span id="gross-pay-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                         <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">Pre-Tax Deductions</span>
                            <span id="pre-tax-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                        <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">Federal Tax</span>
                            <span id="federal-tax-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                        <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">FICA (SS & Medicare)</span>
                            <span id="fica-tax-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                        <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">State & Local Tax</span>
                            <span id="state-local-tax-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                        <div class="flex justify-between items-center py-2 border-b border-gray-700">
                            <span class="text-gray-400">Post-Tax Deductions</span>
                            <span id="post-tax-result" class="font-semibold text-white text-lg">$0.00</span>
                        </div>
                        <div class="flex justify-between items-center py-2 text-red-400">
                            <span class="font-medium">Total Reductions</span>
                            <span id="total-deductions-result" class="font-bold text-lg">$0.00</span>
                        </div>
                    </div>
                    <div class="mt-6 pt-4 border-t-2 border-blue-500 text-center">
                        <p class="text-gray-300 text-lg">Net Take-Home Pay</p>
                        <p id="net-pay-result" class="text-4xl font-extrabold text-green-400 mt-2">$0.00</p>
                    </div>
                </div>

            </div>
             <div id="disclaimer" class="text-center text-xs text-gray-500 mt-8">
                <strong>Important Note:</strong> This calculator provides general guidance and estimates based on 2024 tax data. It should not be relied upon to calculate exact taxes or financial data. Please consult a professional financial advisor for specific concerns.
            </div>
        </div>

        <!-- SEO Content Section (Unchanged) -->
        <div class="mt-12 seo-content">
            <h2>Salary Paycheck Calculator Guide</h2>
            <p>Although our salary paycheck calculator does much of the heavy lifting, it may be helpful to take a closer look at a few of the calculations that are essential to payroll.</p>
            <h3>How to Calculate Net Income</h3>
            <ol>
                <li>Determine taxable income by deducting any pre-tax contributions to benefits.</li>
                <li>Withhold all applicable taxes (federal, state and local).</li>
                <li>Deduct any post-tax contributions to benefits.</li>
                <li>Garnish wages, if necessary.</li>
                <li>The result is net income (or take-home pay).</li>
            </ol>
            <h2>What is a Paycheck?</h2>
            <p>A paycheck is how businesses compensate employees for their work. The most common delivery schedules are bi-weekly and semi-monthly, though this varies based on employer preferences and applicable state laws and regulations.</p>
            <h2>Frequently Asked Questions About Paychecks</h2>
            <h3>Is a pay stub the same as a paycheck?</h3>
            <p>Although paychecks and pay stubs are generally provided together, they are not one in the same. A paycheck is a directive to a financial institution that approves the transfer of funds from the employer to the employee. A pay stub, on the other hand, has no monetary value and is simply an explanatory document.</p>
        </div>
    </div>

    <script>
        // --- DATA (Constants and State Information) ---
        const FICA_RATES_2024 = {
            SOCIAL_SECURITY_RATE: 0.062,
            SOCIAL_SECURITY_LIMIT: 168600,
            MEDICARE_RATE: 0.0145,
            ADDITIONAL_MEDICARE_RATE: 0.009,
            ADDITIONAL_MEDICARE_THRESHOLDS: { single: 200000, married: 250000, hoh: 200000 },
        };

        const FEDERAL_TAX_INFO_2024 = {
            BRACKETS: {
                single: [
                    { rate: 0.10, from: 0, to: 11600 }, { rate: 0.12, from: 11601, to: 47150 },
                    { rate: 0.22, from: 47151, to: 100525 }, { rate: 0.24, from: 100526, to: 191950 },
                    { rate: 0.32, from: 191951, to: 243725 }, { rate: 0.35, from: 243726, to: 609350 },
                    { rate: 0.37, from: 609351, to: Infinity },
                ],
                married: [
                    { rate: 0.10, from: 0, to: 23200 }, { rate: 0.12, from: 23201, to: 94300 },
                    { rate: 0.22, from: 94301, to: 201050 }, { rate: 0.24, from: 201051, to: 383900 },
                    { rate: 0.32, from: 383901, to: 487450 }, { rate: 0.35, from: 487451, to: 731200 },
                    { rate: 0.37, from: 731201, to: Infinity },
                ],
                hoh: [
                    { rate: 0.10, from: 0, to: 16550 }, { rate: 0.12, from: 16551, to: 63100 },
                    { rate: 0.22, from: 63101, to: 100500 }, { rate: 0.24, from: 100501, to: 191950 },
                    { rate: 0.32, from: 191951, to: 243700 }, { rate: 0.35, from: 243701, to: 609350 },
                    { rate: 0.37, from: 609351, to: Infinity },
                ]
            },
            STANDARD_DEDUCTIONS: { single: 14600, married: 29200, hoh: 21900 },
            DEPENDENT_CREDIT_AMOUNT: 2000
        };
        
        // Simplified state tax data for estimation. This is a complex area.
        const STATES = {
            "AL": { name: "Alabama", rate: 0.05 }, "AK": { name: "Alaska", rate: 0 },
            "AZ": { name: "Arizona", rate: 0.025 }, "AR": { name: "Arkansas", rate: 0.049 },
            "CA": { name: "California", rate: 0.093 }, "CO": { name: "Colorado", rate: 0.044 },
            "CT": { name: "Connecticut", rate: 0.0699 }, "DE": { name: "Delaware", rate: 0.066 },
            "DC": { name: "District of Columbia", rate: 0.085 }, "FL": { name: "Florida", rate: 0 },
            "GA": { name: "Georgia", rate: 0.0549 }, "HI": { name: "Hawaii", rate: 0.11 },
            "ID": { name: "Idaho", rate: 0.058 }, "IL": { name: "Illinois", rate: 0.0495 },
            "IN": { name: "Indiana", rate: 0.0315 }, "IA": { name: "Iowa", rate: 0.06 },
            "KS": { name: "Kansas", rate: 0.057 }, "KY": { name: "Kentucky", rate: 0.045 },
            "LA": { name: "Louisiana", rate: 0.0425 }, "ME": { name: "Maine", rate: 0.0715 },
            "MD": { name: "Maryland", rate: 0.0575 }, "MA": { name: "Massachusetts", rate: 0.05 },
            "MI": { name: "Michigan", rate: 0.0425 }, "MN": { name: "Minnesota", rate: 0.0985 },
            "MS": { name: "Mississippi", rate: 0.05 }, "MO": { name: "Missouri", rate: 0.0495 },
            "MT": { name: "Montana", rate: 0.0675 }, "NE": { name: "Nebraska", rate: 0.0664 },
            "NV": { name: "Nevada", rate: 0 }, "NH": { name: "New Hampshire", rate: 0 },
            "NJ": { name: "New Jersey", rate: 0.1075 }, "NM": { name: "New Mexico", rate: 0.059 },
            "NY": { name: "New York", rate: 0.109 }, "NC": { name: "North Carolina", rate: 0.0475 },
            "ND": { name: "North Dakota", rate: 0.029 }, "OH": { name: "Ohio", rate: 0.0399 },
            "OK": { name: "Oklahoma", rate: 0.0475 }, "OR": { name: "Oregon", rate: 0.099 },
            "PA": { name: "Pennsylvania", rate: 0.0307 }, "RI": { name: "Rhode Island", rate: 0.0599 },
            "SC": { name: "South Carolina", rate: 0.065 }, "SD": { name: "South Dakota", rate: 0 },
            "TN": { name: "Tennessee", rate: 0 }, "TX": { name: "Texas", rate: 0 },
            "UT": { name: "Utah", rate: 0.0465 }, "VT": { name: "Vermont", rate: 0.0875 },
            "VA": { name: "Virginia", rate: 0.0575 }, "WA": { name: "Washington", rate: 0 },
            "WV": { name: "West Virginia", rate: 0.0512 }, "WI": { name: "Wisconsin", rate: 0.0765 },
            "WY": { name: "Wyoming", rate: 0 }
        };

        // --- DOM Elements ---
        const calculateBtn = document.getElementById('calculate-btn');
        const stateSelect = document.getElementById('state');

        // --- Initial Setup ---
        window.onload = () => {
            populateStates();
            calculateBtn.addEventListener('click', runCalculation);
        };

        function populateStates() {
            Object.keys(STATES).forEach(key => {
                const option = document.createElement('option');
                option.value = key;
                option.textContent = STATES[key].name;
                stateSelect.appendChild(option);
            });
            stateSelect.value = "CA"; // Default to California
        }

        // --- Main Calculation Orchestrator ---
        function runCalculation() {
            // 1. Get all inputs
            const inputs = getInputs();
            if (!inputs) return; // Stop if validation fails

            // 2. Calculate annual figures
            const payPeriods = getPayPeriods(inputs.payFrequency);
            const annualGrossPay = (inputs.regularPay + inputs.overtimePay + inputs.bonusPay) * payPeriods;
            
            // 3. Calculate pre-tax deductions
            const retirementDeductionPerCheck = (inputs.regularPay + inputs.overtimePay) * (inputs.retirement401k / 100);
            const preTaxDeductionsPerCheck = retirementDeductionPerCheck + inputs.healthInsurance + inputs.otherPreTax;
            const annualPreTaxDeductions = preTaxDeductionsPerCheck * payPeriods;

            // 4. Calculate annual taxes
            const annualFederalTax = calculateFederalTax(annualGrossPay, annualPreTaxDeductions, inputs.filingStatus, inputs.dependents);
            const annualFicaTax = calculateFicaTax(annualGrossPay, inputs.filingStatus);
            const annualStateTax = calculateStateTax(inputs.state, annualGrossPay, annualPreTaxDeductions);
            const annualLocalTax = (annualGrossPay - annualPreTaxDeductions) * (inputs.localTax / 100);

            // 5. Convert to per-check figures
            const grossPayPerCheck = annualGrossPay / payPeriods;
            const federalTaxPerCheck = annualFederalTax / payPeriods;
            const ficaTaxPerCheck = annualFicaTax / payPeriods;
            const stateLocalTaxPerCheck = (annualStateTax + annualLocalTax) / payPeriods;
            const postTaxDeductionsPerCheck = inputs.postTaxDeductions;

            // 6. Calculate final net pay
            const totalReductions = preTaxDeductionsPerCheck + federalTaxPerCheck + ficaTaxPerCheck + stateLocalTaxPerCheck + postTaxDeductionsPerCheck;
            const netPayPerCheck = grossPayPerCheck - totalReductions;

            // 7. Display results
            updateResultsUI({
                grossPayPerCheck,
                preTaxDeductionsPerCheck,
                federalTaxPerCheck,
                ficaTaxPerCheck,
                stateLocalTaxPerCheck,
                postTaxDeductionsPerCheck,
                totalReductions,
                netPayPerCheck
            });
        }
        
        // --- Helper & Calculation Functions ---

        function getInputs() {
            const regularPay = parseFloat(document.getElementById('regular-pay').value) || 0;
            if (regularPay <= 0) {
                alert("Please enter a valid Regular Pay amount.");
                return null;
            }
            return {
                regularPay,
                overtimePay: parseFloat(document.getElementById('overtime-pay').value) || 0,
                bonusPay: parseFloat(document.getElementById('bonus-pay').value) || 0,
                payFrequency: document.getElementById('pay-frequency').value,
                filingStatus: document.getElementById('filing-status').value,
                dependents: parseInt(document.getElementById('dependents').value) || 0,
                state: document.getElementById('state').value,
                localTax: parseFloat(document.getElementById('local-tax').value) || 0,
                retirement401k: parseFloat(document.getElementById('retirement-401k').value) || 0,
                healthInsurance: parseFloat(document.getElementById('health-insurance').value) || 0,
                otherPreTax: parseFloat(document.getElementById('other-pre-tax').value) || 0,
                postTaxDeductions: parseFloat(document.getElementById('post-tax-deductions').value) || 0,
            };
        }

        function getPayPeriods(frequency) {
            const periods = { weekly: 52, 'bi-weekly': 26, 'semi-monthly': 24, monthly: 12, annually: 1 };
            return periods[frequency] || 26;
        }

        function calculateFederalTax(annualGross, annualPreTax, status, dependents) {
            const { BRACKETS, STANDARD_DEDUCTIONS, DEPENDENT_CREDIT_AMOUNT } = FEDERAL_TAX_INFO_2024;
            const standardDeduction = STANDARD_DEDUCTIONS[status] || 0;
            const taxableIncome = Math.max(0, annualGross - annualPreTax - standardDeduction);
            
            let tax = 0;
            const brackets = BRACKETS[status];
            for (const bracket of brackets) {
                if (taxableIncome > bracket.from) {
                    const taxableInBracket = Math.min(taxableIncome, bracket.to) - bracket.from;
                    tax += taxableInBracket * bracket.rate;
                }
            }
            
            const dependentCredit = dependents * DEPENDENT_CREDIT_AMOUNT;
            return Math.max(0, tax - dependentCredit);
        }

        function calculateFicaTax(annualGross, status) {
            const { SOCIAL_SECURITY_RATE, SOCIAL_SECURITY_LIMIT, MEDICARE_RATE, ADDITIONAL_MEDICARE_RATE, ADDITIONAL_MEDICARE_THRESHOLDS } = FICA_RATES_2024;
            const socialSecurityTax = Math.min(annualGross, SOCIAL_SECURITY_LIMIT) * SOCIAL_SECURITY_RATE;
            
            let medicareTax = annualGross * MEDICARE_RATE;
            const threshold = ADDITIONAL_MEDICARE_THRESHOLDS[status];
            if (annualGross > threshold) {
                medicareTax += (annualGross - threshold) * ADDITIONAL_MEDICARE_RATE;
            }
            
            return socialSecurityTax + medicareTax;
        }

        function calculateStateTax(stateAbbr, annualGross, annualPreTax) {
            const stateInfo = STATES[stateAbbr];
            if (!stateInfo || stateInfo.rate === 0) return 0;
            
            // This is a simplification using a flat rate. Real state taxes can be progressive.
            const taxableIncome = Math.max(0, annualGross - annualPreTax);
            return taxableIncome * stateInfo.rate;
        }

        function formatCurrency(amount) {
            return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount);
        }

        function updateResultsUI(data) {
            document.getElementById('gross-pay-result').textContent = formatCurrency(data.grossPayPerCheck);
            document.getElementById('pre-tax-result').textContent = formatCurrency(data.preTaxDeductionsPerCheck);
            document.getElementById('federal-tax-result').textContent = formatCurrency(data.federalTaxPerCheck);
            document.getElementById('fica-tax-result').textContent = formatCurrency(data.ficaTaxPerCheck);
            document.getElementById('state-local-tax-result').textContent = formatCurrency(data.stateLocalTaxPerCheck);
            document.getElementById('post-tax-result').textContent = formatCurrency(data.postTaxDeductionsPerCheck);
            document.getElementById('total-deductions-result').textContent = formatCurrency(data.totalReductions);
            document.getElementById('net-pay-result').textContent = formatCurrency(data.netPayPerCheck);
        }

    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/salary-paycheck-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Compound Interest Calculator</title>
		<link>https://techaiconnect.com/compound-interest-calculator/</link>
					<comments>https://techaiconnect.com/compound-interest-calculator/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 11:27:23 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4242</guid>

					<description><![CDATA[Compound Interest Calculator Compound Interest Calculator Plan your financial future today. Initial Principal Monthly Contribution [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Compound Interest Calculator</title>
    <!-- Tailwind CSS for styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Chart.js for data visualization -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <!-- Google Fonts: Inter -->
    <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=Inter:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        /* Custom styles to apply the font and theme */
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* Tailwind's bg-gray-900 */
            color: #d1d5db; /* Tailwind's text-gray-300 */
        }
        /* Custom styles for the informational prose section */
        .prose-custom h3 {
            color: #818cf8; /* Tailwind's text-indigo-400 */
        }
        .prose-custom ul > li::before {
            background-color: #a78bfa; /* Tailwind's bg-purple-400 */
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 md:p-8 max-w-5xl">
        <!-- Header Section -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-purple-500">Compound Interest Calculator</h1>
            <p class="text-gray-400 mt-2 text-lg">Plan your financial future today.</p>
        </header>

        <main class="space-y-12">
            <!-- Calculator Tool Section -->
            <section class="bg-gray-800 p-6 rounded-2xl shadow-lg border border-gray-700">
                <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
                    <!-- Left Column: Input Fields -->
                    <div class="lg:col-span-1 space-y-4">
                        <div>
                            <label for="initial-principal" class="block mb-2 text-sm font-medium text-gray-300">Initial Principal</label>
                            <input type="text" id="initial-principal" value="$10,000" class="currency-input bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="monthly-contribution" class="block mb-2 text-sm font-medium text-gray-300">Monthly Contribution</label>
                            <input type="text" id="monthly-contribution" value="$500" class="currency-input bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="interest-rate" class="block mb-2 text-sm font-medium text-gray-300">Annual Interest Rate (%)</label>
                            <input type="number" id="interest-rate" value="7" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="years" class="block mb-2 text-sm font-medium text-gray-300">Investment Period (Years)</label>
                            <input type="number" id="years" value="15" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                        </div>
                        <div>
                            <label for="compound-frequency" class="block mb-2 text-sm font-medium text-gray-300">Compound Frequency</label>
                            <select id="compound-frequency" class="bg-gray-700 border border-gray-600 text-white text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5">
                                <option value="12">Monthly</option>
                                <option value="4">Quarterly</option>
                                <option value="2">Semiannually</option>
                                <option value="1" selected>Annually</option>
                            </select>
                        </div>
                    </div>

                    <!-- Right Column: Results & Chart -->
                    <div class="lg:col-span-2 space-y-6">
                        <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 text-center">
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">Total Contribution</p>
                                <p id="total-principal" class="text-2xl font-bold text-white">$0</p>
                            </div>
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">Total Interest Earned</p>
                                <p id="total-interest" class="text-2xl font-bold text-green-400">$0</p>
                            </div>
                            <div class="bg-gray-900/50 p-4 rounded-lg">
                                <p class="text-sm text-gray-400">Future Value</p>
                                <p id="future-value" class="text-2xl font-bold text-purple-400">$0</p>
                            </div>
                        </div>
                        <div>
                            <canvas id="compound-chart"></canvas>
                        </div>
                    </div>
                </div>
            </section>

            <!-- Informational Section -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="prose prose-invert max-w-none text-gray-300 prose-custom">
                    <h2 class="text-2xl font-bold text-center mb-6 text-indigo-400">Compound Interest: The 8th Wonder of the World</h2>
                    
                    <h3>What is Compound Interest?</h3>
                    <p>Compound interest is the process where the interest you earn on an investment is added to the principal amount, and this new, larger principal then earns interest itself. This "interest on interest" effect causes your wealth to grow at an accelerating, exponential rate, which is especially powerful over long periods.</p>
                    
                    <h3>The Compound Interest Formula</h3>
                    <p>This calculator uses the future value formula that includes regular contributions:</p>
                    <p class="text-center font-mono bg-gray-900/50 p-3 rounded-md text-sm">A = P(1 + r/n)<sup>nt</sup> + PMT × [ ((1 + r/n)<sup>nt</sup> - 1) / (r/n) ]</p>
                    <ul class="text-sm">
                        <li><strong>A</strong>: The future value of the investment/loan, including interest.</li>
                        <li><strong>P</strong>: The principal investment amount (the initial deposit).</li>
                        <li><strong>PMT</strong>: The monthly contribution.</li>
                        <li><strong>r</strong>: The annual interest rate (in decimal form).</li>
                        <li><strong>n</strong>: The number of times that interest is compounded per year.</li>
                        <li><strong>t</strong>: The number of years the money is invested for.</li>
                    </ul>

                    <h3>Tips for Maximizing Compound Growth</h3>
                    <p>To make the most of compound interest, remember three golden rules:</p>
                    <ul>
                        <li><strong>Start as early as possible:</strong> Time is your greatest asset. Starting early, even with a small amount, can make a huge difference in the long run.</li>
                        <li><strong>Invest regularly and be disciplined:</strong> Making consistent contributions significantly accelerates the growth of your investment.</li>
                        <li><strong>Optimize your interest rate:</strong> Seek out safe investments with competitive interest rates. Even a small increase in your rate can lead to massive gains over many years.</li>
                    </ul>
                </div>
            </section>
        </main>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // --- DOM ELEMENT REFERENCES ---
            const initialPrincipalInput = document.getElementById('initial-principal');
            const monthlyContributionInput = document.getElementById('monthly-contribution');
            const interestRateInput = document.getElementById('interest-rate');
            const yearsInput = document.getElementById('years');
            const compoundFrequencySelect = document.getElementById('compound-frequency');
            
            const futureValueDisplay = document.getElementById('future-value');
            const totalPrincipalDisplay = document.getElementById('total-principal');
            const totalInterestDisplay = document.getElementById('total-interest');
            
            const chartCanvas = document.getElementById('compound-chart');
            let compoundChart; // Variable to hold the chart instance

            // --- HELPER FUNCTIONS ---
            // Formats a number into USD currency string
            const formatCurrency = (value) => {
                if (isNaN(value) || value === null) return '$0';
                return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
            };

            // Parses a currency string into a number
            const parseCurrency = (value) => {
                return Number(String(value).replace(/[^0-9.-]+/g, ''));
            };

            // --- MAIN CALCULATION LOGIC ---
            function calculateAndDisplay() {
                // Parse input values
                const P = parseCurrency(initialPrincipalInput.value);
                const PMT_monthly = parseCurrency(monthlyContributionInput.value);
                const annualRate = parseFloat(interestRateInput.value) / 100;
                const t = parseInt(yearsInput.value);
                const n = parseInt(compoundFrequencySelect.value);

                // Basic validation
                if (isNaN(P) || isNaN(PMT_monthly) || isNaN(annualRate) || isNaN(t) || isNaN(n) || t <= 0) {
                    return; // Exit if inputs are not valid
                }

                // Core formula variables
                const r = annualRate;
                const nt = n * t;
                const r_over_n = r / n;
                
                // Adjust the monthly payment to match the compounding period's contribution
                const PMT = PMT_monthly * (12 / n);
                
                // Calculate the future value of the initial principal
                const fv_principal = P * Math.pow(1 + r_over_n, nt);

                // Calculate the future value of the series of contributions (annuity)
                let fv_contributions = 0;
                if (r > 0) {
                    fv_contributions = PMT * ((Math.pow(1 + r_over_n, nt) - 1) / r_over_n);
                } else { // Handle the case of 0% interest
                    fv_contributions = PMT * nt;
                }

                // Calculate final totals
                const A = fv_principal + fv_contributions;
                const totalContribution = P + (PMT_monthly * 12 * t);
                const totalInterest = A - totalContribution;

                // Display the results
                futureValueDisplay.textContent = formatCurrency(A);
                totalPrincipalDisplay.textContent = formatCurrency(totalContribution);
                totalInterestDisplay.textContent = formatCurrency(totalInterest);

                // Update the chart with the new data
                updateChart(P, PMT_monthly, r, t, n);
            }

            // --- CHART UPDATE LOGIC ---
            function updateChart(P, PMT_monthly, r, t, n) {
                const labels = Array.from({ length: t + 1 }, (_, i) => `Year ${i}`);
                const contributionData = [];
                const futureValueData = [];

                // Generate data points for each year
                for (let i = 0; i <= t; i++) {
                    // Total amount contributed up to year 'i'
                    const currentContribution = P + (PMT_monthly * 12 * i);
                    contributionData.push(currentContribution);

                    // Calculate future value at year 'i'
                    const nt = n * i;
                    const r_over_n = r / n;
                    const PMT = PMT_monthly * (12 / n);

                    const fv_p = P * Math.pow(1 + r_over_n, nt);
                    let fv_c = 0;
                     if (r > 0) {
                        fv_c = PMT * ((Math.pow(1 + r_over_n, nt) - 1) / r_over_n);
                    } else {
                        fv_c = PMT * nt;
                    }
                    futureValueData.push(fv_p + fv_c);
                }

                // If a chart instance already exists, destroy it before creating a new one
                if (compoundChart) {
                    compoundChart.destroy();
                }

                // Create a new Chart.js instance
                compoundChart = new Chart(chartCanvas, {
                    type: 'line',
                    data: {
                        labels: labels,
                        datasets: [
                            {
                                label: 'Total Contribution',
                                data: contributionData,
                                borderColor: '#9ca3af', // gray-400
                                backgroundColor: '#9ca3af',
                                fill: false,
                                tension: 0.1
                            },
                            {
                                label: 'Future Value',
                                data: futureValueData,
                                borderColor: '#a78bfa', // purple-400
                                backgroundColor: 'rgba(167, 139, 250, 0.2)', // purple-400 with opacity
                                fill: true,
                                tension: 0.1
                            }
                        ]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                position: 'top',
                                labels: { color: '#d1d5db' } // text-gray-300
                            },
                            tooltip: {
                                mode: 'index',
                                intersect: false,
                                callbacks: {
                                    label: function(context) {
                                        let label = context.dataset.label || '';
                                        if (label) {
                                            label += ': ';
                                        }
                                        if (context.parsed.y !== null) {
                                            label += formatCurrency(context.parsed.y);
                                        }
                                        return label;
                                    }
                                }
                            }
                        },
                        scales: {
                            x: {
                                ticks: { color: '#9ca3af' }, // text-gray-400
                                grid: { color: '#374151' } // bg-gray-700
                            },
                            y: {
                                ticks: { 
                                    color: '#9ca3af', // text-gray-400
                                    callback: function(value) {
                                        // Format Y-axis labels as currency
                                        return formatCurrency(value);
                                    }
                                },
                                grid: { color: '#374151' } // bg-gray-700
                            }
                        }
                    }
                });
            }

            // --- EVENT LISTENERS ---
            // Recalculate whenever any input changes
            const inputs = [initialPrincipalInput, monthlyContributionInput, interestRateInput, yearsInput, compoundFrequencySelect];
            inputs.forEach(input => input.addEventListener('input', calculateAndDisplay));

            // Add auto-formatting to currency inputs
            document.querySelectorAll('.currency-input').forEach(input => {
                input.addEventListener('blur', (e) => { // Format on blur to avoid cursor jumping
                    const value = parseCurrency(e.target.value);
                    e.target.value = formatCurrency(value).replace(/\.00$/, ''); // Remove cents for whole numbers
                });
            });

            // --- INITIALIZATION ---
            // Perform an initial calculation on page load
            calculateAndDisplay();
        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/compound-interest-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>The Rule of 72: Definition, Usefulness, and How to Use It</title>
		<link>https://techaiconnect.com/the-rule-of-72/</link>
					<comments>https://techaiconnect.com/the-rule-of-72/#respond</comments>
		
		<dc:creator><![CDATA[techai]]></dc:creator>
		<pubDate>Sat, 05 Jul 2025 11:17:59 +0000</pubDate>
				<category><![CDATA[Webapps]]></category>
		<guid isPermaLink="false">https://techaiconnect.com/?p=4239</guid>

					<description><![CDATA[Rule of 72 Calculator Rule of 72 Calculator Estimate the number of years required to [&#8230;]]]></description>
										<content:encoded><![CDATA[<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Rule of 72 Calculator</title>
    <script src="https://cdn.tailwindcss.com"></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=Inter:wght@400;500;700;900&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Inter', sans-serif;
            background-color: #111827; /* bg-gray-900 */
            color: #d1d5db; /* text-gray-300 */
        }
        .prose-custom h3 {
            color: #2dd4bf; /* text-teal-400 */
            border-bottom: 1px solid #374151; /* border-gray-700 */
            padding-bottom: 0.5rem;
            margin-top: 2em;
        }
        .prose-custom p {
            line-height: 1.8;
        }
        .prose-custom ul > li::before {
            background-color: #5eead4; /* bg-teal-300 */
            content: '';
            position: absolute;
            left: 0;
            top: 0.8em;
            width: 0.5em;
            height: 0.5em;
            border-radius: 50%;
        }
        .prose-custom ul {
            list-style: none;
            padding-left: 1.5em;
        }
        .prose-custom li {
            position: relative;
            padding-left: 0;
        }
    </style>
</head>
<body>

    <div class="container mx-auto p-4 md:p-8 max-w-3xl">
        <!-- Header -->
        <header class="text-center mb-8">
            <h1 class="text-4xl md:text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-teal-400 to-cyan-500">Rule of 72 Calculator</h1>
            <p class="text-gray-400 mt-2 text-lg">Estimate the number of years required to double your invested money.</p>
        </header>

        <main class="space-y-12">
            <!-- Calculator Tool -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
                    <!-- Left Column: Input -->
                    <div class="space-y-4">
                        <div>
                            <label for="interest-rate" class="block mb-2 text-lg font-medium text-gray-300">Enter annual interest rate (%):</label>
                            <input type="number" id="interest-rate" placeholder="Example: 8" class="bg-gray-700 border border-gray-600 text-white text-2xl rounded-lg focus:ring-cyan-500 focus:border-cyan-500 block w-full p-4">
                        </div>
                        <div class="text-center text-gray-400">
                            <p class="font-mono text-lg">Years ≈ 72 / Interest Rate</p>
                            <p class="text-sm">A quick mental math shortcut.</p>
                        </div>
                    </div>

                    <!-- Right Column: Result -->
                    <div class="text-center">
                        <p class="text-lg text-gray-400 mb-2">Years to double your investment:</p>
                        <p id="result-display" class="text-6xl font-bold text-teal-400">0</p>
                    </div>
                </div>
            </section>

            <!-- Information Section -->
            <section class="bg-gray-800 p-8 rounded-2xl shadow-lg border border-gray-700">
                <div class="prose prose-invert max-w-none text-gray-300 prose-custom">
                    <h2 class="text-2xl font-bold text-center mb-6 text-teal-400">Understanding the Rule of 72</h2>
                    
                    <h3>What Is the Rule of 72?</h3>
                    <p>The Rule of 72 is a quick, useful formula that is popularly used to estimate the number of years required to double invested money at a given annual rate of return. While calculators can find the precise time, the Rule of 72 is handy for mental calculations to quickly gauge an approximate value.</p>
                    
                    <h3>How to Use It</h3>
                    <p>The formula is simple: divide 72 by the annual rate of return to find out how many years it will take for your investment to double. For example, if an investment has an 8% annual compounded rate of return, it will take approximately 9 years (72 / 8 = 9) to double the money.</p>
                    <ul>
                        <li>It applies to compounded interest, not simple interest.</li>
                        <li>It can also be used for anything that grows exponentially, like GDP or inflation.</li>
                        <li>It can show the long-term effect of fees. A fund with a 3% annual fee will cut your principal in half in about 24 years (72 / 3 = 24).</li>
                    </ul>

                    <h3>How Accurate Is It?</h3>
                    <p>The Rule of 72 is reasonably accurate, especially for interest rates between 6% and 10%. It's a simplification of a more complex logarithmic formula. For daily or continuous compounding, using 69.3 instead of 72 gives a more accurate result. For rates far from the 6-10% range, other variations like the Rule of 69 or Rule of 73 can be more precise.</p>
                </div>
            </section>
        </main>
    </div>

    <script>
        // The JavaScript logic is language-independent and does not need changes.
        // It calculates based on the input value and updates the result display.
        document.addEventListener('DOMContentLoaded', () => {
            const interestRateInput = document.getElementById('interest-rate');
            const resultDisplay = document.getElementById('result-display');

            function calculateRuleOf72() {
                const rate = parseFloat(interestRateInput.value);

                if (isNaN(rate) || rate <= 0) {
                    resultDisplay.textContent = '0';
                    return;
                }

                const years = 72 / rate;
                // Format to a maximum of 2 decimal places.
                resultDisplay.textContent = Number(years.toFixed(2));
            }

            interestRateInput.addEventListener('input', calculateRuleOf72);
        });
    </script>
</body>
</html>
]]></content:encoded>
					
					<wfw:commentRss>https://techaiconnect.com/the-rule-of-72/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
