{"error":"A\u00e7\u00e3o n\u00e3o encontrada"}<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Canabrava PX</title>
    <link rel="manifest" href="?pwa=manifest">
    <script src="https://cdn.jsdelivr.net/npm/livekit-client@2.5.0/dist/livekit-client.umd.min.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@700;900&family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
        body {
            font-family: 'Inter', sans-serif; background: radial-gradient(ellipse at center, #1a1a2e, #0a0a12);
            min-height: 100vh; display: flex; justify-content: center; align-items: center;
            padding: 12px; margin: 0; transition: filter 0.5s ease;
        }
        body.eco-mode { filter: brightness(0.15); background: #000; }

        :root, body.theme-voyager {
            --radio-bg: linear-gradient(160deg, #1e293b, #0f172a); --radio-border: #334155;
            --brand-color: #38bdf8; --panel-text: #f1f5f9; --led-color: #facc15; --led-bg: #1e1b0e;
            --knob-bg: linear-gradient(145deg, #334155, #0f172a); --ptt-bg: radial-gradient(circle at 40% 40%, #334155, #0f172a);
            --ptt-border: #475569; --list-bg: rgba(0,0,0,0.25); --card-bg: rgba(255,255,255,0.05);
            --shadow-color: rgba(0,0,0,0.7); --status-bg: rgba(0,0,0,0.5); --status-color: #facc15;
            --display-bg: #0a0a0a; --display-border: #2a2a2a; --antenna-color: #475569;
            --body-inner-shadow: inset 0 0 40px rgba(0,0,0,0.4); --speaker-grill: rgba(255,255,255,0.04);
            --ticker-bg: #0a0a0a; --ticker-text: #facc15; --toggle-bg: #2a2a2a; --toggle-active: #38bdf8;
        }
        body.theme-cobra { --radio-bg: linear-gradient(160deg, #f5e6d3, #d9c8b2); --radio-border: #8b6b4a; --brand-color: #4a2810; --panel-text: #2d1e12; --led-color: #22c55e; --led-bg: #052e16; --knob-bg: linear-gradient(145deg, #e8d5c4, #b8a08e); --ptt-bg: radial-gradient(circle at 40% 40%, #e8d5c4, #b8a08e); --ptt-border: #7a5f44; --list-bg: rgba(0,0,0,0.05); --card-bg: rgba(255,255,255,0.3); --shadow-color: rgba(0,0,0,0.3); --status-bg: rgba(0,0,0,0.6); --status-color: #ffffff; --display-bg: #0a0a0a; --display-border: #3a2a1a; --antenna-color: #8b6b4a; --body-inner-shadow: inset 0 0 20px rgba(0,0,0,0.1); --speaker-grill: rgba(0,0,0,0.04); --ticker-bg: #0a0a0a; --ticker-text: #22c55e; --toggle-bg: #ccc; --toggle-active: #22c55e; }
        body.theme-camouflage { --radio-bg: linear-gradient(160deg, #4a5d4a, #2d3a2d); --radio-border: #5a6b5a; --brand-color: #b8c4b8; --panel-text: #e8f0e8; --led-color: #8fbc8f; --led-bg: #1a2a1a; --knob-bg: linear-gradient(145deg, #5a6b5a, #2d3a2d); --ptt-bg: radial-gradient(circle at 40% 40%, #5a6b5a, #2d3a2d); --ptt-border: #7a8f7a; --list-bg: rgba(0,0,0,0.3); --card-bg: rgba(255,255,255,0.08); --shadow-color: rgba(0,0,0,0.7); --status-bg: rgba(0,0,0,0.5); --status-color: #8fbc8f; --display-bg: #0a0a0a; --display-border: #2a3a2a; --antenna-color: #5a6b5a; --body-inner-shadow: inset 0 0 40px rgba(0,0,0,0.5); --speaker-grill: rgba(255,255,255,0.04); --ticker-bg: #0a0a0a; --ticker-text: #8fbc8f; --toggle-bg: #3a4a3a; --toggle-active: #8fbc8f; }
        body.theme-aviation { --radio-bg: linear-gradient(160deg, #b8d4e8, #8bb3cc); --radio-border: #4a6a7a; --brand-color: #e67e22; --panel-text: #1a2a3a; --led-color: #e67e22; --led-bg: #1a1a2a; --knob-bg: linear-gradient(145deg, #aac4d4, #7a9ab0); --ptt-bg: radial-gradient(circle at 40% 40%, #aac4d4, #7a9ab0); --ptt-border: #4a6a7a; --list-bg: rgba(0,0,0,0.08); --card-bg: rgba(255,255,255,0.2); --shadow-color: rgba(0,0,0,0.3); --status-bg: rgba(0,0,0,0.5); --status-color: #ffffff; --display-bg: #0a1a2a; --display-border: #2a4a5a; --antenna-color: #4a6a7a; --body-inner-shadow: inset 0 0 20px rgba(0,0,0,0.1); --speaker-grill: rgba(0,0,0,0.04); --ticker-bg: #0a1a2a; --ticker-text: #e67e22; --toggle-bg: #b0c8d8; --toggle-active: #e67e22; }
        body.theme-nautica { --radio-bg: linear-gradient(160deg, #1a3a5a, #0a1a2a); --radio-border: #2a5a7a; --brand-color: #7ab8d4; --panel-text: #d4e8f0; --led-color: #7ab8d4; --led-bg: #0a1a2a; --knob-bg: linear-gradient(145deg, #1a3a5a, #0a1a2a); --ptt-bg: radial-gradient(circle at 40% 40%, #1a4a6a, #0a1a2a); --ptt-border: #3a6a8a; --list-bg: rgba(0,0,0,0.3); --card-bg: rgba(255,255,255,0.06); --shadow-color: rgba(0,0,0,0.7); --status-bg: rgba(0,0,0,0.5); --status-color: #7ab8d4; --display-bg: #0a1a2a; --display-border: #1a3a5a; --antenna-color: #2a5a7a; --body-inner-shadow: inset 0 0 40px rgba(0,0,0,0.5); --speaker-grill: rgba(255,255,255,0.04); --ticker-bg: #0a1a2a; --ticker-text: #7ab8d4; --toggle-bg: #1a3a4a; --toggle-active: #7ab8d4; }
        body.theme-retro { --radio-bg: linear-gradient(160deg, #f5e6c8, #d4c4a8); --radio-border: #8a7a5a; --brand-color: #c0392b; --panel-text: #3a2a1a; --led-color: #c0392b; --led-bg: #2a1a0a; --knob-bg: linear-gradient(145deg, #e8d4b8, #b8a488); --ptt-bg: radial-gradient(circle at 40% 40%, #e8d4b8, #b8a488); --ptt-border: #8a7a5a; --list-bg: rgba(0,0,0,0.05); --card-bg: rgba(255,255,255,0.3); --shadow-color: rgba(0,0,0,0.3); --status-bg: rgba(0,0,0,0.5); --status-color: #f1c40f; --display-bg: #1a0a0a; --display-border: #3a2a1a; --antenna-color: #8a7a5a; --body-inner-shadow: inset 0 0 20px rgba(0,0,0,0.1); --speaker-grill: rgba(0,0,0,0.04); --ticker-bg: #1a0a0a; --ticker-text: #f1c40f; --toggle-bg: #d4c4a8; --toggle-active: #c0392b; }
        body.theme-night { --radio-bg: linear-gradient(160deg, #0a0a1a, #1a1a2a); --radio-border: #3a3a5a; --brand-color: #00ffff; --panel-text: #e0e0ff; --led-color: #00ffff; --led-bg: #001a1a; --knob-bg: linear-gradient(145deg, #1a1a2a, #0a0a1a); --ptt-bg: radial-gradient(circle at 40% 40%, #1a1a3a, #0a0a1a); --ptt-border: #3a3a6a; --list-bg: rgba(0,0,0,0.4); --card-bg: rgba(0,255,255,0.05); --shadow-color: rgba(0,0,0,0.8); --status-bg: rgba(0,0,0,0.6); --status-color: #00ffff; --display-bg: #000a0a; --display-border: #1a3a3a; --antenna-color: #3a3a5a; --body-inner-shadow: inset 0 0 50px rgba(0,0,0,0.6); --speaker-grill: rgba(255,255,255,0.03); --ticker-bg: #000a0a; --ticker-text: #00ffff; --toggle-bg: #1a1a3a; --toggle-active: #00ffff; }

        .ticker-bar {
            background: var(--ticker-bg); border: 1px solid var(--radio-border); border-radius: 12px;
            padding: 6px 12px; margin-bottom: 12px; overflow: hidden; white-space: nowrap;
            box-shadow: inset 0 0 20px rgba(0,0,0,0.5); position: relative; height: 40px; display: flex; align-items: center;
        }
        .ticker-text {
            display: inline-block; font-family: 'Orbitron', sans-serif; font-size: 16px; font-weight: 700;
            color: var(--ticker-text); text-shadow: 0 0 8px var(--ticker-text); animation: ticker-scroll 15s linear infinite;
            white-space: nowrap; padding-left: 100%;
        }
        @keyframes ticker-scroll { 0% { transform: translateX(0); } 100% { transform: translateX(-100%); } }
        .ticker-edit {
            position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
            background: rgba(0,0,0,0.6); border: none; color: var(--ticker-text); border-radius: 30px;
            padding: 4px 12px; font-size: 11px; font-weight: 700; cursor: pointer; backdrop-filter: blur(4px);
            border: 1px solid rgba(255,255,255,0.1); display: none; z-index: 2;
        }
        .ticker-edit:hover { background: rgba(255,255,255,0.15); }
        .ticker-edit.show { display: block; }

        .radio-body {
            background: var(--radio-bg); border: 3px solid var(--radio-border); border-radius: 28px 28px 32px 32px;
            padding: 18px; max-width: 420px; width: 100%; box-shadow: 0 25px 50px -8px var(--shadow-color), var(--body-inner-shadow);
            transition: all 0.3s; position: relative; backdrop-filter: blur(4px); -webkit-user-select: none; user-select: none;
        }
        .antenna {
            position: absolute; top: -38px; left: 50%; transform: translateX(-50%); width: 8px; height: 38px;
            background: linear-gradient(180deg, var(--antenna-color), var(--radio-border)); border-radius: 4px 4px 0 0; box-shadow: 0 -4px 12px rgba(0,0,0,0.3);
        }
        .antenna::after {
            content: ''; position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
            width: 14px; height: 6px; background: var(--antenna-color); border-radius: 4px 4px 0 0; opacity: 0.6;
        }
        .speaker-grill {
            position: absolute; bottom: 16px; right: 16px; width: 48px; height: 48px; background: var(--speaker-grill);
            border-radius: 50%; display: grid; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(5, 1fr);
            gap: 2px; padding: 6px; opacity: 0.3; pointer-events: none;
        }
        .speaker-grill span { background: var(--radio-border); border-radius: 2px; opacity: 0.3; }

        .brand-row {
            display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;
            padding-bottom: 8px; border-bottom: 2px solid rgba(128,128,128,0.12); flex-wrap: wrap; gap: 8px;
        }
        .brand-title {
            font-family: 'Orbitron', sans-serif; font-weight: 900; font-size: 18px; color: var(--brand-color);
            text-shadow: 0 0 16px rgba(56,189,248,0.25); letter-spacing: 1.5px;
        }
        .header-controls { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
        .header-controls select {
            background: rgba(0,0,0,0.5); color: var(--panel-text); border: 1px solid rgba(255,255,255,0.1);
            padding: 4px 8px; border-radius: 20px; font-size: 10px; font-family: inherit; outline: none; cursor: pointer;
        }
        .btn-header {
            background: rgba(255,255,255,0.06); color: var(--panel-text); border: 1px solid rgba(255,255,255,0.08);
            padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 600; cursor: pointer;
            transition: 0.2s; backdrop-filter: blur(4px); display: inline-flex; align-items: center; gap: 4px;
        }
        .btn-header:hover { background: rgba(255,255,255,0.12); }
        .btn-install { background: #0044aa; border-color: #003377; }
        .btn-eco { background: #004400; border-color: #002200; }
        .btn-logout { background: #aa0000; border-color: #770000; }
        .btn-update { background: #ff8800; border-color: #cc5500; color: #000; display: none; }

        .setup-screen {
            display: flex; flex-direction: column; gap: 12px; background: rgba(0,0,0,0.2);
            padding: 24px 20px; border-radius: 20px; backdrop-filter: blur(4px); border: 1px solid rgba(255,255,255,0.04);
        }
        .setup-screen input {
            background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08); border-radius: 30px;
            padding: 12px 18px; font-size: 15px; color: var(--panel-text); font-family: inherit;
            outline: none; transition: 0.3s; text-align: center; text-transform: uppercase;
        }
        .setup-screen input:focus { border-color: var(--brand-color); box-shadow: 0 0 0 3px rgba(56,189,248,0.15); }
        .setup-screen input::placeholder { color: rgba(255,255,255,0.25); text-transform: none; }
        .btn-connect {
            background: linear-gradient(135deg, #38bdf8, #2563eb); border: none; border-radius: 30px;
            padding: 14px; font-weight: 700; font-size: 16px; color: #fff; box-shadow: 0 6px 20px rgba(56,189,248,0.25);
            cursor: pointer; transition: 0.3s; font-family: inherit;
        }
        .btn-connect:hover { transform: translateY(-2px); box-shadow: 0 10px 28px rgba(56,189,248,0.4); }
        .btn-connect:active { transform: scale(0.97); }

        .panel-top { display: flex; gap: 12px; margin-bottom: 16px; align-items: stretch; }
        .vu-meter {
            flex: 0 0 140px; background: var(--display-bg); border-radius: 10px; border: 2px solid var(--display-border);
            box-shadow: inset 0 0 20px rgba(0,0,0,0.7); position: relative; height: 100px; overflow: hidden;
        }
        .vu-bg {
            position: absolute; bottom: 8px; left: 12px; right: 12px; height: 12px;
            background: linear-gradient(90deg, #22c55e 0%, #facc15 55%, #ef4444 85%); border-radius: 6px; opacity: 0.5;
        }
        .vu-needle {
            position: absolute; bottom: -12px; left: 50%; width: 3px; height: 100px; background: #f87171;
            transform-origin: bottom center; transform: rotate(-45deg); transition: transform 0.05s ease-out;
            z-index: 2; border-radius: 3px; box-shadow: 0 0 12px #f87171;
        }
        .vu-label {
            position: absolute; top: 4px; left: 0; right: 0; text-align: center; color: #888;
            font-size: 9px; font-weight: 700; letter-spacing: 0.5px; font-family: 'Inter', sans-serif;
        }
        .display-group { flex: 1; display: flex; flex-direction: column; gap: 6px; }
        .led-display {
            background: var(--display-bg); border: 2px solid var(--display-border); border-radius: 10px;
            box-shadow: inset 0 0 30px rgba(0,0,0,0.6); padding: 6px 10px; display: flex; align-items: center;
            justify-content: center; text-align: center; transition: all 0.3s; font-family: 'Orbitron', sans-serif; min-height: 44px;
        }
        .display-channel { font-size: 40px; color: var(--led-color); text-shadow: 0 0 16px var(--led-color); line-height: 1; }
        .display-status {
            font-size: 20px; font-weight: 700; color: var(--status-color); background: var(--status-bg);
            padding: 2px 14px; border-radius: 30px; text-shadow: 0 0 10px rgba(255,255,255,0.08);
            border: 1px solid rgba(255,255,255,0.04); font-family: 'Inter', sans-serif; min-height: 36px;
            display: flex; align-items: center; justify-content: center;
        }

        .channel-controls { display: flex; gap: 12px; align-items: center; justify-content: center; margin-bottom: 14px; }
        .radio-btn {
            background: linear-gradient(145deg, #2a3a4a, #1a2a3a); border: 1px solid #4a5a6a; border-bottom: 4px solid #0a1a2a;
            border-radius: 8px; color: #fff; font-family: 'Orbitron', sans-serif; font-weight: 700; text-transform: uppercase;
            box-shadow: 0 4px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.1); transition: all 0.1s ease;
            cursor: pointer; display: flex; align-items: center; justify-content: center;
        }
        .radio-btn:active, .radio-btn.pressed {
            transform: translateY(3px); border-bottom-width: 1px; box-shadow: 0 1px 2px rgba(0,0,0,0.3), inset 0 2px 4px rgba(0,0,0,0.5);
            background: linear-gradient(145deg, #1a2a3a, #2a3a4a);
        }
        .knob-btn { width: 52px; height: 44px; font-size: 22px; }

        .toggles-row {
            display: flex; justify-content: space-between; gap: 8px; background: rgba(0,0,0,0.1);
            border-radius: 16px; padding: 10px; margin-bottom: 14px; border: 1px solid rgba(255,255,255,0.03); flex-wrap: wrap;
        }
        .toggle-btn {
            flex: 1; min-width: 70px; position: relative; padding: 8px 6px; display: flex; flex-direction: column;
            align-items: center; gap: 6px; background: #1a1a2a; border: 1px solid #3a3a5a; border-radius: 8px;
            color: #888; font-size: 10px; font-weight: 700; cursor: pointer; box-shadow: inset 0 2px 4px rgba(0,0,0,0.5);
            transition: all 0.2s; text-align: center;
        }
        .toggle-btn.active {
            color: #fff; border-color: var(--toggle-active); background: rgba(56,189,248,0.1);
            box-shadow: 0 0 10px var(--toggle-active), inset 0 1px 2px rgba(0,0,0,0.3);
        }
        .toggle-btn .led {
            width: 8px; height: 8px; border-radius: 50%; background: #333;
            box-shadow: inset 0 1px 2px rgba(0,0,0,0.5); transition: all 0.2s;
        }
        .toggle-btn.active .led {
            background: var(--toggle-active); box-shadow: 0 0 8px var(--toggle-active), 0 0 15px var(--toggle-active);
        }
        .switch-input { display: none; }

        .ptt-container { display: flex; justify-content: center; margin: 6px 0 10px; }
        .ptt-btn {
            width: 120px; height: 120px; border-radius: 50%; background: var(--ptt-bg);
            border: 4px solid var(--ptt-border); box-shadow: 0 6px 0 rgba(0,0,0,0.3), 0 10px 25px rgba(0,0,0,0.4);
            cursor: pointer; transition: all 0.08s ease; display: flex; align-items: center;
            justify-content: center; font-size: 0; touch-action: manipulation; position: relative;
        }
        .ptt-btn::before { font-size: 44px; line-height: 1; transition: 0.2s; }
        .ptt-btn.ready::before { content: "🎙️"; color: #888; }
        .ptt-btn.tx::before { content: "🔴"; color: #ff4444; text-shadow: 0 0 20px #ff4444; }
        .ptt-btn.rx::before { content: "🟢"; color: #44ff44; text-shadow: 0 0 20px #44ff44; }
        .ptt-btn:active { transform: scale(0.93); box-shadow: 0 2px 0 rgba(0,0,0,0.3); }
        .ptt-btn.rx { animation: pulse-rx 1.2s infinite; }
        @keyframes pulse-rx {
            0% { box-shadow: 0 0 0 0 rgba(68,255,68,0.3); }
            70% { box-shadow: 0 0 0 16px rgba(68,255,68,0); }
            100% { box-shadow: 0 0 0 0 rgba(68,255,68,0); }
        }

        .member-list {
            margin-top: 12px; background: var(--list-bg); backdrop-filter: blur(4px);
            border-radius: 14px; padding: 12px; max-height: 140px; overflow-y: auto;
            border: 1px solid rgba(255,255,255,0.03);
        }
        .member-list-title {
            font-weight: 700; font-size: 10px; margin-bottom: 8px; color: var(--panel-text);
            opacity: 0.5; letter-spacing: 1px; display: flex; justify-content: space-between;
        }
        .member-item {
            display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 24px;
            background: var(--card-bg); margin-bottom: 4px; transition: 0.2s;
        }
        .member-item:last-child { margin-bottom: 0; }
        .member-avatar {
            width: 26px; height: 26px; border-radius: 50%; background: #2d3748; display: flex;
            align-items: center; justify-content: center; font-weight: 700; font-size: 11px;
            color: #fff; flex-shrink: 0;
        }
        .member-info { flex: 1; min-width: 0; }
        .member-name { font-size: 12px; font-weight: 600; color: var(--panel-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .member-status { font-size: 9px; color: #888; display: flex; align-items: center; gap: 4px; }
        .status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
        .status-dot.online { background: #22c55e; box-shadow: 0 0 6px #22c55e; }
        .member-badge { font-size: 8px; font-weight: 700; padding: 1px 6px; border-radius: 16px; background: #facc15; color: #000; }
        .btn-kick {
            background: rgba(239,68,68,0.15); color: #ef4444; border: none; padding: 2px 8px;
            border-radius: 20px; font-size: 10px; font-weight: 700; cursor: pointer; transition: 0.2s;
        }
        .btn-kick:hover { background: #ef4444; color: #fff; }

        .modal {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8);
            backdrop-filter: blur(4px); z-index: 1000; display: flex; align-items: center; justify-content: center;
        }
        .modal.hidden { display: none; }
        .modal-content {
            background: var(--radio-bg); border: 2px solid var(--radio-border); border-radius: 16px;
            padding: 24px; width: 90%; max-width: 360px; box-shadow: 0 20px 50px rgba(0,0,0,0.8);
        }
        .modal-content h3 { font-family: 'Orbitron', sans-serif; color: var(--brand-color); margin-bottom: 16px; text-align: center; }
        .modal-content input {
            width: 100%; background: rgba(0,0,0,0.3); border: 1px solid var(--radio-border); border-radius: 8px;
            padding: 12px; color: var(--panel-text); font-family: 'Inter', sans-serif; font-size: 14px;
            margin-bottom: 16px; outline: none;
        }
        .modal-content input:focus { border-color: var(--brand-color); }
        .modal-actions { display: flex; gap: 10px; }
        .modal-actions .radio-btn { flex: 1; padding: 12px; font-size: 12px; }

        .hidden { display: none !important; }
        .member-list::-webkit-scrollbar { width: 4px; }
        .member-list::-webkit-scrollbar-track { background: transparent; }
        .member-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.15); border-radius: 10px; }

        @media (max-width: 400px) {
            .radio-body { padding: 14px; border-radius: 24px 24px 28px 28px; }
            .vu-meter { flex: 0 0 110px; height: 85px; }
            .display-channel { font-size: 32px; }
            .display-status { font-size: 16px; }
            .toggles-row { gap: 6px; padding: 8px; }
            .toggle-btn { font-size: 9px; padding: 6px 4px; }
            .ptt-btn { width: 100px; height: 100px; }
            .brand-title { font-size: 16px; }
        }
    </style>
</head>
<body class="theme-voyager">

<div id="audio-container" style="display: none;"></div>

<div class="radio-body">
    <div class="antenna"></div>
    <div class="speaker-grill">
        <span></span><span></span><span></span><span></span><span></span>
        <span></span><span></span><span></span><span></span><span></span>
        <span></span><span></span><span></span><span></span><span></span>
        <span></span><span></span><span></span><span></span><span></span>
        <span></span><span></span><span></span><span></span><span></span>
    </div>

    <div class="ticker-bar">
        <span class="ticker-text" id="tickerDisplay">Bem-vindo ao Canabrava PX</span>
        <button class="ticker-edit" id="tickerEditBtn">✎ Editar</button>
    </div>

    <div class="brand-row">
        <div class="brand-title" id="brand-label">CANABRAVA PX</div>
        <div class="header-controls">
            <button id="btn-update" class="btn-header btn-update" onclick="forceServerUpdate()">🔄</button>
            <button id="btn-logout" class="btn-header btn-logout hidden" onclick="logout()">🚪</button>
            <button id="btn-eco" class="btn-header btn-eco hidden" onclick="toggleEcoMode()">🌙</button>
            <button id="btn-install-pwa" class="btn-header btn-install hidden">📲 App</button>
            <select id="skin-select" onchange="changeTheme(this.value)">
                <option value="theme-voyager">Voyager</option>
                <option value="theme-cobra">Cobra</option>
                <option value="theme-camouflage">Camuflagem</option>
                <option value="theme-aviation">Aviação</option>
                <option value="theme-nautica">Náutica</option>
                <option value="theme-retro">Retrô</option>
                <option value="theme-night">Noturno</option>
            </select>
        </div>
    </div>

    <div id="setup-screen" class="setup-screen">
        <input type="text" id="in-username" placeholder="📡 OPERADOR" required autocomplete="off">
        <input type="text" id="in-city" placeholder="📍 QTH (CIDADE)" required autocomplete="off">
        <button class="btn-connect" id="btn-start" onclick="connectRadio()">🔌 LIGAR RÁDIO</button>
    </div>

    <div id="radio-screen" class="hidden">
        <div class="panel-top">
            <div class="vu-meter">
                <div class="vu-label">S/RF</div>
                <div class="vu-bg"></div>
                <div class="vu-needle" id="vu-needle"></div>
            </div>
            <div class="display-group">
                <div class="led-display display-channel" id="display-ch">01</div>
                <div class="led-display display-status" id="display-status">QAP</div>
            </div>
        </div>

        <div class="channel-controls">
            <button class="radio-btn knob-btn" onclick="changeChannel(-1)">▼</button>
            <button class="radio-btn knob-btn" onclick="changeChannel(1)">▲</button>
        </div>

        <div class="toggles-row">
            <label class="toggle-btn" id="vox-toggle-label" onclick="toggleSwitch('vox')">
                <span class="led"></span><span>VOX</span>
                <input type="checkbox" class="switch-input" id="vox-toggle">
            </label>
            <label class="toggle-btn active" id="beep-toggle-label" onclick="toggleSwitch('beep')">
                <span class="led"></span><span>BEEP</span>
                <input type="checkbox" class="switch-input" id="beep-toggle" checked>
            </label>
            <label class="toggle-btn" id="echo-toggle-label" onclick="toggleSwitch('echo')">
                <span class="led"></span><span>ECHO</span>
                <input type="checkbox" class="switch-input" id="echo-toggle">
            </label>
        </div>
        
        <div style="display: flex; gap: 8px; margin-bottom: 14px; justify-content: center;">
            <select id="beep-select" style="background: rgba(0,0,0,0.4); color: var(--panel-text); border: 1px solid rgba(255,255,255,0.06); padding: 4px 8px; border-radius: 20px; font-size: 10px; outline: none; flex: 1;">
                <option value="classic">Bip Clássico</option>
                <option value="nasa">NASA</option>
                <option value="motorola">Motorola HT</option>
                <option value="policia">Policial</option>
                <option value="digital">Digital</option>
                <option value="custom">Arquivo...</option>
            </select>
            <input type="range" id="echo-level" min="1" max="8" value="4" style="flex: 1; height: 6px; -webkit-appearance: none; background: #555; border-radius: 3px; outline: none;" title="Nível do Echo">
        </div>
        <input type="file" id="beep-file" accept="audio/mp3, audio/wav, audio/ogg" class="hidden">

        <div class="ptt-container">
            <button id="ptt-trigger" class="ptt-btn ready"></button>
        </div>

        <div class="member-list">
            <div class="member-list-title">
                <span>📻 ESTAÇÕES NO CANAL</span>
                <span id="peer-count" style="color: var(--brand-color);">0</span>
            </div>
            <div id="peers-container"></div>
        </div>
    </div>
</div>

<div id="ticker-modal" class="modal hidden">
    <div class="modal-content">
        <h3>✎ EDITAR LETREIRO</h3>
        <input type="text" id="ticker-input" maxlength="200" placeholder="Digite a mensagem do letreiro...">
        <div class="modal-actions">
            <button class="radio-btn" onclick="closeTickerModal()">CANCELAR</button>
            <button class="radio-btn" style="background: var(--toggle-active); color: #000; border-bottom-color: #1a6a8a;" onclick="saveTicker()">SALVAR</button>
        </div>
    </div>
</div>

<script>
    document.addEventListener("DOMContentLoaded", () => {
        const savedUser = localStorage.getItem('radio_user');
        const savedCity = localStorage.getItem('radio_city');
        if (savedUser && savedCity) {
            document.getElementById('in-username').value = savedUser;
            document.getElementById('in-city').value = savedCity;
            document.getElementById('btn-start').innerText = "🔌 RECONECTAR";
        }
    });

    function logout() {
        localStorage.removeItem('radio_user');
        localStorage.removeItem('radio_city');
        if (room) room.disconnect();
        window.location.reload();
    }

    let deferredPrompt;
    let wakeLock = null;
    const btnInstallPwa = document.getElementById('btn-install-pwa');
    if ('serviceWorker' in navigator) navigator.serviceWorker.register('?pwa=sw');

    window.addEventListener('beforeinstallprompt', (e) => {
        e.preventDefault(); 
        deferredPrompt = e; 
        btnInstallPwa.classList.remove('hidden');
    });
    
    btnInstallPwa.addEventListener('click', async () => {
        if (!deferredPrompt) return;
        deferredPrompt.prompt();
        const { outcome } = await deferredPrompt.userChoice;
        if (outcome === 'accepted') btnInstallPwa.classList.add('hidden');
        deferredPrompt = null;
    });

    async function requestWakeLock() {
        try {
            if ('wakeLock' in navigator) {
                wakeLock = await navigator.wakeLock.request('screen');
                document.getElementById('btn-eco').classList.remove('hidden');
            }
        } catch (err) {}
    }
    document.addEventListener('visibilitychange', async () => {
        if (wakeLock !== null && document.visibilityState === 'visible') requestWakeLock();
    });
    function toggleEcoMode() { document.body.classList.toggle('eco-mode'); }

    function changeTheme(theme) { document.body.className = theme; localStorage.setItem('radio_skin', theme); }
    const savedSkin = localStorage.getItem('radio_skin') || 'theme-voyager';
    document.getElementById('skin-select').value = savedSkin;
    changeTheme(savedSkin);

    function toggleSwitch(id) {
        const chk = document.getElementById(id + '-toggle');
        const label = document.getElementById(id + '-toggle-label');
        chk.checked = !chk.checked;
        label.classList.toggle('active', chk.checked);
        chk.dispatchEvent(new Event('change'));
    }

    let config = { peerId: null, username: '', city: '', channel: 1 };
    let room = null;
    let localTrack = null;
    let state = { isAdmin: false, isReceiving: false, isTransmitting: false, currentSpeaker: null };
    let rxAnalysers = {};
    let audioCtx, localStream, processedStream;
    let localAnalyser, txGain;
    let dataArray = new Uint8Array(0);
    let beepGainNode = null;
    let currentTicker = 'Bem-vindo ao Canabrava PX';

    const vuNeedle = document.getElementById('vu-needle');
    const pttBtn = document.getElementById('ptt-trigger');
    const displayStatus = document.getElementById('display-status');
    const voxToggle = document.getElementById('vox-toggle');
    const beepToggle = document.getElementById('beep-toggle');
    const tickerDisplay = document.getElementById('tickerDisplay');
    const tickerEditBtn = document.getElementById('tickerEditBtn');
    const peersContainer = document.getElementById('peers-container');
    const peerCount = document.getElementById('peer-count');

    const echoToggle = document.getElementById('echo-toggle');
    const echoSlider = document.getElementById('echo-level');

    function updateEchoSettings() {
        if (window.echoFeedbackNode) {
            window.echoFeedbackNode.gain.value = echoToggle.checked ? parseInt(echoSlider.value) / 10 : 0;
        }
    }
    echoToggle.addEventListener('change', updateEchoSettings);
    echoSlider.addEventListener('input', updateEchoSettings);

    const beepSelect = document.getElementById('beep-select');
    const beepFile = document.getElementById('beep-file');
    let customBeepUrl = null, customBeepAudio = null, customBeepSource = null;

    beepSelect.addEventListener('change', (e) => { if (e.target.value === 'custom') beepFile.click(); });
    beepFile.addEventListener('change', (e) => {
        const file = e.target.files[0];
        if (file) {
            if (customBeepUrl) URL.revokeObjectURL(customBeepUrl);
            customBeepUrl = URL.createObjectURL(file);
            customBeepAudio = new Audio(customBeepUrl);
            if (audioCtx) customBeepSource = audioCtx.createMediaElementSource(customBeepAudio);
        } else { beepSelect.value = 'classic'; }
    });

    function generateBeep(broadcast = false) {
        if (!audioCtx) return 0;
        const beepType = beepSelect.value;
        let duration = 0.2;
        if (!beepGainNode) { beepGainNode = audioCtx.createGain(); beepGainNode.gain.value = 0.4; }
        if (broadcast && txGain) beepGainNode.connect(txGain);
        beepGainNode.connect(audioCtx.destination);
        function createTone(freq, startOffset, dur, type = 'sine') {
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.type = type;
            osc.frequency.setValueAtTime(freq, audioCtx.currentTime + startOffset);
            gain.gain.setValueAtTime(0.3, audioCtx.currentTime + startOffset);
            gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + startOffset + dur);
            osc.connect(gain); gain.connect(beepGainNode);
            osc.start(audioCtx.currentTime + startOffset);
            osc.stop(audioCtx.currentTime + startOffset + dur);
        }
        switch(beepType) {
            case 'classic': createTone(1200, 0, 0.15); duration = 0.2; break;
            case 'nasa': createTone(2525, 0, 0.25); duration = 0.3; break;
            case 'motorola': createTone(1000, 0, 0.1); createTone(1500, 0.1, 0.15); duration = 0.3; break;
            case 'policia': createTone(880, 0, 0.1); createTone(1320, 0.12, 0.1); duration = 0.25; break;
            case 'digital': createTone(1800, 0, 0.05); createTone(2200, 0.06, 0.05); duration = 0.15; break;
            case 'custom':
                if (customBeepAudio) {
                    customBeepAudio.currentTime = 0;
                    if (customBeepSource) {
                        customBeepSource.disconnect(); customBeepSource.connect(beepGainNode);
                        if (broadcast && txGain) customBeepSource.connect(txGain);
                    }
                    customBeepAudio.play().catch(e => {});
                    duration = customBeepAudio.duration || 1;
                } break;
            default: createTone(1200, 0, 0.15); duration = 0.2;
        }
        return duration;
    }

    function ensureAudioContextRunning() {
        if (audioCtx && audioCtx.state === 'suspended') audioCtx.resume().catch(e => {});
    }

    function setupAudioSystem() {
        audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        ensureAudioContextRunning();
        const source = audioCtx.createMediaStreamSource(localStream);
        const txDest = audioCtx.createMediaStreamDestination();
        txGain = audioCtx.createGain(); txGain.gain.value = 1;
        localAnalyser = audioCtx.createAnalyser(); localAnalyser.fftSize = 256;
        dataArray = new Uint8Array(localAnalyser.frequencyBinCount);
        const delayNode = audioCtx.createDelay(1.0); delayNode.delayTime.value = 0.28;
        const feedbackGain = audioCtx.createGain(); feedbackGain.gain.value = 0;
        window.echoFeedbackNode = feedbackGain;
        source.connect(localAnalyser); source.connect(delayNode);
        delayNode.connect(feedbackGain); feedbackGain.connect(delayNode);
        source.connect(txGain); feedbackGain.connect(txGain);
        txGain.connect(txDest); processedStream = txDest.stream;
        updateEchoSettings(); renderMasterLoop();
    }

    function renderMasterLoop() {
        requestAnimationFrame(renderMasterLoop);
        if (state.isTransmitting && localAnalyser) {
            localAnalyser.getByteFrequencyData(dataArray);
            updateNeedleUI(dataArray, localAnalyser.frequencyBinCount);
        } else if (state.isReceiving && state.currentSpeaker && rxAnalysers[state.currentSpeaker]) {
            let rx = rxAnalysers[state.currentSpeaker];
            rx.analyser.getByteFrequencyData(rx.dataArray);
            updateNeedleUI(rx.dataArray, rx.analyser.frequencyBinCount);
        } else {
            vuNeedle.style.transform = `rotate(-45deg)`;
        }
    }

    function updateNeedleUI(data, length) {
        let sum = 0; for(let i=0; i<length; i++) sum += data[i];
        let avg = sum / length;
        let angle = -45 + ((avg / 60) * 90);
        if(angle > 45) angle = 45; if(angle < -45) angle = -45;
        vuNeedle.style.transform = `rotate(${angle}deg)`;
    }

    async function checkAdminStatus() {
        try {
            const res = await fetch('?action=ping', {
                method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config)
            });
            
            // Verifica se a resposta é OK antes de tentar ler JSON
            if (!res.ok) {
                const errorText = await res.text();
                console.error("❌ Erro HTTP no ping:", res.status, errorText);
                return null;
            }
            
            const data = await res.json();
            
            if (data.error) {
                console.error("❌ Erro no ping:", data.error);
                return null;
            }
            
            console.log("✅ Admin check:", { isAdmin: data.isAdmin, username: config.username });
            
            state.isAdmin = data.isAdmin;
            
            if (state.isAdmin) {
                tickerEditBtn.classList.add('show');
                console.log("✅ Botão de editar letreiro ativado");
            } else {
                tickerEditBtn.classList.remove('show');
            }
            
            if (data.ticker && data.ticker !== currentTicker) {
                currentTicker = data.ticker;
                tickerDisplay.textContent = currentTicker;
            }
            
            return data;
        } catch (e) {
            console.error("❌ Erro ao verificar admin:", e);
            return null;
        }
    }

    async function initLiveKit() {
        try {
            displayStatus.innerText = "CONECTANDO...";
            const response = await fetch('?action=getToken', {
                method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config)
            });
            
            if (!response.ok) {
                throw new Error("Falha ao obter token do servidor");
            }
            
            const authData = await response.json();

            room = new LivekitClient.Room({ adaptiveStream: true, dynacast: true });

            room.on(LivekitClient.RoomEvent.ParticipantConnected, () => renderPeers());
            room.on(LivekitClient.RoomEvent.ParticipantDisconnected, () => renderPeers());

            room.on(LivekitClient.RoomEvent.TrackSubscribed, (track, publication, participant) => {
                if (track.kind === LivekitClient.Track.Kind.Audio) {
                    const element = track.attach();
                    document.getElementById('audio-container').appendChild(element);
                    ensureAudioContextRunning();
                    const stream = new MediaStream([track.mediaStreamTrack]);
                    const rxSource = audioCtx.createMediaStreamSource(stream);
                    const analyser = audioCtx.createAnalyser(); analyser.fftSize = 256;
                    rxSource.connect(analyser);
                    rxAnalysers[participant.identity] = { analyser, dataArray: new Uint8Array(analyser.frequencyBinCount) };
                }
            });

            room.on(LivekitClient.RoomEvent.TrackUnsubscribed, (track, publication, participant) => {
                track.detach(); delete rxAnalysers[participant.identity];
            });

            room.on(LivekitClient.RoomEvent.DataReceived, (payload, participant) => {
                const msg = new TextDecoder().decode(payload);
                if (msg === 'TX_START') {
                    state.isReceiving = true; state.currentSpeaker = participant.identity;
                    updateUIReady(); ensureAudioContextRunning();
                } else if (msg === 'TX_END') {
                    if (state.currentSpeaker === participant.identity) {
                        state.isReceiving = false; state.currentSpeaker = null;
                        if (beepToggle.checked) generateBeep(false);
                        updateUIReady();
                    }
                } else if (msg === 'FORCE_UPDATE') {
                    window.location.reload(true);
                } else if (msg.startsWith('TICKER:')) {
                    currentTicker = msg.substring(7); tickerDisplay.textContent = currentTicker;
                }
            });

            await room.connect(authData.url, authData.token);

            const mediaTrack = processedStream.getAudioTracks()[0];
            if (!mediaTrack) throw new Error("Nenhuma faixa de áudio no stream processado");

            localTrack = new LivekitClient.LocalAudioTrack(mediaTrack);
            await room.localParticipant.publishTrack(localTrack, { name: 'ptt-audio', source: LivekitClient.Track.Source.Microphone });
            
            localTrack.mute();

            // Verifica admin imediatamente após conectar
            await checkAdminStatus();
            
            // Mantém sync periódico
            setInterval(checkAdminStatus, 5000);

            displayStatus.innerText = "QAP";
            pttBtn.className = "ptt-btn ready";
            console.log("✅ LiveKit conectado. Remotos:", room.remoteParticipants.size);
            renderPeers();

        } catch (e) {
            console.error("❌ Erro LiveKit:", e);
            alert("Falha ao conectar: " + e.message);
            displayStatus.innerText = "ERRO";
        }
    }

    function renderPeers() {
        if (!peersContainer || !room) return;
        peersContainer.innerHTML = '';

        const myItem = document.createElement('div');
        myItem.className = 'member-item';
        myItem.innerHTML = `
            <div class="member-avatar" style="background:#facc15;color:#000;">${config.username.charAt(0)}</div>
            <div class="member-info">
                <div class="member-name">${config.username} (${config.city})</div>
                <div class="member-status"><span class="status-dot online"></span> VOCÊ</div>
            </div>
            <span class="member-badge">LOCAL</span>`;
        peersContainer.appendChild(myItem);

        const remotePeers = Array.from(room.remoteParticipants.values());
        peerCount.innerText = remotePeers.length;

        if (remotePeers.length === 0) {
            const emptyMsg = document.createElement('div');
            emptyMsg.style.cssText = 'color:var(--panel-text);opacity:0.4;font-size:12px;text-align:center;padding:12px;';
            emptyMsg.innerText = 'Nenhuma outra estação conectada.';
            peersContainer.appendChild(emptyMsg);
        } else {
            remotePeers.forEach(p => {
                const item = document.createElement('div');
                item.className = 'member-item';
                item.innerHTML = `
                    <div class="member-avatar">${p.name ? p.name.charAt(0) : '?'}</div>
                    <div class="member-info">
                        <div class="member-name">${p.name || 'Desconhecida'}</div>
                        <div class="member-status"><span class="status-dot online"></span> Online</div>
                    </div>`;
                if (state.isAdmin) {
                    const btn = document.createElement('button');
                    btn.className = 'btn-kick'; btn.innerText = 'KICK';
                    btn.onclick = () => kickUser(p.identity);
                    item.appendChild(btn);
                }
                peersContainer.appendChild(item);
            });
        }
    }

    async function kickUser(targetId) {
        if (!confirm("Expulsar esta estação?")) return;
        await fetch('?action=admin_kick', {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ channel: config.channel, targetId })
        });
    }

    const tickerModal = document.getElementById('ticker-modal');
    const tickerInput = document.getElementById('ticker-input');
    tickerEditBtn.addEventListener('click', () => {
        if (!state.isAdmin) {
            alert("Apenas administradores podem editar o letreiro.");
            return;
        }
        tickerInput.value = currentTicker;
        tickerModal.classList.remove('hidden');
        tickerInput.focus();
    });
    
    function closeTickerModal() { tickerModal.classList.add('hidden'); }
    
    async function saveTicker() {
        const newMsg = tickerInput.value.trim();
        if (!newMsg) { alert('Mensagem vazia.'); return; }
        const res = await fetch('?action=set_ticker', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                channel: config.channel,
                userId: config.peerId,
                username: config.username,
                message: newMsg
            })
        });
        const data = await res.json();
        if (data.success) {
            currentTicker = data.new_ticker;
            tickerDisplay.textContent = currentTicker;
            await room.localParticipant.publishData(new TextEncoder().encode('TICKER:' + currentTicker), { reliable: true });
            closeTickerModal();
        } else {
            alert('Erro: ' + (data.error || ''));
        }
    }

    async function changeChannel(direction) {
        if (state.isTransmitting || state.isReceiving) return;
        config.channel += direction;
        if (config.channel > 23) config.channel = 1;
        if (config.channel < 1) config.channel = 23;
        document.getElementById('display-ch').innerText = config.channel.toString().padStart(2, '0');
        displayStatus.innerText = "TROCANDO...";
        if (room) await room.disconnect();
        rxAnalysers = {};
        localTrack = null;
        initLiveKit();
    }

    async function connectRadio() {
        config.username = document.getElementById('in-username').value.trim().toUpperCase();
        config.city = document.getElementById('in-city').value.trim().toUpperCase();
        if (!config.username || !config.city) {
            alert("Preencha Operador e Cidade.");
            return;
        }
        
        localStorage.setItem('radio_user', config.username);
        localStorage.setItem('radio_city', config.city);
        
        if (config.username === "ADMIN") {
            document.getElementById('btn-update').style.display = 'inline-flex';
            console.log("✅ Usuário ADMIN detectado - botão de update ativado");
        }
        
        if (!localStorage.getItem('radio_id')) {
            localStorage.setItem('radio_id', 'op_' + Math.floor(Math.random() * 100000));
        }
        config.peerId = localStorage.getItem('radio_id');

        try {
            localStream = await navigator.mediaDevices.getUserMedia({
                audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true }
            });
            setupAudioSystem();
            requestWakeLock();
        } catch (e) {
            alert("Erro no Microfone: " + e.message);
            return;
        }

        document.getElementById('setup-screen').classList.add('hidden');
        document.getElementById('radio-screen').classList.remove('hidden');
        document.getElementById('btn-logout').classList.remove('hidden');
        
        initLiveKit();
    }

    async function forceServerUpdate() {
        if (!confirm("Recarregar todos?")) return;
        await room.localParticipant.publishData(new TextEncoder().encode("FORCE_UPDATE"), { reliable: true });
        window.location.reload(true);
    }

    let txTimeout;

    async function startTx() {
        try {
            if (state.isReceiving || state.isTransmitting || !room || !localTrack) return;
            ensureAudioContextRunning();
            state.isTransmitting = true;

            await room.localParticipant.publishData(new TextEncoder().encode("TX_START"), { reliable: true });
            localTrack.unmute();
            updateUIReady();
            txTimeout = setTimeout(() => stopTx(), 60000);
        } catch (e) {
            console.error("Erro TX:", e);
            alert("Falha ao transmitir: " + e.message);
            state.isTransmitting = false;
            updateUIReady();
        }
    }

    async function stopTx() {
        if (!state.isTransmitting) return;
        clearTimeout(txTimeout);
        state.isTransmitting = false;

        let delayDoBip = 0;
        if (beepToggle.checked) delayDoBip = generateBeep(true);

        setTimeout(async () => {
            if (!state.isTransmitting && localTrack) {
                localTrack.mute();
                await room.localParticipant.publishData(new TextEncoder().encode("TX_END"), { reliable: true });
            }
        }, (delayDoBip * 1000) + 100);

        updateUIReady();
    }

    function updateUIReady() {
        if (state.isTransmitting) {
            displayStatus.innerText = "TX";
            pttBtn.className = "ptt-btn tx";
        } else if (state.isReceiving) {
            displayStatus.innerText = "RX";
            pttBtn.className = "ptt-btn rx";
        } else {
            displayStatus.innerText = state.isAdmin ? "QAP ★" : "QAP";
            pttBtn.className = "ptt-btn ready";
        }
    }

    function handlePttPress(e) {
        if (e.target.id === 'ptt-trigger' || e.target.closest('#ptt-trigger')) {
            e.preventDefault();
            if (voxToggle.checked) {
                if (state.isTransmitting) stopTx();
                else startTx();
            } else {
                startTx();
            }
        }
    }
    
    function handlePttRelease(e) {
        if (!voxToggle.checked && state.isTransmitting) stopTx();
    }

    pttBtn.addEventListener('mousedown', handlePttPress);
    pttBtn.addEventListener('touchstart', handlePttPress, { passive: false });
    window.addEventListener('mouseup', handlePttRelease);
    window.addEventListener('touchend', handlePttRelease);
</script>
</body>
</html>