Загрузка данных


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>OwO.FM // Radio Active</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Luckiest+Guy&family=Outfit:wght@400;600;700;800&family=Syne:wght@800&display=swap');

        :root {
            --orange-rich: #ff4500;
            --orange-deep: #cc2900;
            --purple-neon: #7b1fa2;
            --purple-deep: #30004a;
            --vfd-cyan: #00cca3;
            --card-bg: rgba(18, 12, 22, 0.72);
            --bg-dark: #050308;
            --glass-border: rgba(255, 255, 255, 0.09);
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            user-select: none;
            -webkit-tap-highlight-color: transparent;
        }

        body {
            background-color: var(--bg-dark);
            font-family: 'Outfit', sans-serif;
            height: 100vh;
            width: 100vw;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            position: relative;
            color: #ffffff;
        }

        /* --- СЛОЙ 0: АВРОРА И ШУМ --- */
        .aurora-container {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            z-index: 0;
            overflow: hidden;
            background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.03'/%3E%3C/svg%3E");
        }

        .aurora-blob {
            position: absolute;
            border-radius: 50%;
            filter: blur(90px);
            opacity: 0.65;
            animation: float-aurora 12s infinite alternate cubic-bezier(0.4, 0, 0.2, 1);
        }

        .aurora-1 { width: 75vw; height: 75vw; background: var(--purple-neon); top: -15%; left: -20%; }
        .aurora-2 { width: 85vw; height: 85vw; background: var(--orange-rich); bottom: -20%; right: -20%; animation-delay: -5s; }
        .aurora-3 { width: 55vw; height: 55vw; background: var(--purple-deep); top: 15%; right: -10%; animation-delay: -8s; opacity: 0.8; }

        /* --- PAGE 1: СТАРТОВЫЙ ЭКРАН --- */
        #phase1 {
            position: absolute;
            inset: 0;
            z-index: 10;
            display: flex;
            flex-direction: column;
            justify-content: flex-end;
            align-items: center;
            padding-bottom: 24px;
            transition: opacity 0.8s cubic-bezier(0.25, 1, 0.3, 1), transform 0.8s ease;
        }

        #phase1.hidden {
            opacity: 0;
            pointer-events: none;
            transform: scale(0.95);
        }

        /* Летящий текст */
        #fx-container {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            z-index: 1;
            pointer-events: none;
            overflow: hidden;
        }

        .fox-word {
            position: absolute;
            font-family: 'Luckiest Guy', cursive;
            color: #ffffff;
            font-size: 2.3rem;
            letter-spacing: 2px;
            white-space: nowrap;
            text-shadow: 
                0 0 12px var(--orange-rich),
                0 0 25px var(--purple-neon),
                0 0 45px rgba(255, 69, 0, 0.6);
            opacity: 0;
            will-change: transform, opacity;
            animation: continuousFly 3.4s linear forwards;
        }

        /* Маскот Лис */
        .mascot-layer {
            position: absolute;
            top: 38%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 2;
            height: 72vh;
            max-height: 680px;
            max-width: 100vw;
            pointer-events: none;
            display: flex;
            align-items: center;
            justify-content: center;
            animation: breathe 7s infinite alternate ease-in-out;
        }

        .mascot-layer img {
            height: 100%;
            width: auto;
            object-fit: contain;
            mix-blend-mode: screen; 
            filter: 
                drop-shadow(0 0 30px rgba(255, 69, 0, 0.5))
                drop-shadow(0 0 65px rgba(123, 31, 162, 0.4)); 
        }

        /* Стартовая карточка */
        .radio-card {
            position: relative;
            z-index: 10;
            width: calc(100% - 32px);
            max-width: 480px; /* Увеличено для лучшего вида на ПК */
            background: var(--card-bg);
            backdrop-filter: blur(28px) saturate(150%);
            -webkit-backdrop-filter: blur(28px) saturate(150%);
            border-radius: 32px;
            padding: 26px 24px;
            border: 1px solid var(--glass-border);
            box-shadow: 
                0 30px 60px rgba(0, 0, 0, 0.9),
                inset 0 1px 1px rgba(255, 255, 255, 0.15);
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .fm-badge {
            background: rgba(255, 255, 255, 0.06);
            border: 1px solid rgba(255, 255, 255, 0.1);
            padding: 5px 14px;
            border-radius: 20px;
            font-size: 13px;
            font-weight: 700;
            color: #e2d9eb;
            letter-spacing: 0.5px;
        }

        .status-indicator {
            font-size: 11px;
            font-weight: 800;
            letter-spacing: 2px;
            color: #9d91a6;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .status-dot {
            width: 6px;
            height: 6px;
            background-color: #8c7f96;
            border-radius: 50%;
        }

        .card-body {
            text-align: left;
            margin-bottom: 22px;
        }

        .meta-label {
            font-size: 11px;
            font-weight: 800;
            letter-spacing: 2px;
            color: #9d91a6;
            margin-bottom: 4px;
            text-transform: uppercase;
        }

        .main-title {
            font-family: 'Syne', sans-serif;
            font-size: 44px;
            font-weight: 800;
            color: #ffffff;
            line-height: 0.95;
            letter-spacing: -1px;
            margin-bottom: 8px;
        }

        .accent-title {
            font-size: 15px;
            font-weight: 800;
            letter-spacing: 3px;
            color: var(--orange-rich);
            margin-bottom: 10px;
            text-transform: uppercase;
        }

        .tagline {
            font-size: 13.5px;
            color: #b3a5bd;
            font-weight: 400;
            line-height: 1.4;
        }

        .btn-tune {
            width: 100%;
            padding: 16px 0;
            border-radius: 20px;
            border: 1px solid rgba(255, 255, 255, 0.14);
            background: linear-gradient(180deg, #362f3b 0%, #1c1721 100%);
            color: #ffffff;
            font-size: 15px;
            font-weight: 800;
            letter-spacing: 2px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            box-shadow: 
                0 10px 25px rgba(0,0,0,0.6),
                inset 0 1px 1px rgba(255,255,255,0.2);
            transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
            font-family: inherit;
        }

        .btn-tune:hover {
            background: linear-gradient(180deg, #483e4f 0%, #28212e 100%);
            border-color: rgba(255, 69, 0, 0.5);
            box-shadow: 
                0 12px 30px rgba(255, 69, 0, 0.25),
                inset 0 1px 1px rgba(255,255,255,0.3);
            transform: translateY(-2px);
        }

        .loader {
            display: none;
            width: 22px;
            height: 22px;
            border: 3px solid rgba(255, 255, 255, 0.15);
            border-radius: 50%;
            border-top-color: var(--orange-rich);
            border-right-color: var(--purple-neon);
            animation: spin 0.8s linear infinite;
        }

        /* --- PAGE 2: ПЛЕЕР В ЕДИНОМ СТИЛЕ --- */
        #phase2 {
            position: absolute;
            inset: 0;
            z-index: 20;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.8s cubic-bezier(0.25, 1, 0.3, 1), transform 0.8s ease;
            transform: scale(1.05);
        }

        #phase2.active {
            opacity: 1;
            pointer-events: auto;
            transform: scale(1);
        }

        .player-card {
            width: 100%;
            max-width: 480px; /* Увеличено для лучшего вида на ПК */
            background: var(--card-bg);
            backdrop-filter: blur(32px) saturate(160%);
            -webkit-backdrop-filter: blur(32px) saturate(160%);
            border-radius: 32px;
            padding: 24px;
            border: 1px solid var(--glass-border);
            box-shadow: 
                0 35px 70px rgba(0, 0, 0, 0.95),
                inset 0 1px 1px rgba(255, 255, 255, 0.15);
            display: flex;
            flex-direction: column;
            gap: 18px;
        }

        /* Дисплей Плеера (VFD / Cover Art) */
        .vfd-screen {
            position: relative;
            width: 100%;
            aspect-ratio: 1 / 1; /* Жестко заданный квадратный формат */
            background: rgba(3, 2, 6, 0.85);
            border-radius: 20px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow: inset 0 0 25px rgba(0,0,0,0.9);
            overflow: hidden;
            cursor: pointer;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #visualizerCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        .cover-container {
            position: absolute;
            inset: 0;
            display: none;
            justify-content: center;
            align-items: center;
            background: rgba(3, 2, 6, 1);
        }

        .cover-art {
            width: 100%;
            height: 100%;
            object-fit: cover;
            opacity: 0.95;
            transition: opacity 0.3s ease;
        }

        .cover-overlay {
            position: absolute;
            inset: 0;
            background: linear-gradient(180deg, rgba(255,255,255,0.05) 0%, rgba(0,0,0,0.2) 50%, rgba(0,0,0,0.6) 100%);
            box-shadow: inset 0 0 40px rgba(0,0,0,0.8);
            pointer-events: none;
        }

        .view-badge {
            position: absolute;
            top: 12px;
            right: 12px;
            font-size: 9px;
            font-weight: 800;
            letter-spacing: 1.5px;
            color: var(--vfd-cyan);
            background: rgba(12, 8, 18, 0.85);
            padding: 4px 10px;
            border-radius: 20px;
            border: 1px solid rgba(0, 204, 163, 0.3);
            backdrop-filter: blur(4px);
            z-index: 5;
        }

        /* Бегущая строка VFD Ticker */
        .vfd-ticker-box {
            width: 100%;
            height: 44px;
            background: rgba(3, 2, 6, 0.85);
            border-radius: 14px;
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow: inset 0 0 12px rgba(0,0,0,0.9);
            overflow: hidden;
            display: flex;
            align-items: center;
        }

        #tickerCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        /* Кнопки управления плеером (Стеклянный Vista стиль) */
        .controls-row {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 16px;
            width: 100%;
        }

        .ctrl-btn {
            height: 54px;
            border-radius: 18px;
            /* Стеклянный градиент с резким переходом */
            background: linear-gradient(180deg, 
                rgba(255, 255, 255, 0.12) 0%, 
                rgba(255, 255, 255, 0.03) 49%, 
                rgba(0, 0, 0, 0.25) 50%, 
                rgba(0, 0, 0, 0.4) 100%);
            border: 1px solid rgba(255, 255, 255, 0.12);
            color: #ffffff;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            box-shadow: 
                0 8px 20px rgba(0,0,0,0.5),
                inset 0 1px 1px rgba(255,255,255,0.25);
            backdrop-filter: blur(10px);
            transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
            font-family: inherit;
        }

        .ctrl-btn:hover {
            border-color: rgba(255, 255, 255, 0.25);
            box-shadow: 
                0 0 25px rgba(255, 69, 0, 0.25),
                inset 0 1px 2px rgba(255,255,255,0.4);
            transform: translateY(-2px);
        }

        .ctrl-btn:active {
            transform: translateY(1px) scale(0.97);
            background: linear-gradient(180deg, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.5) 100%);
        }

        .play-main {
            width: 140px;
            font-size: 22px;
            color: var(--orange-rich);
        }

        .next-btn {
            width: 80px;
            font-size: 18px;
            color: var(--purple-neon);
        }

        /* Выбор радиостанций */
        .stations-grid {
            display: flex;
            gap: 12px;
            width: 100%;
        }

        .station-card-btn {
            flex: 1;
            padding: 14px 10px;
            background: linear-gradient(180deg, rgba(35, 27, 43, 0.8) 0%, rgba(18, 13, 23, 0.8) 100%);
            border: 1px solid rgba(255, 255, 255, 0.08);
            border-radius: 16px;
            font-size: 12px;
            font-weight: 800;
            letter-spacing: 1px;
            color: #b3a5bd;
            cursor: pointer;
            transition: all 0.25s ease;
            text-align: center;
        }

        .station-card-btn:hover {
            color: #ffffff;
            border-color: rgba(255, 255, 255, 0.2);
        }

        .station-card-btn.active {
            color: #ffffff;
            border-color: var(--orange-rich);
            background: linear-gradient(180deg, rgba(255, 69, 0, 0.25) 0%, rgba(48, 0, 74, 0.4) 100%);
            box-shadow: 0 0 20px rgba(255, 69, 0, 0.25);
            text-shadow: 0 0 10px rgba(255, 69, 0, 0.6);
        }

        /* --- АНИМАЦИИ --- */
        @keyframes float-aurora {
            0% { transform: translate(0, 0) scale(1); }
            50% { transform: translate(5%, 5%) scale(1.1); }
            100% { transform: translate(-5%, -5%) scale(0.9); }
        }

        @keyframes breathe {
            0% { transform: translate(-50%, -50%) scale(0.97) rotate(-1deg); }
            100% { transform: translate(-50%, -50%) scale(1.02) rotate(1deg); }
        }

        @keyframes continuousFly {
            0% { opacity: 0; transform: translate(var(--dx-start), 0) scale(0.8) rotate(var(--rot-start)); }
            15% { opacity: 0.9; }
            80% { opacity: 0.9; }
            100% { opacity: 0; transform: translate(var(--dx-end), -85vh) scale(1.1) rotate(var(--rot-end)); }
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        @media (max-width: 480px) {
            .mascot-layer { height: 60vh; top: 35%; }
            .radio-card, .player-card { padding: 22px 20px; }
            .main-title { font-size: 38px; }
            .fox-word { font-size: 1.8rem; }
            /* Убрано ограничение высоты .vfd-screen для сохранения квадратных пропорций на мобильных */
        }
    </style>
</head>
<body>

    <!-- Слой 0: Неоновая Аврора -->
    <div class="aurora-container">
        <div class="aurora-blob aurora-1"></div>
        <div class="aurora-blob aurora-2"></div>
        <div class="aurora-blob aurora-3"></div>
    </div>

    <!-- PHASE 1: СТАРТОВЫЙ ЭКРАН -->
    <div id="phase1">
        <div id="fx-container"></div>

        <div class="mascot-layer">
            <img id="mascot-img" src="https://i.postimg.cc/yNBJTg1H/e6d91e43-7287-49ed-8327-ef521fd39899.webp" alt="OwO Fox Mascot"> 
        </div>

        <div class="radio-card" id="main-panel">
            <div class="card-header">
                <span class="fm-badge">88.7 FM</span>
                <div class="status-indicator">
                    <span class="status-dot"></span>
                    STANDBY
                </div>
            </div>

            <div class="card-body">
                <div class="meta-label">ONLINE RADIO</div>
                <h1 class="main-title">OwO.FM</h1>
                <div class="accent-title">RADIO ACTIVE</div>
                <p class="tagline">Cinematic nights. A loud fox. One live signal.</p>
            </div>

            <button class="btn-tune" id="enter-btn">
                <span class="btn-icon">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M4.93 19.07A10 10 0 0 1 4.93 4.93M7.76 16.24a7 7 0 0 1 0-9.48M12 12m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0M16.24 7.76a7 7 0 0 1 0 9.48M19.07 4.93a10 10 0 0 1 0 14.14"/>
                    </svg>
                </span>
                <span id="btn-text">TUNE IN</span>
                <div class="loader" id="loader"></div>
            </button>
        </div>
    </div>

    <!-- PHASE 2: ПЛЕЕР С VFD ЭКРАНОМ -->
    <div id="phase2">
        <div class="player-card">
            <div class="card-header" style="margin-bottom: 0;">
                <span class="fm-badge" id="playerFmBadge">88.7 FM</span>
                <div class="status-indicator">
                    <span class="status-dot" style="background-color: var(--orange-rich); box-shadow: 0 0 8px var(--orange-rich);"></span>
                    LIVE STREAM
                </div>
            </div>

            <!-- VFD Экран / Обложка -->
            <div class="vfd-screen" id="vfdDisplay">
                <div class="view-badge" id="viewBadge">VFD SPECTRUM</div>
                <canvas id="visualizerCanvas"></canvas>
                <div class="cover-container" id="coverContainer">
                    <img class="cover-art" id="coverImg" src="" alt="Station Artwork">
                    <div class="cover-overlay"></div>
                </div>
            </div>

            <!-- VFD Dot-Matrix Бегущая строка -->
            <div class="vfd-ticker-box">
                <canvas id="tickerCanvas"></canvas>
            </div>

            <!-- Управление -->
            <div class="controls-row">
                <button class="ctrl-btn play-main" id="playBtn" aria-label="Play/Pause">▶</button>
                <button class="ctrl-btn next-btn" id="nextBtn" aria-label="Next Station">⏭</button>
            </div>

            <!-- Переключатель станций -->
            <div class="stations-grid" id="stationsList"></div>
        </div>
    </div>

    <script>
        /* === НАСТРОЙКИ РАДИОСТАНЦИЙ И ПОТОКОВ === */
        const stations = [
            { 
                id: 'owo', 
                name: 'OwO by NFX', 
                freq: '88.7 FM',
                url: 'https://stream.nightride.fm/nightride.mp3', // Замените на ваш Icecast поток
                cover: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=600&auto=format&fit=crop'
            },
            { 
                id: 'citypop', 
                name: 'City Pop', 
                freq: '101.3 FM',
                url: 'https://stream.nightride.fm/chillsynth.mp3', // Замените на ваш Icecast поток
                cover: 'https://images.unsplash.com/photo-1508700115892-45ecd05ae2ad?q=80&w=600&auto=format&fit=crop'
            }
        ];

        /* === ЭФФЕКТЫ СТАРТОВОГО ЭКРАНА === */
        const foxSounds = ["RING DING DING!", "WA PA PA PA!", "HATEE HATEE!", "JACHA CHACHA!", "FRAKA KAKA!", "A-OOO-OO-OOO!", "YIP YIP!", "PAW PAW!"];
        const fxContainer = document.getElementById('fx-container');
        let spawnInterval;

        function spawnWord() {
            const wordEl = document.createElement('div');
            wordEl.className = 'fox-word';
            wordEl.innerText = foxSounds[Math.floor(Math.random() * foxSounds.length)];
            const startX = Math.floor(Math.random() * 60) + 20;
            wordEl.style.left = `${startX}%`;
            wordEl.style.top = `75%`; 
            
            const driftX = (Math.random() - 0.5) * 140; 
            const rotStart = (Math.random() - 0.5) * 16;
            const rotEnd = rotStart + (Math.random() - 0.5) * 20;

            wordEl.style.setProperty('--dx-start', '0px');
            wordEl.style.setProperty('--dx-end', `${driftX}px`);
            wordEl.style.setProperty('--rot-start', `${rotStart}deg`);
            wordEl.style.setProperty('--rot-end', `${rotEnd}deg`);

            fxContainer.appendChild(wordEl);
            setTimeout(() => wordEl.remove(), 3400);
        }
        spawnInterval = setInterval(spawnWord, 650);

        /* === ПЕРЕХОД МЕЖДУ СТРАНИЦАМИ === */
        const phase1 = document.getElementById('phase1');
        const phase2 = document.getElementById('phase2');
        const enterBtn = document.getElementById('enter-btn');
        const btnText = document.getElementById('btn-text');
        const btnIcon = enterBtn.querySelector('.btn-icon');
        const loader = document.getElementById('loader');

        enterBtn.addEventListener('click', () => {
            enterBtn.style.pointerEvents = 'none';
            btnIcon.style.display = 'none';
            btnText.style.display = 'none';
            loader.style.display = 'block';
            
            clearInterval(spawnInterval);
            spawnInterval = setInterval(spawnWord, 180);
            
            setTimeout(() => {
                phase1.classList.add('hidden');
                setTimeout(() => {
                    phase2.classList.add('active');
                    initPlayer();
                    loadStation(0, true);
                }, 400);
            }, 600);
        });

        /* === ПЛЕЕР И VFD СИСТЕМА === */
        let activeStationIdx = 0;
        let isPlaying = false;
        let viewMode = 'VFD'; // 'VFD' или 'COVER'
        let tempNotice = null;
        let tempNoticeTimer = null;

        const audio = new Audio();
        audio.crossOrigin = "anonymous";
        let audioCtx, analyser, freqData;

        const vfdDisplay = document.getElementById('vfdDisplay');
        const viewBadge = document.getElementById('viewBadge');
        const coverContainer = document.getElementById('coverContainer');
        const coverImg = document.getElementById('coverImg');
        const playerFmBadge = document.getElementById('playerFmBadge');
        
        const playBtn = document.getElementById('playBtn');
        const nextBtn = document.getElementById('nextBtn');
        const stationsList = document.getElementById('stationsList');

        const vCanvas = document.getElementById('visualizerCanvas');
        const vCtx = vCanvas.getContext('2d');
        const tCanvas = document.getElementById('tickerCanvas');
        const tCtx = tCanvas.getContext('2d');

        const NUM_COLS = 32;
        let peaks = new Array(NUM_COLS).fill(0);
        let tickerScrollX = 0;

        // VFD Шрифт 5x7
        const FONT_5X7 = {
            'A': [0x0e,0x11,0x11,0x1f,0x11,0x11,0x11], 'B': [0x1e,0x11,0x11,0x1e,0x11,0x11,0x1e],
            'C': [0x0e,0x11,0x10,0x10,0x10,0x11,0x0e], 'D': [0x1c,0x12,0x11,0x11,0x11,0x12,0x1c],
            'E': [0x1f,0x10,0x10,0x1e,0x10,0x10,0x1f], 'F': [0x1f,0x10,0x10,0x1e,0x10,0x10,0x10],
            'G': [0x0e,0x11,0x10,0x17,0x11,0x11,0x0e], 'H': [0x11,0x11,0x11,0x1f,0x11,0x11,0x11],
            'I': [0x0e,0x04,0x04,0x04,0x04,0x04,0x0e], 'J': [0x07,0x02,0x02,0x02,0x02,0x12,0x0c],
            'K': [0x11,0x12,0x14,0x18,0x14,0x12,0x11], 'L': [0x10,0x10,0x10,0x10,0x10,0x10,0x1f],
            'M': [0x11,0x1b,0x15,0x15,0x11,0x11,0x11], 'N': [0x11,0x19,0x15,0x13,0x11,0x11,0x11],
            'O': [0x0e,0x11,0x11,0x11,0x11,0x11,0x0e], 'P': [0x1e,0x11,0x11,0x1e,0x10,0x10,0x10],
            'Q': [0x0e,0x11,0x11,0x11,0x15,0x12,0x0d], 'R': [0x1e,0x11,0x11,0x1e,0x14,0x12,0x11],
            'S': [0x0e,0x11,0x10,0x0e,0x01,0x11,0x0e], 'T': [0x1f,0x04,0x04,0x04,0x04,0x04,0x04],
            'U': [0x11,0x11,0x11,0x11,0x11,0x11,0x0e], 'V': [0x11,0x11,0x11,0x11,0x11,0x0a,0x04],
            'W': [0x11,0x11,0x11,0x15,0x15,0x1b,0x11], 'X': [0x11,0x11,0x0a,0x04,0x0a,0x11,0x11],
            'Y': [0x11,0x11,0x11,0x0a,0x04,0x04,0x04], 'Z': [0x1f,0x01,0x02,0x04,0x08,0x10,0x1f],
            '0': [0x0e,0x13,0x15,0x19,0x15,0x13,0x0e], '1': [0x04,0x0c,0x04,0x04,0x04,0x04,0x0e],
            '2': [0x0e,0x11,0x01,0x02,0x04,0x08,0x1f], '3': [0x1f,0x01,0x02,0x0e,0x01,0x11,0x0e],
            '4': [0x02,0x06,0x0a,0x12,0x1f,0x02,0x02], '5': [0x1f,0x10,0x1e,0x01,0x01,0x11,0x0e],
            '6': [0x0e,0x10,0x10,0x1e,0x11,0x11,0x0e], '7': [0x1f,0x01,0x02,0x04,0x08,0x08,0x08],
            '8': [0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e], '9': [0x0e,0x11,0x11,0x0f,0x01,0x01,0x0e],
            '-': [0x00,0x00,0x00,0x1f,0x00,0x00,0x00], '.': [0x00,0x00,0x00,0x00,0x00,0x06,0x06],
            ':': [0x00,0x06,0x06,0x00,0x06,0x06,0x00], '/': [0x01,0x02,0x04,0x08,0x10,0x00,0x00],
            ' ': [0x00,0x00,0x00,0x00,0x00,0x00,0x00]
        };

        function initPlayer() {
            renderStationButtons();
            setupCanvases();
            window.addEventListener('resize', setupCanvases);

            vfdDisplay.addEventListener('click', toggleViewMode);
            playBtn.addEventListener('click', () => togglePlay());
            
            // Кнопка NEXT - переключает на следующую станцию
            nextBtn.addEventListener('click', () => {
                const nextIdx = (activeStationIdx + 1) % stations.length;
                loadStation(nextIdx, isPlaying);
                triggerNotice('>> NEXT STATION');
            });

            requestAnimationFrame(animationLoop);
        }

        function renderStationButtons() {
            stationsList.innerHTML = '';
            stations.forEach((s, i) => {
                const btn = document.createElement('button');
                btn.className = `station-card-btn ${i === activeStationIdx ? 'active' : ''}`;
                btn.innerText = s.name;
                btn.onclick = () => loadStation(i, true);
                stationsList.appendChild(btn);
            });
        }

        function loadStation(idx, autoPlay = false) {
            activeStationIdx = idx;
            const st = stations[idx];
            audio.src = st.url;
            coverImg.src = st.cover;
            playerFmBadge.innerText = st.freq;

            Array.from(stationsList.children).forEach((b, i) => {
                b.classList.toggle('active', i === idx);
            });

            if (autoPlay) togglePlay(true);
        }

        function toggleViewMode() {
            if (viewMode === 'VFD') {
                viewMode = 'COVER';
                vCanvas.style.display = 'none';
                coverContainer.style.display = 'flex';
                viewBadge.innerText = 'COVER ART';
            } else {
                viewMode = 'VFD';
                vCanvas.style.display = 'block';
                coverContainer.style.display = 'none';
                viewBadge.innerText = 'VFD SPECTRUM';
            }
        }

        function triggerNotice(msg) {
            tempNotice = msg;
            if (tempNoticeTimer) clearTimeout(tempNoticeTimer);
            tempNoticeTimer = setTimeout(() => tempNotice = null, 1800);
        }

        function initAudioContext() {
            if (audioCtx) return;
            const AudioCtx = window.AudioContext || window.webkitAudioContext;
            audioCtx = new AudioCtx();
            analyser = audioCtx.createAnalyser();
            analyser.fftSize = 128;
            freqData = new Uint8Array(analyser.frequencyBinCount);

            const source = audioCtx.createMediaElementSource(audio);
            source.connect(analyser);
            analyser.connect(audioCtx.destination);
        }

        function togglePlay(force) {
            isPlaying = force !== undefined ? force : !isPlaying;

            if (isPlaying) {
                initAudioContext();
                if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume();
                audio.play().catch(() => { isPlaying = false; updatePlayBtn(); });
            } else {
                audio.pause();
            }
            updatePlayBtn();
        }

        function updatePlayBtn() {
            playBtn.innerText = isPlaying ? '❚❚' : '▶';
        }

        function setupCanvases() {
            const dpr = window.devicePixelRatio || 1;
            [vCanvas, tCanvas].forEach(c => {
                const rect = c.parentElement.getBoundingClientRect();
                c.width = rect.width * dpr;
                c.height = rect.height * dpr;
            });
        }

        function animationLoop() {
            requestAnimationFrame(animationLoop);
            if (viewMode === 'VFD') drawVfdSpectrum();
            drawTicker();
        }

        function drawVfdSpectrum() {
            const dpr = window.devicePixelRatio || 1;
            const w = vCanvas.width / dpr;
            const h = vCanvas.height / dpr;

            vCtx.save();
            vCtx.scale(dpr, dpr);
            vCtx.clearRect(0, 0, w, h);

            if (isPlaying && analyser) analyser.getByteFrequencyData(freqData);

            const colWidth = w / NUM_COLS;
            const barWidth = colWidth * 0.65;
            const radius = barWidth / 2;

            // Стеклянный градиент в тонах интерфейса
            const gradient = vCtx.createLinearGradient(0, h, 0, 0);
            gradient.addColorStop(0, '#7b1fa2');   /* purple-neon */
            gradient.addColorStop(0.6, '#ff4500'); /* orange-rich */
            gradient.addColorStop(1, '#ffc107');   /* яркий акцент на пиках */

            for (let i = 0; i < NUM_COLS; i++) {
                const val = isPlaying && freqData ? freqData[i] : 0;
                peaks[i] = Math.max(val, peaks[i] - 1.2); // плавное падение пиков

                const barHeight = (val / 255) * (h * 0.85); // небольшой отступ сверху для пиков
                const peakHeight = (peaks[i] / 255) * (h * 0.85);
                const x = i * colWidth + (colWidth - barWidth) / 2;

                // Отрисовка основного столбца
                if (barHeight > 0) {
                    vCtx.fillStyle = gradient;
                    vCtx.globalAlpha = 0.85;
                    vCtx.beginPath();
                    if (vCtx.roundRect) {
                        vCtx.roundRect(x, h - barHeight, barWidth, barHeight, radius);
                    } else {
                        vCtx.fillRect(x, h - barHeight, barWidth, barHeight);
                    }
                    vCtx.fill();
                }

                // Отрисовка светящейся точки пика
                if (peakHeight > 0) {
                    vCtx.fillStyle = '#ffffff';
                    vCtx.globalAlpha = 1;
                    vCtx.beginPath();
                    vCtx.arc(x + barWidth / 2, h - peakHeight - (barWidth), barWidth / 2, 0, Math.PI * 2);
                    vCtx.fill();
                    
                    // Эффект свечения
                    vCtx.shadowBlur = 12;
                    vCtx.shadowColor = '#ff4500';
                    vCtx.fill();
                    vCtx.shadowBlur = 0;
                }
            }
            vCtx.restore();
        }

        function drawTicker() {
            const dpr = window.devicePixelRatio || 1;
            const w = tCanvas.width / dpr;
            const h = tCanvas.height / dpr;

            tCtx.save();
            tCtx.scale(dpr, dpr);
            tCtx.clearRect(0, 0, w, h);

            const st = stations[activeStationIdx];
            const text = tempNotice ? tempNotice : (isPlaying ? `NOW PLAYING: ${st.name} // LIVE STEREO SIGNAL` : `STANDBY - ${st.name} - PRESS PLAY`);

            const dotSize = 2.2;
            const dotGap = 1;
            const charWidth = 6 * (dotSize + dotGap);

            tickerScrollX -= 0.9;
            if (tickerScrollX < -(text.length * charWidth)) tickerScrollX = w;

            let startX = tickerScrollX;
            const startY = (h - (7 * (dotSize + dotGap))) / 2;

            for (let c = 0; c < text.length; c++) {
                const char = text[c].toUpperCase();
                const matrix = FONT_5X7[char] || FONT_5X7[' '];

                if (startX > -charWidth && startX < w) {
                    for (let row = 0; row < 7; row++) {
                        const bits = matrix[row] || 0;
                        for (let col = 0; col < 5; col++) {
                            const bit = (bits >> (4 - col)) & 1;
                            const dx = startX + col * (dotSize + dotGap);
                            const dy = startY + row * (dotSize + dotGap);

                            tCtx.fillStyle = '#ff4500';
                            tCtx.globalAlpha = bit ? 0.9 : 0.05;
                            tCtx.fillRect(dx, dy, dotSize, dotSize);
                        }
                    }
                }
                startX += charWidth;
            }
            tCtx.restore();
        }
    </script>
</body>
</html>