puzzle

<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name='robots' content='noindex, nofollow'/>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#FFFFFF">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
<meta name="format-detection" content="telephone=no"> 
<link rel="apple-touch-icon" sizes="180x180" href="icon.png">
<meta name="apple-mobile-web-app-title" content="Cat">
<link rel="icon" type="image/png" sizes="192x192" href="icon.png">
<link rel="icon" type="image/png" sizes="512x512" href="icon.png"> 
  
   <title>Cat</title>
    <style>
        :root {
            --tile-size: min(12vh, 25vw);
            --gap: 8px;
        }

        * { 
            box-sizing: border-box; 
            touch-action: none; 
            -webkit-user-select: none;
            user-select: none;
        }

        body {
            margin: 0;
            padding: 0;
            height: 100dvh;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: space-evenly;
            background-color: #f0f9ff;
            font-family: 'Comic Sans MS', sans-serif;
            overflow: hidden;
        }

        .grid {
            display: grid;
            grid-template-columns: repeat(3, var(--tile-size));
            grid-template-rows: repeat(3, var(--tile-size));
            gap: var(--gap);
            padding: var(--gap);
            background: #e2e8f0;
            border-radius: 15px;
            box-shadow: inset 0 4px 10px rgba(0,0,0,0.1);
        }
        .full { width: 80%; }

        .tray {
            display: grid;
            grid-template-columns: repeat(3, var(--tile-size));
            grid-template-rows: repeat(3, var(--tile-size));
            gap: var(--gap);
            padding: var(--gap);
            background: rgba(255,255,255,0.5);
            border: 3px dashed #94a3b8;
            border-radius: 15px;
        }

        .slot {
            width: var(--tile-size);
            height: var(--tile-size);
            background: #cbd5e1;
            border-radius: 8px;
            position: relative;
        }

        .piece {
            width: var(--tile-size);
            height: var(--tile-size);
            background-size: cover;
            background-position: center;
            border-radius: 8px;
            position: absolute;
            cursor: grab;
            z-index: 2;
            transition: transform 0.1s;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .piece.dragging {
            z-index: 100;
            transform: scale(1.1);
            box-shadow: 0 10px 20px rgba(0,0,0,0.2);
            opacity: 0.9;
        }

        #win-overlay {
            position: fixed;
            inset: 0;
            background: rgba(255, 255, 255, 0.9);
            display: none;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 1000;
        }

        .btn {
            padding: 15px 30px;
            background: #0ea5e9;
            color: white;
            border: none;
            border-radius: 50px;
            font-size: 1.2rem;
            font-weight: bold;
            box-shadow: 0 4px 0 #0369a1;
        }
    </style>
</head>
<body> 

    <div class="grid" id="main-grid">
        <div class="slot" data-index="1"></div>
        <div class="slot" data-index="2"></div>
        <div class="slot" data-index="3"></div>
        <div class="slot" data-index="4"></div>
        <div class="slot" data-index="5"></div>
        <div class="slot" data-index="6"></div>
        <div class="slot" data-index="7"></div>
        <div class="slot" data-index="8"></div>
        <div class="slot" data-index="9"></div>
    </div>

    <div class="tray" id="tray">
        <div class="slot"></div><div class="slot"></div><div class="slot"></div>
        <div class="slot"></div><div class="slot"></div><div class="slot"></div>
        <div class="slot"></div><div class="slot"></div><div class="slot"></div>
    </div>

    <div id="win-overlay">
        <img class="full" src="full.jpg">
        <h2 style="color: #0369a1; font-size: 2rem;">Meow! You found cat!</h2>
        <button class="btn" onclick="location.reload()">Play Again</button>
    </div>

    <script>
        // --- SOUND ENGINE ---
        const AudioContext = window.AudioContext || window.webkitAudioContext;
        const audioCtx = new AudioContext();

        const unlockAudio = () => {
            if (audioCtx.state === 'suspended') audioCtx.resume();
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            gain.gain.value = 0; 
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(0); osc.stop(0.1);
            window.removeEventListener('pointerdown', unlockAudio);
            window.removeEventListener('touchstart', unlockAudio);
        };
        window.addEventListener('pointerdown', unlockAudio);
        window.addEventListener('touchstart', unlockAudio);

        function playTone(freq, type, duration, vol = 0.1) {
            try {
                const osc = audioCtx.createOscillator();
                const gain = audioCtx.createGain();
                osc.type = type;
                osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
                gain.gain.setValueAtTime(vol, audioCtx.currentTime);
                gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
                osc.connect(gain);
                gain.connect(audioCtx.destination);
                osc.start();
                osc.stop(audioCtx.currentTime + duration);
            } catch(e) {}
        }

        // --- WEB SPEECH FUNCTION ---
        function speakWinMessage() {
            if ('speechSynthesis' in window) {
                const msg = new SpeechSynthesisUtterance();
                msg.text = "Meow! You found cat!";
                msg.rate = 1;
                msg.pitch = 1.3; // Higher pitch for cat theme
                window.speechSynthesis.speak(msg);
            }
        }

        const SFX = {
            pickup: () => playTone(600, 'sine', 0.1),
            drop: () => playTone(400, 'triangle', 0.15),
            tap: () => playTone(800, 'sine', 0.05, 0.05),
            win: () => {
                const notes = [523, 659, 783, 1046];
                notes.forEach((f, i) => setTimeout(() => playTone(f, 'sine', 0.4, 0.05), i * 120));
            }
        };

        // --- GAME LOGIC ---
        let draggedElement = null;
        let startX, startY, initialX, initialY;
        const traySlots = document.querySelectorAll('#tray .slot');
        const gridSlots = document.querySelectorAll('#main-grid .slot');

        function init() {
            const ids = [1, 2, 3, 4, 5, 6, 7, 8, 9].sort(() => Math.random() - 0.5);
            ids.forEach((id, i) => {
                const piece = document.createElement('div');
                piece.className = 'piece';
                piece.style.backgroundImage = `url('${id}.jpg')`;
                piece.dataset.id = id;
                piece.addEventListener('pointerdown', onPointerDown);
                piece.addEventListener('click', onClickPiece);
                traySlots[i].appendChild(piece);
            });
        }

        function onPointerDown(e) {
            SFX.pickup();
            draggedElement = e.target;
            draggedElement.classList.add('dragging');
            const rect = draggedElement.getBoundingClientRect();
            startX = e.clientX; startY = e.clientY;
            initialX = rect.left; initialY = rect.top;
            draggedElement.style.position = 'fixed';
            draggedElement.style.left = initialX + 'px';
            draggedElement.style.top = initialY + 'px';
            document.addEventListener('pointermove', onPointerMove);
            document.addEventListener('pointerup', onPointerUp);
        }

        function onPointerMove(e) {
            if (!draggedElement) return;
            draggedElement.style.left = (initialX + (e.clientX - startX)) + 'px';
            draggedElement.style.top = (initialY + (e.clientY - startY)) + 'px';
        }

        function onPointerUp(e) {
            if (!draggedElement) return;
            SFX.drop();
            draggedElement.hidden = true;
            const target = document.elementFromPoint(e.clientX, e.clientY);
            draggedElement.hidden = false;
            const slot = target ? target.closest('.slot') : null;
            if (slot) {
                if (slot.hasChildNodes()) {
                    const existingPiece = slot.querySelector('.piece');
                    draggedElement.parentElement.appendChild(existingPiece);
                    existingPiece.style.position = 'static';
                }
                slot.appendChild(draggedElement);
            }
            draggedElement.style.position = 'static';
            draggedElement.classList.remove('dragging');
            draggedElement = null;
            document.removeEventListener('pointermove', onPointerMove);
            document.removeEventListener('pointerup', onPointerUp);
            checkWin();
        }

        function onClickPiece(e) {
            const piece = e.target;
            if (piece.closest('#main-grid')) {
                SFX.tap();
                for (let slot of traySlots) {
                    if (!slot.hasChildNodes()) {
                        slot.appendChild(piece);
                        break;
                    }
                }
            }
        }

        function checkWin() {
            let correct = 0;
            gridSlots.forEach((slot, i) => {
                const piece = slot.querySelector('.piece');
                if (piece && piece.dataset.id == (i + 1)) correct++;
            });
            if (correct === 9) {
                SFX.win();
                document.getElementById('win-overlay').style.display = 'flex';
                // Trigger Web Speech
                speakWinMessage();
            }
        }

        init();
    </script>
</body>
</html>