<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.pandoraburo.ru/" xml:lang="ru">
  <id>https://www.pandoraburo.ru/reabilitatciya-balansa-plavnost-dvizheniy</id>
  <link href="https://www.pandoraburo.ru/reabilitatciya-balansa-plavnost-dvizheniy.xml" rel="self"/>
  <title>Реабилитация баланса. Плавность движений</title>
  <subtitle type="html">
&lt;html lang="ru"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"&gt;
    &lt;title&gt;Нейро-тренажер: Трекер баланса&lt;/title&gt;
    &lt;style&gt;
        * { margin: 0; padding: 0; box-sizing: border-box; user-select: none; }
        body {
            background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', Tahoma, sans-serif;
            padding: 15px;
            touch-action: none;
        }
        .container {
            background: rgba(255,255,255,0.95);
            border-radius: 24px;
            padding: 30px;
            max-width: 750px;
            width: 100%;
            box-shadow: 0 20px 60px rgba(0,0,0,0.5);
        }
        h1 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 10px;
            font-size: 1.6rem;
        }
        .subtitle {
            text-align: center;
            color: #7f8c8d;
            margin-bottom: 25px;
            font-size: 0.95rem;
        }
        .stats-row {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
            margin-bottom: 20px;
        }
        .stat-card {
            background: white;
            border-radius: 14px;
            padding: 12px;
            text-align: center;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .stat-label {
            font-size: 0.8rem;
            color: #95a5a6;
            margin-bottom: 4px;
        }
        .stat-value {
            font-size: 1.5rem;
            font-weight: bold;
            color: #2c3e50;
        }
        .stat-value.good { color: #27ae60; }
        .stat-value.bad { color: #e74c3c; }
        .tracker-area {
            position: relative;
            background: #ecf0f1;
            height: 400px;
            border-radius: 18px;
            margin-bottom: 20px;
            overflow: hidden;
            box-shadow: inset 0 2px 10px rgba(0,0,0,0.15);
            touch-action: none;
            cursor: none;
        }
        .moving-ball {
            position: absolute;
            width: 80px;
            height: 80px;
            border-radius: 50%;
            background: radial-gradient(circle, #3498db, #2980b9);
            border: 3px solid #2c3e50;
            box-shadow: 0 4px 15px rgba(52, 152, 219, 0.4);
            pointer-events: none;
            transition: none;
        }
        .cursor-dot {
            position: absolute;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            background: radial-gradient(circle, #e74c3c, #c0392b);
            border: 2px solid white;
            box-shadow: 0 2px 8px rgba(231, 76, 60, 0.6);
            pointer-events: none;
            z-index: 10;
        }
        .cursor-dot.inside {
            background: radial-gradient(circle, #27ae60, #229954);
            box-shadow: 0 2px 8px rgba(39, 174, 96, 0.6);
        }
        .trail-line {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 5;
        }
        .controls {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 12px;
            margin-bottom: 15px;
        }
        .btn {
            padding: 14px;
            font-size: 1.1rem;
            font-weight: bold;
            border: none;
            border-radius: 12px;
            cursor: pointer;
            transition: all 0.2s;
        }
        .btn-primary {
            background: #27ae60;
            color: white;
            box-shadow: 0 4px 0 #1e8449;
        }
        .btn-primary:active {
            transform: translateY(4px);
            box-shadow: none;
        }
        .btn-secondary {
            background: #95a5a6;
            color: white;
        }
        .btn-export {
            background: #8e44ad;
            color: white;
            grid-column: 1 / -1;
        }
        .settings {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 10px;
            margin-bottom: 15px;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 12px;
        }
        .setting-item {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .setting-item label {
            font-size: 0.85rem;
            color: #7f8c8d;
            margin-bottom: 5px;
        }
        .setting-item input[type="range"] {
            width: 100%;
            max-width: 120px;
        }
        .setting-item span {
            font-size: 0.9rem;
            font-weight: bold;
            color: #2c3e50;
            margin-top: 3px;
        }
        .message {
            text-align: center;
            padding: 12px;
            border-radius: 12px;
            font-weight: bold;
            min-height: 45px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .message.info { background: #d1ecf1; color: #0c5460; }
        .message.success { background: #d4edda; color: #155724; }
        .message.error { background: #f8d7da; color: #721c24; }
        .progress-ring {
            position: absolute;
            top: 15px;
            right: 15px;
            width: 60px;
            height: 60px;
        }
        .progress-ring circle {
            fill: none;
            stroke-width: 6;
            transform: rotate(-90deg);
            transform-origin: 50% 50%;
        }
        .progress-ring .bg { stroke: #ecf0f1; }
        .progress-ring .progress {
            stroke: #27ae60;
            stroke-linecap: round;
            transition: stroke-dashoffset 0.3s;
        }
        .progress-text {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 0.9rem;
            font-weight: bold;
            color: #2c3e50;
        }
        @media (max-width: 600px) {
            .tracker-area { height: 300px; }
            .moving-ball { width: 60px; height: 60px; }
            .cursor-dot { width: 16px; height: 16px; }
            .settings { grid-template-columns: 1fr; }
            .stat-value { font-size: 1.2rem; }
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="container"&gt;
    &lt;h1&gt;? Трекер баланса&lt;/h1&gt;
    &lt;p class="subtitle"&gt;Держите курсор внутри движущегося мяча как можно дольше&lt;/p&gt;
    
    &lt;div class="stats-row"&gt;
        &lt;div class="stat-card"&gt;
            &lt;div class="stat-label"&gt;✅ Внутри&lt;/div&gt;
            &lt;div class="stat-value good" id="insideTime"&gt;0с&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="stat-card"&gt;
            &lt;div class="stat-label"&gt;❌ Снаружи&lt;/div&gt;
            &lt;div class="stat-value bad" id="outsideTime"&gt;0с&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="stat-card"&gt;
            &lt;div class="stat-label"&gt;⚡ Точность&lt;/div&gt;
            &lt;div class="stat-value" id="accuracy"&gt;0%&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;div class="settings"&gt;
        &lt;div class="setting-item"&gt;
            &lt;label&gt;Скорость&lt;/label&gt;
            &lt;input type="range" id="speedSlider" min="1" max="10" value="3" /&gt;
            &lt;span id="speedValue"&gt;3&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="setting-item"&gt;
            &lt;label&gt;Размер мяча&lt;/label&gt;
            &lt;input type="range" id="sizeSlider" min="40" max="120" value="80" step="10" /&gt;
            &lt;span id="sizeValue"&gt;80px&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class="setting-item"&gt;
            &lt;label&gt;Сложность&lt;/label&gt;
            &lt;input type="range" id="complexitySlider" min="1" max="5" value="2" /&gt;
            &lt;span id="complexityValue"&gt;2&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    
    &lt;div class="tracker-area" id="trackerArea"&gt;
        &lt;svg class="trail-line" id="trailSvg"&gt;&lt;/svg&gt;
        &lt;div class="moving-ball" id="movingBall"&gt;&lt;/div&gt;
        &lt;div class="cursor-dot" id="cursorDot"&gt;&lt;/div&gt;
        
        &lt;svg class="progress-ring"&gt;
            &lt;circle class="bg" cx="30" cy="30" r="25"&gt;&lt;/circle&gt;
            &lt;circle class="progress" cx="30" cy="30" r="25" stroke-dasharray="157" stroke-dashoffset="157" id="progressCircle"&gt;&lt;/circle&gt;
        &lt;/svg&gt;
        &lt;div class="progress-text" id="progressText"&gt;0%&lt;/div&gt;
    &lt;/div&gt;
    
    &lt;div class="controls"&gt;
        &lt;button class="btn btn-primary" id="startBtn"&gt;▶ Старт&lt;/button&gt;
        &lt;button class="btn btn-secondary" id="stopBtn" disabled="disabled"&gt;⏹ Стоп&lt;/button&gt;
        &lt;button class="btn btn-export" id="exportBtn"&gt;? Экспорт отчёта (CSV)&lt;/button&gt;
    &lt;/div&gt;
    
    &lt;div class="message info" id="message"&gt;
        Ведите мышью или пальцем по экрану, удерживая курсор внутри мяча
    &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
(function() {
    const area = document.getElementById('trackerArea');
    const ball = document.getElementById('movingBall');
    const cursor = document.getElementById('cursorDot');
    const trailSvg = document.getElementById('trailSvg');
    
    const startBtn = document.getElementById('startBtn');
    const stopBtn = document.getElementById('stopBtn');
    const exportBtn = document.getElementById('exportBtn');
    const message = document.getElementById('message');
    
    const insideTimeEl = document.getElementById('insideTime');
    const outsideTimeEl = document.getElementById('outsideTime');
    const accuracyEl = document.getElementById('accuracy');
    
    const speedSlider = document.getElementById('speedSlider');
    const sizeSlider = document.getElementById('sizeSlider');
    const complexitySlider = document.getElementById('complexitySlider');
    const speedValue = document.getElementById('speedValue');
    const sizeValue = document.getElementById('sizeValue');
    const complexityValue = document.getElementById('complexityValue');
    
    const progressCircle = document.getElementById('progressCircle');
    const progressText = document.getElementById('progressText');
    
    let isRunning = false;
    let animationId = null;
    let insideTime = 0;
    let outsideTime = 0;
    let startTime = 0;
    let ballX = 0, ballY = 0;
    let ballVX = 0, ballVY = 0;
    let cursorX = 0, cursorY = 0;
    let trailPoints = [];
    
    let speed = 3;
    let ballSize = 80;
    let complexity = 2;
    
    speedSlider.addEventListener('input', e =&gt; {
        speed = parseInt(e.target.value);
        speedValue.textContent = speed;
    });
    
    sizeSlider.addEventListener('input', e =&gt; {
        ballSize = parseInt(e.target.value);
        sizeValue.textContent = ballSize + 'px';
        ball.style.width = ballSize + 'px';
        ball.style.height = ballSize + 'px';
    });
    
    complexitySlider.addEventListener('input', e =&gt; {
        complexity = parseInt(e.target.value);
        complexityValue.textContent = complexity;
    });
    
    function getRelativePosition(e) {
        const rect = area.getBoundingClientRect();
        const clientX = e.touches ? e.touches[0].clientX : e.clientX;
        const clientY = e.touches ? e.touches[0].clientY : e.clientY;
        return {
            x: clientX - rect.left,
            y: clientY - rect.top
        };
    }
    
    function handleMove(e) {
        if (!isRunning) return;
        e.preventDefault();
        
        const pos = getRelativePosition(e);
        cursorX = pos.x;
        cursorY = pos.y;
        
        cursor.style.left = (cursorX - 10) + 'px';
        cursor.style.top = (cursorY - 10) + 'px';
        
        const ballCenterX = ballX + ballSize / 2;
        const ballCenterY = ballY + ballSize / 2;
        const distance = Math.sqrt(Math.pow(cursorX - ballCenterX, 2) + Math.pow(cursorY - ballCenterY, 2));
        const isInside = distance &lt;= (ballSize / 2 - 10);
        
        if (isInside) {
            cursor.classList.add('inside');
            insideTime += 16;
        } else {
            cursor.classList.remove('inside');
            outsideTime += 16;
        }
        
        trailPoints.push({ x: cursorX, y: cursorY, time: Date.now() - startTime });
        updateTrail();
        updateStats();
    }
    
    area.addEventListener('mousemove', handleMove);
    area.addEventListener('touchmove', handleMove, { passive: false });
    
    function updateTrail() {
        if (trailPoints.length &lt; 2) return;
        
        const recent = trailPoints.slice(-50);
        let pathD = `M ${recent[0].x} ${recent[0].y}`;
        
        for (let i = 1; i &lt; recent.length; i++) {
            pathD += ` L ${recent[i].x} ${recent[i].y}`;
        }
        
        trailSvg.innerHTML = `&lt;path d="${pathD}" stroke="rgba(231, 76, 60, 0.3)" 
                                     stroke-width="2" fill="none" stroke-linecap="round"/&gt;`;
    }
    
    function updateStats() {
        const total = insideTime + outsideTime;
        const acc = total &gt; 0 ? Math.round((insideTime / total) * 100) : 0;
        
        insideTimeEl.textContent = Math.round(insideTime / 1000) + 'с';
        outsideTimeEl.textContent = Math.round(outsideTime / 1000) + 'с';
        accuracyEl.textContent = acc + '%';
        
        const offset = 157 - (157 * acc / 100);
        progressCircle.style.strokeDashoffset = offset;
        progressText.textContent = acc + '%';
    }
    
    function updateBall() {
        if (!isRunning) return;
        
        const areaWidth = area.clientWidth - ballSize;
        const areaHeight = area.clientHeight - ballSize;
        
        if (Math.random() &lt; 0.02 * complexity) {
            ballVX += (Math.random() - 0.5) * speed * 2;
            ballVY += (Math.random() - 0.5) * speed * 2;
        }
        
        const maxSpeed = speed * 3;
        const currentSpeed = Math.sqrt(ballVX * ballVX + ballVY * ballVY);
        if (currentSpeed &gt; maxSpeed) {
            ballVX = (ballVX / currentSpeed) * maxSpeed;
            ballVY = (ballVY / currentSpeed) * maxSpeed;
        }
        
        ballX += ballVX;
        ballY += ballVY;
        
        if (ballX &lt; 0) { ballX = 0; ballVX = -ballVX * 0.8; }
        if (ballX &gt; areaWidth) { ballX = areaWidth; ballVX = -ballVX * 0.8; }
        if (ballY &lt; 0) { ballY = 0; ballVY = -ballVY * 0.8; }
        if (ballY &gt; areaHeight) { ballY = areaHeight; ballVY = -ballVY * 0.8; }
        
        ball.style.left = ballX + 'px';
        ball.style.top = ballY + 'px';
        
        animationId = requestAnimationFrame(updateBall);
    }
    
    function startSession() {
        isRunning = true;
        insideTime = 0;
        outsideTime = 0;
        trailPoints = [];
        startTime = Date.now();
        
        ballX = Math.random() * (area.clientWidth - ballSize);
        ballY = Math.random() * (area.clientHeight - ballSize);
        ballVX = (Math.random() - 0.5) * speed * 2;
        ballVY = (Math.random() - 0.5) * speed * 2;
        
        ball.style.left = ballX + 'px';
        ball.style.top = ballY + 'px';
        ball.style.width = ballSize + 'px';
        ball.style.height = ballSize + 'px';
        
        startBtn.disabled = true;
        stopBtn.disabled = false;
        message.className = 'message success';
        message.textContent = 'Тренировка началась! Держите курсор внутри мяча';
        
        updateBall();
    }
    
    function stopSession() {
        isRunning = false;
        cancelAnimationFrame(animationId);
        
        startBtn.disabled = false;
        stopBtn.disabled = true;
        
        const total = insideTime + outsideTime;
        const acc = total &gt; 0 ? Math.round((insideTime / total) * 100) : 0;
        const duration = Math.round(total / 1000);
        
        message.className = 'message info';
        message.textContent = `⏹ Сессия завершена: ${duration}с, точность ${acc}%`;
        
        const session = {
            date: new Date().toISOString(),
            duration: duration,
            accuracy: acc,
            insideTime: Math.round(insideTime / 1000),
            outsideTime: Math.round(outsideTime / 1000),
            speed: speed,
            ballSize: ballSize,
            complexity: complexity
        };
        
        const history = JSON.parse(localStorage.getItem('balance_history') || '[]');
        history.push(session);
        localStorage.setItem('balance_history', JSON.stringify(history));
    }
    
    exportBtn.addEventListener('click', () =&gt; {
        const history = JSON.parse(localStorage.getItem('balance_history') || '[]');
        
        if (history.length === 0) {
            message.className = 'message error';
            message.textContent = 'Нет данных для экспорта';
            return;
        }
        
        let csv = 'Дата,Длительность(с),Точность(%),Внутри(с),Снаружи(с),Скорость,Размер_мяча,Сложность\n';
        
        history.forEach(s =&gt; {
            csv += `${new Date(s.date).toLocaleString('ru-RU')},${s.duration},${s.accuracy},${s.insideTime},${s.outsideTime},${s.speed},${s.ballSize},${s.complexity}\n`;
        });
        
        const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `balance_tracker_${new Date().toISOString().split('T')[0]}.csv`;
        link.click();
        
        message.className = 'message success';
        message.textContent = '✅ Отчёт экспортирован!';
    });
    
    startBtn.addEventListener('click', startSession);
    stopBtn.addEventListener('click', stopSession);
    
    ball.style.width = ballSize + 'px';
    ball.style.height = ballSize + 'px';
})();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</subtitle>
  <updated>2026-08-15T11:34:19Z</updated>
  <logo>https://pandoraburo.ru/ssl/u/pic/69/be1662953111f1a508b532fbee6d5f/-/logo.png</logo>
  <author>
    <name>1592962</name>
    <uri>https://www.pandoraburo.ru/</uri>
  </author>
</feed>
