<?php
require_once __DIR__ . '/auth.php';
require_once __DIR__ . '/announcements.php';
require_once __DIR__ . '/service_status.php';

function currentScriptPath() {
    $script = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? ''));
    $parts = explode('/music/', $script, 2);
    return isset($parts[1]) ? $parts[1] : ltrim($script, '/');
}

function assetUrl($basePath, $relativePath) {
    $relativePath = ltrim((string)$relativePath, '/');
    $filePath = dirname(__DIR__) . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativePath);
    $version = is_file($filePath) ? (string)filemtime($filePath) : date('Ymd');
    $separator = strpos($relativePath, '?') === false ? '?' : '&';
    return $basePath . $relativePath . $separator . 'v=' . rawurlencode($version);
}

function appRootUrlPath() {
    $script = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? ''));
    $dir = str_replace('\\', '/', dirname($script));
    if (basename($dir) === 'admin') {
        $dir = dirname($dir);
    }
    $dir = rtrim($dir, '/');
    return ($dir === '' || $dir === '.') ? '/' : $dir . '/';
}

function appAssetVersion() {
    $root = dirname(__DIR__);
    $files = [
        'service-worker.js',
        'assets/css/style.css',
        'assets/css/floating-player.css',
        'assets/js/api-client.js',
        'assets/js/sidebar.js',
        'assets/js/player.js',
        'assets/js/download.js',
        'assets/js/announcements.js',
        'assets/js/feedback.js',
        'assets/js/metadata-request.js',
        'assets/js/user-settings.js',
        'assets/js/home-customizer.js',
        'assets/js/onboarding.js',
        'assets/js/scroll-top.js',
        'assets/js/accessibility.js',
        'assets/js/app-navigation.js',
        'assets/js/pwa.js',
    ];
    $version = 0;
    foreach ($files as $file) {
        $path = $root . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $file);
        if (is_file($path)) {
            $version = max($version, (int)filemtime($path));
        }
    }
    return (string)($version ?: time());
}

function getCurrentMenuKey($fallback = 'home') {
    $path = currentScriptPath();
    $file = basename($path);
    if ($file === 'index.php') {
        return (isset($_GET['view']) && $_GET['view'] === 'all') ? 'all' : 'home';
    }
    $map = [
        'login.php' => 'login',
        'logout.php' => 'logout',
        'search.php' => 'search',
        'user_settings.php' => 'user_settings',
        'help.php' => 'help',
        'policies.php' => 'policies',
        'updates.php' => 'updates',
        'status.php' => 'service_status',
        'stats.php' => 'stats',
        'listening_notes.php' => 'listening_notes',
        'tags.php' => 'tags',
        'tag.php' => 'tags',
        'new.php' => 'new',
        'liked.php' => 'liked',
        'recent.php' => 'recent',
        'popular.php' => 'popular',
        'folders.php' => 'folders',
        'folder.php' => 'folders',
        'artists.php' => 'artists',
        'artist.php' => 'artists',
        'albums.php' => 'albums',
        'album.php' => 'albums',
        'genres.php' => 'genres',
        'genre.php' => 'genres',
        'recommend.php' => 'recommend',
        'auto_playlists.php' => 'auto_playlists',
        'auto_playlist.php' => 'auto_playlists',
        'playlists.php' => 'playlists',
        'playlist_view.php' => 'playlists',
        'playlist_create.php' => 'playlist_create',
        'playlist_edit.php' => 'playlists',
        'song.php' => 'all',
        'song_edit.php' => 'song_edit',
        'settings.php' => 'settings',
        'setup_admin.php' => 'setup_admin',
        'users.php' => 'admin_users',
        'api_tokens.php' => 'api_tokens',
        'scan.php' => 'scan',
        'discovery.php' => 'discovery',
        'diagnostics.php' => 'diagnostics',
        'logs.php' => 'logs',
        'log_view.php' => 'logs',
        'log_clear.php' => 'logs',
        'audit_logs.php' => 'audit_logs',
        'alerts.php' => 'alerts',
        'announcements.php' => 'announcements',
        'patch_notes.php' => 'patch_notes',
        'service_status.php' => 'service_status_admin',
        'music_requests.php' => 'music_requests',
        'jobs.php' => 'jobs',
        'feedback.php' => 'feedback',
        'playback_failures.php' => 'playback_failures',
        'performance.php' => 'performance',
        'listening_stats.php' => 'admin_listening_stats',
        'smart_organize.php' => 'smart_organize',
        'tag_manage.php' => 'tag_manage',
        'name_merge.php' => 'name_merge',
        'metadata_quality.php' => 'metadata_quality',
        'metadata.php' => 'metadata',
        'metadata_requests.php' => 'metadata_requests',
        'bulk_edit.php' => 'bulk_edit',
        'bulk_update.php' => 'bulk_edit',
        'duplicates.php' => 'duplicates',
        'backup_center.php' => 'backup',
        'export.php' => 'backup',
        'import.php' => 'backup',
        'backup_delete.php' => 'backup',
        'backup.php' => 'backup',
        'restore.php' => 'backup',
        'backup_download.php' => 'backup',
        'restore_run.php' => 'backup',
        'missing_files.php' => 'missing_files',
        'maintenance.php' => 'maintenance',
    ];
    if ($path === 'admin/index.php') return 'admin_home';
    if (strpos($path, 'admin/') !== false) return 'admin_home';
    return $map[$file] ?? $fallback;
}

function sidebarSettingBool($key, $default = true) {
    return function_exists('settingBool') ? settingBool($key, $default) : $default;
}

function getSidebarSettings() {
    return [
        'showRecommend' => sidebarSettingBool('sidebar_show_recommend', true),
        'showAutoPlaylists' => sidebarSettingBool('sidebar_show_auto_playlists', true),
        'showAdmin' => sidebarSettingBool('sidebar_show_admin', true),
        'showSettingsDetail' => sidebarSettingBool('sidebar_show_settings_detail', true),
        'groupsDefaultOpen' => sidebarSettingBool('sidebar_groups_default_open', true),
        'mobileAutoClose' => sidebarSettingBool('sidebar_mobile_auto_close', true),
        'compactMode' => sidebarSettingBool('sidebar_compact_mode', false),
    ];
}

function sidebarMenuGroups($basePath, $settings) {
    $groups = [
        'my_music' => [
            'title' => '내 음악',
            'items' => [
                'all' => ['name' => '전체 음악', 'link' => $basePath . 'index.php?view=all', 'icon' => 'fa-solid fa-music'],
                'new' => ['name' => '최근 추가', 'link' => $basePath . 'new.php', 'icon' => 'fa-solid fa-sparkles'],
                'liked' => ['name' => '좋아한 곡', 'link' => $basePath . 'liked.php', 'icon' => 'fa-solid fa-heart'],
                'recent' => ['name' => '최근 재생', 'link' => $basePath . 'recent.php', 'icon' => 'fa-regular fa-clock'],
                'popular' => ['name' => '많이 들은 곡', 'link' => $basePath . 'popular.php', 'icon' => 'fa-solid fa-fire'],
                'folders' => ['name' => '폴더별 보기', 'link' => $basePath . 'folders.php', 'icon' => 'fa-solid fa-folder-tree'],
            ],
        ],
        'explore' => [
            'title' => '탐색',
            'items' => [
                'artists' => ['name' => '아티스트', 'link' => $basePath . 'artists.php', 'icon' => 'fa-solid fa-user'],
                'albums' => ['name' => '앨범', 'link' => $basePath . 'albums.php', 'icon' => 'fa-solid fa-compact-disc'],
                'genres' => ['name' => '장르', 'link' => $basePath . 'genres.php', 'icon' => 'fa-solid fa-tags'],
            ],
        ],
        'recommend' => [
            'title' => '추천',
            'items' => [
                'recommend' => ['name' => '추천 음악 보기', 'link' => $basePath . 'recommend.php', 'icon' => 'fa-solid fa-star'],
                'auto_playlists' => ['name' => '자동 플레이리스트', 'link' => $basePath . 'auto_playlists.php', 'icon' => 'fa-solid fa-wand-magic-sparkles'],
            ],
        ],
        'playlists' => [
            'title' => '플레이리스트',
            'items' => [
                'playlists' => ['name' => '내 플레이리스트', 'link' => $basePath . 'playlists.php', 'icon' => 'fa-solid fa-list'],
                'playlist_create' => ['name' => '새 플레이리스트', 'link' => $basePath . 'playlist_create.php', 'icon' => 'fa-solid fa-plus'],
            ],
        ],
        'support' => [
            'title' => '도움 / 설정',
            'items' => [
                'music_requests' => ['name' => '음악 요청', 'link' => $basePath . 'music_requests.php', 'icon' => 'fa-regular fa-message'],
                'user_settings' => ['name' => '사용자 설정', 'link' => $basePath . 'user_settings.php', 'icon' => 'fa-solid fa-sliders'],
                'help' => ['name' => '도움말', 'link' => $basePath . 'help.php', 'icon' => 'fa-solid fa-circle-question'],
                'policies' => ['name' => '정책 안내', 'link' => $basePath . 'policies.php', 'icon' => 'fa-solid fa-scale-balanced'],
                'updates' => ['name' => '업데이트', 'link' => $basePath . 'updates.php', 'icon' => 'fa-solid fa-clock-rotate-left'],
                'service_status' => ['name' => '서비스 상태', 'link' => $basePath . 'status.php', 'icon' => 'fa-solid fa-signal'],
            ],
        ],
    ];

    if (isset($groups['explore']['items']) && !isset($groups['explore']['items']['tags'])) {
        $groups['explore']['items'] = array_slice($groups['explore']['items'], 0, 3, true)
            + ['tags' => ['name' => '태그 탐색', 'link' => $basePath . 'tags.php', 'icon' => 'fa-solid fa-hashtag']]
            + array_slice($groups['explore']['items'], 3, null, true);
    }

    if ($settings['showRecommend'] === false) unset($groups['recommend']);
    if (!$settings['showAutoPlaylists'] && isset($groups['recommend'])) {
        unset($groups['recommend']['items']['auto_playlists']);
    }

    if (isLoggedIn() && $settings['showAdmin']) {
        $groups['admin'] = [
            'title' => '관리',
            'items' => [
                'admin_home' => ['name' => '관리자 홈', 'link' => $basePath . 'admin/index.php', 'icon' => 'fa-solid fa-gauge-high'],
                'admin_users' => ['name' => '사용자 관리', 'link' => $basePath . 'admin/users.php', 'icon' => 'fa-solid fa-users-gear'],
                'scan' => ['name' => '음악 스캔', 'link' => $basePath . 'admin/scan.php', 'icon' => 'fa-solid fa-rotate'],
                'discovery' => ['name' => '음악 발견 추천', 'link' => $basePath . 'admin/discovery.php', 'icon' => 'fa-solid fa-lightbulb'],
                'metadata' => ['name' => '메타데이터 관리', 'link' => $basePath . 'admin/metadata.php', 'icon' => 'fa-solid fa-pen-to-square'],
                'bulk_edit' => ['name' => '일괄 수정', 'link' => $basePath . 'admin/bulk_edit.php', 'icon' => 'fa-solid fa-table-list'],
                'duplicates' => ['name' => '중복 후보 관리', 'link' => $basePath . 'admin/duplicates.php', 'icon' => 'fa-solid fa-clone'],
                'missing_files' => ['name' => '파일 누락 관리', 'link' => $basePath . 'admin/missing_files.php', 'icon' => 'fa-solid fa-file-circle-question'],
                'maintenance' => ['name' => '유지보수 도구', 'link' => $basePath . 'settings.php#maintenance-tools', 'icon' => 'fa-solid fa-screwdriver-wrench'],
                'backup' => ['name' => '백업 센터', 'link' => $basePath . 'admin/backup_center.php', 'icon' => 'fa-solid fa-database'],
            ],
        ];
        $groups['settings'] = [
            'title' => '설정',
            'items' => [
                'settings' => ['name' => '전체 설정', 'link' => $basePath . 'settings.php', 'icon' => 'fa-solid fa-gear'],
                'theme_settings' => ['name' => '테마 설정', 'link' => $basePath . 'settings.php#theme-settings', 'icon' => 'fa-solid fa-palette'],
                'player_settings' => ['name' => '플레이어 설정', 'link' => $basePath . 'settings.php#player-settings', 'icon' => 'fa-solid fa-headphones'],
                'pwa_settings' => ['name' => 'PWA/설치 설정', 'link' => $basePath . 'settings.php#pwa-settings', 'icon' => 'fa-solid fa-mobile-screen-button'],
            ],
        ];
        if (!$settings['showSettingsDetail']) {
            $groups['settings']['items'] = ['settings' => $groups['settings']['items']['settings']];
        }
    }

    if (isset($groups['admin']['items']['admin_users']) && !isset($groups['admin']['items']['api_tokens'])) {
        $adminItems = [];
        foreach ($groups['admin']['items'] as $key => $item) {
            $adminItems[$key] = $item;
            if ($key === 'admin_users') {
                $adminItems['api_tokens'] = ['name' => 'API 토큰', 'link' => $basePath . 'admin/api_tokens.php', 'icon' => 'fa-solid fa-key'];
            }
        }
        $groups['admin']['items'] = $adminItems;
    }

    if (isset($groups['admin']['items']['metadata']) && !isset($groups['admin']['items']['smart_organize'])) {
        $adminItems = [];
        foreach ($groups['admin']['items'] as $key => $item) {
            $adminItems[$key] = $item;
            if ($key === 'metadata') {
                $adminItems['smart_organize'] = ['name' => '스마트 정리', 'link' => $basePath . 'admin/smart_organize.php', 'icon' => 'fa-solid fa-broom'];
                $adminItems['tag_manage'] = ['name' => '태그 관리', 'link' => $basePath . 'admin/tag_manage.php', 'icon' => 'fa-solid fa-tags'];
                $adminItems['name_merge'] = ['name' => '표기 통합', 'link' => $basePath . 'admin/name_merge.php', 'icon' => 'fa-solid fa-code-merge'];
            }
        }
        $groups['admin']['items'] = $adminItems;
    }

    if (isset($groups['admin']['items']['metadata']) && !isset($groups['admin']['items']['metadata_quality'])) {
        $adminItems = [];
        foreach ($groups['admin']['items'] as $key => $item) {
            $adminItems[$key] = $item;
            if ($key === 'metadata') {
                $adminItems['metadata_quality'] = ['name' => '품질 관리', 'link' => $basePath . 'admin/metadata_quality.php', 'icon' => 'fa-solid fa-list-check'];
            }
        }
        $groups['admin']['items'] = $adminItems;
    }

    if (isset($groups['admin']['items']['scan'])) {
        $adminItems = [];
        foreach ($groups['admin']['items'] as $key => $item) {
            $adminItems[$key] = $item;
            if ($key === 'scan') {
                $adminItems['diagnostics'] = ['name' => '진단 센터', 'link' => $basePath . 'admin/diagnostics.php', 'icon' => 'fa-solid fa-stethoscope'];
                $adminItems['logs'] = ['name' => '오류 로그', 'link' => $basePath . 'admin/logs.php', 'icon' => 'fa-solid fa-clipboard-list'];
                $adminItems['audit_logs'] = ['name' => '감사 로그', 'link' => $basePath . 'admin/audit_logs.php', 'icon' => 'fa-solid fa-user-shield'];
                $adminItems['alerts'] = ['name' => '알림 센터', 'link' => $basePath . 'admin/alerts.php', 'icon' => 'fa-solid fa-bell'];
                $adminItems['announcements'] = ['name' => '공지 관리', 'link' => $basePath . 'admin/announcements.php', 'icon' => 'fa-solid fa-bullhorn'];
                $adminItems['patch_notes'] = ['name' => '패치노트 관리', 'link' => $basePath . 'admin/patch_notes.php', 'icon' => 'fa-regular fa-note-sticky'];
                $adminItems['service_status_admin'] = ['name' => '서비스 상태 관리', 'link' => $basePath . 'admin/service_status.php', 'icon' => 'fa-solid fa-signal'];
                $adminItems['jobs'] = ['name' => '작업 큐', 'link' => $basePath . 'admin/jobs.php', 'icon' => 'fa-solid fa-list-check'];
                $adminItems['performance'] = ['name' => '성능 점검', 'link' => $basePath . 'admin/performance.php', 'icon' => 'fa-solid fa-gauge'];
            }
        }
        $groups['admin']['items'] = $adminItems;
        if (!isset($groups['admin']['items']['admin_listening_stats'])) {
            $groups['admin']['items']['admin_listening_stats'] = ['name' => '청취 통계', 'link' => $basePath . 'admin/listening_stats.php', 'icon' => 'fa-solid fa-chart-simple'];
        }
    }

    if (isset($groups['my_music']['items']['popular']) && !isset($groups['my_music']['items']['stats'])) {
        $myMusicItems = [];
        foreach ($groups['my_music']['items'] as $key => $item) {
            $myMusicItems[$key] = $item;
            if ($key === 'popular') {
                $myMusicItems['stats'] = ['name' => '청취 리포트', 'link' => $basePath . 'stats.php', 'icon' => 'fa-solid fa-chart-line'];
            }
        }
        $groups['my_music']['items'] = $myMusicItems;
    }

    if (isset($groups['my_music']['items']['stats']) && !isset($groups['my_music']['items']['listening_notes'])) {
        $items = [];
        foreach ($groups['my_music']['items'] as $key => $item) {
            $items[$key] = $item;
            if ($key === 'stats') {
                $items['listening_notes'] = ['name' => '감상 노트', 'link' => $basePath . 'listening_notes.php', 'icon' => 'fa-regular fa-note-sticky'];
            }
        }
        $groups['my_music']['items'] = $items;
    }
    if (isset($groups['admin']['items']) && function_exists('hasPermission')) {
        $permissionByKey = [
            'admin_home' => 'admin_home',
            'admin_users' => 'admin_manage',
            'api_tokens' => 'admin_manage',
            'scan' => 'music_scan',
            'discovery' => 'admin_home',
            'diagnostics' => 'diagnostics_view',
            'logs' => 'log_view',
            'audit_logs' => 'audit_view',
            'alerts' => 'alert_view',
            'announcements' => 'announcement_manage',
            'patch_notes' => 'patch_note_manage',
            'service_status_admin' => 'service_status_manage',
            'jobs' => 'job_queue_view',
            'performance' => 'performance_view',
            'admin_listening_stats' => 'performance_view',
            'metadata' => 'metadata_view',
            'metadata_quality' => 'metadata_view',
            'metadata_requests' => 'metadata_request_manage',
            'bulk_edit' => 'metadata_bulk_edit',
            'duplicates' => 'duplicates_manage',
            'missing_files' => 'missing_files_manage',
            'smart_organize' => 'smart_organize',
            'tag_manage' => 'tag_manage',
            'name_merge' => 'name_merge',
            'maintenance' => 'maintenance_run',
            'backup' => 'backup_view',
        ];
        foreach ($groups['admin']['items'] as $key => $item) {
            $permission = $permissionByKey[$key] ?? null;
            if ($permission !== null && !hasPermission($permission)) {
                unset($groups['admin']['items'][$key]);
            }
        }
        if (empty($groups['admin']['items'])) unset($groups['admin']);
    }

    if (isset($groups['admin']['items']['admin_home'])) {
        $groups['admin'] = [
            'title' => '관리',
            'items' => [
                'admin_home' => [
                    'name' => '통합 관리자 센터',
                    'link' => $basePath . 'admin/index.php',
                    'icon' => 'fa-solid fa-gauge-high',
                ],
            ],
        ];
    }

    if (isset($groups['admin']['items']) && count($groups['admin']['items']) > 1) {
        $adminItems = $groups['admin']['items'];
        $adminGroupMap = [
            'admin' => ['title' => '관리 핵심', 'keys' => ['admin_home', 'admin_users', 'api_tokens']],
            'admin_music' => ['title' => '음악 관리', 'keys' => ['scan', 'discovery', 'metadata', 'metadata_quality', 'bulk_edit', 'duplicates', 'missing_files', 'smart_organize', 'tag_manage', 'name_merge']],
            'admin_ops' => ['title' => '운영/점검', 'keys' => ['diagnostics', 'logs', 'audit_logs', 'alerts', 'announcements', 'patch_notes', 'service_status_admin', 'jobs', 'performance', 'admin_listening_stats', 'maintenance', 'backup']],
        ];
        $splitAdminGroups = [];
        foreach ($adminGroupMap as $groupKey => $definition) {
            $items = [];
            foreach ($definition['keys'] as $itemKey) {
                if (isset($adminItems[$itemKey])) {
                    $items[$itemKey] = $adminItems[$itemKey];
                }
            }
            if (!empty($items)) {
                $splitAdminGroups[$groupKey] = [
                    'title' => $definition['title'],
                    'items' => $items,
                ];
            }
        }
        if (!empty($splitAdminGroups)) {
            $rebuiltGroups = [];
            foreach ($groups as $groupKey => $group) {
                if ($groupKey === 'admin') {
                    foreach ($splitAdminGroups as $splitKey => $splitGroup) {
                        $rebuiltGroups[$splitKey] = $splitGroup;
                    }
                    continue;
                }
                $rebuiltGroups[$groupKey] = $group;
            }
            $groups = $rebuiltGroups;
        }
    }

    return $groups;
}

function renderMenuItem($key, $item, $currentMenu) {
    $active = $currentMenu === $key;
    ?>
    <a href="<?php echo h($item['link']); ?>" class="nav-item <?php echo $active ? 'active' : ''; ?>" data-menu-key="<?php echo h($key); ?>" data-menu-label="<?php echo h($item['name']); ?>" title="<?php echo h($item['name']); ?>" aria-label="<?php echo h($item['name']); ?>"<?php echo $active ? ' aria-current="page"' : ''; ?>>
        <i class="<?php echo h($item['icon']); ?> nav-icon"></i>
        <span class="nav-label"><?php echo h($item['name']); ?></span>
    </a>
    <?php
}

function renderMenuGroup($groupKey, $group, $currentMenu, $defaultOpen) {
    $hasActive = array_key_exists($currentMenu, $group['items']);
    $openClass = ($defaultOpen || $hasActive) ? 'open' : 'collapsed';
    ?>
    <div class="menu-group <?php echo h($openClass); ?> <?php echo $hasActive ? 'has-active' : ''; ?>" data-group-key="<?php echo h($groupKey); ?>">
        <button type="button" class="menu-group-toggle" aria-expanded="<?php echo $openClass === 'open' ? 'true' : 'false'; ?>">
            <span><?php echo h($group['title']); ?></span>
            <i class="fa-solid fa-chevron-down"></i>
        </button>
        <div class="menu-group-items">
            <?php foreach ($group['items'] as $key => $item) renderMenuItem($key, $item, $currentMenu); ?>
        </div>
    </div>
    <?php
}

function renderHeader($pageTitle = 'MelonLite', $activeMenu = 'home', $options = []) {
    if (!headers_sent()) {
        header('Content-Type: text/html; charset=utf-8');
    }
    $isSubdir = strpos((string)($_SERVER['SCRIPT_NAME'] ?? ''), '/admin/') !== false;
    if (!headers_sent() && function_exists('sendSecurityHeaders')) {
        sendSecurityHeaders($isSubdir || isLoggedIn());
    }
    $basePath = $isSubdir ? '../' : './';
    $authPage = in_array(basename(currentScriptPath()), ['login.php', 'setup_admin.php'], true);
    if (!$authPage && !isLoggedIn() && function_exists('settingBool') && settingBool('require_login_for_all_pages', false)) {
        $return = safeReturnUrl(currentRelativeUrl(), 'index.php');
        header('Location: ' . $basePath . 'login.php?return=' . urlencode($return));
        exit;
    }
    $showPlayer = $options['show_player'] ?? true;
    $themeClass = function_exists('getThemeClass') ? getThemeClass() : 'theme-melon';
    $accentColor = function_exists('getSetting') ? getSetting('accent_color', '#00CD3C') : '#00CD3C';
    if (!preg_match('/^#[0-9a-fA-F]{6}$/', $accentColor)) $accentColor = '#00CD3C';
    $appName = function_exists('getSetting') ? trim((string)getSetting('pwa_app_name', '개인 음악실')) : '개인 음악실';
    if ($appName === '') $appName = '개인 음악실';

    $sidebarSettings = getSidebarSettings();
    $currentMenu = getCurrentMenuKey($activeMenu);
    $menuGroups = sidebarMenuGroups($basePath, $sidebarSettings);
    $bodyClasses = trim($themeClass . ($sidebarSettings['compactMode'] ? ' sidebar-compact' : '') . (!$showPlayer ? ' no-player-page' : '') . ($authPage ? ' auth-layout' : ''));
    $currentQuery = isset($_GET['q']) ? (string)$_GET['q'] : (isset($_GET['search']) ? (string)$_GET['search'] : '');
    $adminName = isLoggedIn() ? adminDisplayName() : '';
    ?>
    <!DOCTYPE html>
    <html lang="ko">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
        <meta name="theme-color" content="<?php echo h($accentColor); ?>">
        <meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
        <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#121212">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-title" content="<?php echo h($appName); ?>">
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="application-name" content="<?php echo h($appName); ?>">
        <title><?php echo h($pageTitle); ?> - <?php echo h($appName); ?></title>
        <link rel="manifest" href="<?php echo assetUrl($basePath, 'manifest.json'); ?>">
        <link rel="apple-touch-icon" href="<?php echo $basePath; ?>assets/img/icons/icon-192.png">
        <link rel="apple-touch-icon" sizes="192x192" href="<?php echo $basePath; ?>assets/img/icons/icon-192.png">
        <link rel="apple-touch-icon" sizes="512x512" href="<?php echo $basePath; ?>assets/img/icons/icon-512.png">
        <link rel="icon" type="image/png" sizes="192x192" href="<?php echo $basePath; ?>assets/img/icons/icon-192.png">
        <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;600;800&family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
        <link rel="stylesheet" href="<?php echo assetUrl($basePath, 'assets/css/style.css'); ?>">
        <link rel="stylesheet" href="<?php echo assetUrl($basePath, 'assets/css/floating-player.css'); ?>">
        <style>:root,body{--accent-color: <?php echo h($accentColor); ?>;--accent-hover: <?php echo h($accentColor); ?>;}</style>
        <script>
            (function() {
                const savedTheme = localStorage.getItem('melon_lite_theme');
                if (savedTheme && ['system', 'light', 'dark', 'melon', 'black'].includes(savedTheme)) {
                    document.documentElement.setAttribute('data-theme-restore', savedTheme);
                    const checkBody = setInterval(function() {
                        if (document.body) {
                            clearInterval(checkBody);
                            document.body.classList.forEach(function(cls) {
                                if (cls.startsWith('theme-')) document.body.classList.remove(cls);
                            });
                            document.body.classList.add('theme-' + savedTheme);
                        }
                    }, 5);
                    setTimeout(function() { clearInterval(checkBody); }, 3000);
                }
            })();
        </script>
    </head>
    <body class="<?php echo h($bodyClasses); ?>">
        <div class="app-container">
            <div class="sidebar-overlay" id="sidebar-overlay"></div>
            <aside class="sidebar" id="app-sidebar">
                <div class="brand">
                    <i class="fa-solid fa-compact-disc brand-icon"></i>
                    <span class="brand-name"><?php echo h($appName); ?></span>
                    <button type="button" class="sidebar-collapse-btn" id="sidebar-collapse-toggle" aria-label="사이드바 접기/펼치기" title="사이드바 접기/펼치기">
                        <i class="fa-solid fa-angles-left"></i>
                    </button>
                </div>
                <div class="sidebar-scroll">
                    <form action="<?php echo $basePath; ?>search.php" method="GET" class="sidebar-search-form">
                        <i class="fa-solid fa-magnifying-glass"></i>
                        <input type="search" name="q" value="<?php echo h($currentQuery); ?>" placeholder="음악 검색">
                        <button type="submit" title="검색"><i class="fa-solid fa-arrow-right"></i></button>
                    </form>
                    <div class="sidebar-menu-filter" role="search" aria-label="사이드바 메뉴 검색">
                        <i class="fa-solid fa-filter" aria-hidden="true"></i>
                        <input type="search" id="sidebar-menu-filter" autocomplete="off" aria-label="메뉴 찾기" placeholder="메뉴 찾기">
                        <button type="button" id="sidebar-menu-filter-clear" aria-label="메뉴 검색 지우기" title="메뉴 검색 지우기" hidden><i class="fa-solid fa-xmark" aria-hidden="true"></i></button>
                    </div>
                    <p class="sidebar-menu-empty" id="sidebar-menu-empty" hidden>찾은 메뉴가 없습니다.</p>
                    <div class="sidebar-utility-panel">
                        <div class="sidebar-panel-label">빠른 실행</div>
                        <div class="sidebar-quick-actions" aria-label="빠른 실행">
                            <a href="<?php echo $basePath; ?>recommend.php" title="추천 음악"><i class="fa-solid fa-star"></i><span>추천</span></a>
                            <a href="<?php echo $basePath; ?>auto_playlists.php" title="자동 플레이리스트"><i class="fa-solid fa-wand-magic-sparkles"></i><span>자동</span></a>
                            <a href="<?php echo $basePath; ?>playlist_create.php" title="새 플레이리스트"><i class="fa-solid fa-plus"></i><span>새 목록</span></a>
                            <?php if (isLoggedIn()): ?><a href="<?php echo $basePath; ?>admin/scan.php" title="음악 스캔"><i class="fa-solid fa-rotate"></i><span>스캔</span></a><?php endif; ?>
                        </div>
                    </div>
                    <nav class="nav-menu" aria-label="주 메뉴">
                        <div class="sidebar-single-menu">
                            <?php renderMenuItem('home', ['name' => '홈', 'link' => $basePath . 'index.php', 'icon' => 'fa-solid fa-house'], $currentMenu); ?>
                            <?php renderMenuItem('search', ['name' => '음악 검색', 'link' => $basePath . 'search.php', 'icon' => 'fa-solid fa-magnifying-glass'], $currentMenu); ?>
                        </div>
                        <?php foreach ($menuGroups as $groupKey => $group) renderMenuGroup($groupKey, $group, $currentMenu, $sidebarSettings['groupsDefaultOpen']); ?>
                        <div class="sidebar-single-menu auth-menu">
                            <?php if (isLoggedIn()): ?>
                                <?php renderMenuItem('logout', ['name' => '로그아웃', 'link' => $basePath . 'logout.php', 'icon' => 'fa-solid fa-right-from-bracket'], $currentMenu); ?>
                            <?php else: ?>
                                <?php renderMenuItem('login', ['name' => '로그인', 'link' => $basePath . 'login.php', 'icon' => 'fa-solid fa-right-to-bracket'], $currentMenu); ?>
                            <?php endif; ?>
                        </div>
                    </nav>
                </div>
                <div class="sidebar-footer">
                    <p>&copy; 2026 <?php echo h($appName); ?></p>
                    <div class="sidebar-footer-links" aria-label="서비스 정책 링크">
                        <a href="<?php echo $basePath; ?>policies.php?type=terms">이용안내</a>
                        <a href="<?php echo $basePath; ?>policies.php?type=privacy">개인정보</a>
                        <a href="<?php echo $basePath; ?>policies.php?type=copyright">저작권</a>
                    </div>
                </div>
            </aside>
            <main class="main-content">
                <header class="top-bar">
                    <button class="mobile-menu-btn" id="mobile-hamburger" title="메뉴 열기" aria-label="메뉴 열기" type="button"><i class="fas fa-bars"></i></button>
                    <div class="search-container">
                        <form action="<?php echo $basePath; ?>search.php" method="GET" class="search-form quick-search-form">
                            <i class="fas fa-search search-icon"></i>
                            <input type="search" name="q" placeholder="곡, 아티스트, 앨범, 가사 검색" value="<?php echo h($currentQuery); ?>">
                        </form>
                    </div>
                    <div class="user-profile">
                        <i class="fa-regular fa-user-circle profile-icon"></i>
                        <span><?php echo isLoggedIn() ? h($adminName) : '방문자'; ?></span>
                    </div>
                </header>
                <?php renderAnnouncementBanner($basePath); ?>
                <?php renderServiceStatusBanner($basePath); ?>
                <div class="content-body" id="app-content" data-pjax-root>
    <?php
}

function renderFooter() {
    $isSubdir = strpos((string)($_SERVER['SCRIPT_NAME'] ?? ''), '/admin/') !== false;
    $basePath = $isSubdir ? '../' : './';
    $playerSettings = [
        'defaultVolume' => settingInt('player_default_volume', 80, 0, 100) / 100,
        'resumeEnabled' => settingBool('player_resume_enabled', true),
        'autoNextEnabled' => settingBool('player_auto_next_enabled', true),
        'historyEnabled' => settingBool('player_history_enabled', true),
        'countRule' => getSetting('player_count_rule', 'start'),
        'defaultRepeat' => getSetting('player_default_repeat', 'none'),
        'defaultShuffle' => settingBool('player_default_shuffle', false),
        'mobileCompact' => settingBool('player_mobile_compact', false),
        'mediaSessionEnabled' => settingBool('media_session_enabled', true),
        'keyboardShortcutsEnabled' => settingBool('keyboard_shortcuts_enabled', true),
        'lazyLoadingEnabled' => settingBool('album_art_lazy_loading', true),
        'isLoggedIn' => isLoggedIn(),
        'guestPreviewSeconds' => 30,
    ];
    $pwaSettings = [
        'enabled' => settingBool('pwa_enabled', true),
        'showGuide' => settingBool('pwa_show_install_guide', true),
        'appName' => getSetting('pwa_app_name', '개인 음악실'),
        'audioCachingEnabled' => settingBool('pwa_audio_cache_enabled', false),
    ];
    $showPlayer = strpos((string)($_SERVER['SCRIPT_NAME'] ?? ''), 'setup_admin.php') === false;
    ?>
                </div>
            </main>
            <?php if ($pwaSettings['showGuide']): ?>
                <div class="pwa-install-banner pwa-install-modal" id="pwa-install-banner" role="dialog" aria-modal="true" aria-labelledby="pwa-install-title" hidden>
                    <section class="pwa-install-card">
                        <button type="button" class="modal-close-btn pwa-install-x" id="pwa-install-close" title="닫기" aria-label="PWA 설치 안내 닫기"><i class="fas fa-times"></i></button>
                        <div class="pwa-install-hero" aria-hidden="true">
                            <i class="fas fa-music"></i>
                        </div>
                        <div class="pwa-install-copy">
                            <span class="pwa-install-badge">설치 안내</span>
                            <strong id="pwa-install-title"><?php echo h($pwaSettings['appName']); ?> 홈 화면에 설치하기</strong>
                            <span id="pwa-install-message">홈 화면에 추가하면 더 빠르게 음악을 감상할 수 있습니다.</span>
                        </div>
                        <div class="pwa-install-steps" id="pwa-install-steps" hidden></div>
                        <div class="pwa-install-actions">
                            <button type="button" class="btn btn-primary btn-sm" id="pwa-install-btn">&#50545; &#49444;&#52824;&#54616;&#44592;</button>
                            <button type="button" class="btn btn-secondary btn-sm" id="pwa-guide-btn">&#49444;&#52824; &#48169;&#48277; &#48372;&#44592;</button>
                            <button type="button" class="btn btn-secondary btn-sm" id="pwa-install-later">&#45208;&#51473;&#50640;</button>
                            <button type="button" class="btn btn-secondary btn-sm pwa-install-never" id="pwa-install-never">&#45796;&#49884; &#48372;&#51648; &#50506;&#44592;</button>
                        </div>
                    </section>
                </div>
                <div class="pwa-guide-modal" id="pwa-guide-modal" hidden aria-hidden="true">
                    <div class="pwa-guide-backdrop" id="pwa-guide-backdrop"></div>
                    <section class="pwa-guide-card" role="dialog" aria-modal="true" aria-labelledby="pwa-guide-title">
                        <button type="button" class="modal-close-btn" id="pwa-guide-close" title="닫기" aria-label="PWA 안내 닫기"><i class="fas fa-times"></i></button>
                        <h2 id="pwa-guide-title">설치 방법</h2>
                        <div class="pwa-guide-body" id="pwa-guide-body"></div>
                    </section>
                </div>
            <?php endif; ?>
            <?php if ($showPlayer): ?>
                <div class="player-bar">
                    <div class="player-song-info">
                        <div class="player-album-art" id="player-art-container">
                            <img src="<?php echo $basePath; ?>assets/img/default-cover.svg" alt="Cover" id="player-album-image" width="56" height="56">
                        </div>
                        <div class="player-song-details">
                            <div class="player-song-title" id="player-title">재생 중인 곡이 없습니다.</div>
                            <div class="player-song-artist" id="player-artist">-</div>
                        </div>
                    </div>
                    <div class="player-controls">
                        <div class="control-buttons">
                            <button class="control-btn" id="btn-shuffle" title="셔플" aria-label="셔플 켜기 또는 끄기"><i class="fas fa-random"></i></button>
                            <button class="control-btn" id="btn-prev" title="이전 곡" aria-label="이전 곡"><i class="fas fa-step-backward"></i></button>
                            <button class="control-btn play-btn" id="btn-play-pause" title="재생/일시정지" aria-label="재생 또는 일시정지"><i class="fas fa-play"></i></button>
                            <button class="control-btn" id="btn-next" title="다음 곡" aria-label="다음 곡"><i class="fas fa-step-forward"></i></button>
                            <button class="control-btn" id="btn-repeat" title="반복" aria-label="반복 모드 변경"><i class="fas fa-redo"></i></button>
                        </div>
                        <div class="progress-container">
                            <span class="time-label" id="time-current">00:00</span>
                            <div class="progress-bar-wrapper" id="progress-wrapper"><div class="progress-bar" id="progress-bar"><div class="progress-handle"></div></div></div>
                            <span class="time-label" id="time-total">00:00</span>
                        </div>
                    </div>
                    <div class="player-utils">
                        <button class="control-btn" id="btn-queue" title="재생 대기열" aria-label="재생 대기열 열기"><i class="fas fa-list"></i></button>
                        <button class="control-btn" id="btn-expand-player" title="전체화면 플레이어" aria-label="전체화면 플레이어 열기"><i class="fas fa-expand"></i></button>
                        <i class="fas fa-volume-up volume-icon" id="btn-mute"></i>
                        <div class="volume-slider-wrapper" id="volume-wrapper"><div class="volume-slider" id="volume-bar"></div></div>
                    </div>
                </div>
                <audio id="audio-player" style="display:none;"></audio>
                <div class="expand-player-overlay" id="expand-player-overlay" style="display: none;">
                    <div class="expand-player-container" role="dialog" aria-modal="true" aria-label="확장 플레이어">
                        <button type="button" class="expand-close" id="expand-player-close" title="닫기" aria-label="전체화면 플레이어 닫기"><i class="fas fa-times"></i></button>
                        <div class="expand-player-main">
                            <section class="expand-art-section">
                                <img src="<?php echo $basePath; ?>assets/img/default-cover.svg" alt="Cover" class="expand-album-art" id="expand-album-image">
                                <div class="expand-song-meta">
                                    <h2 id="expand-player-title">재생 중인 곡이 없습니다.</h2>
                                    <p id="expand-player-artist">-</p>
                                    <p id="expand-player-album"></p>
                                    <p class="mini-lyrics-line" id="mini-lyrics-line"></p>
                                </div>
                            </section>
                            <section class="expand-tab-section">
                                <div class="expand-tab-headers">
                                    <button type="button" class="tab-header-btn active" id="tab-btn-queue" aria-label="대기열 탭">대기열</button>
                                    <button type="button" class="tab-header-btn" id="tab-btn-lyrics" aria-label="가사 탭">가사</button>
                                </div>
                                <div class="expand-tab-content" id="expand-tab-queue-content">
                                    <div class="queue-body" id="expand-queue-list"></div>
                                </div>
                                <div class="expand-tab-content" id="expand-tab-lyrics-content" style="display: none;">
                                    <button type="button" class="btn btn-secondary btn-sm" id="expand-btn-lyrics-toggle">가사 보기</button>
                                    <div class="expand-lyrics-full-text" id="expand-lyrics-full-text"><p class="queue-empty-text">곡을 먼저 재생해 주세요.</p></div>
                                </div>
                            </section>
                        </div>
                        <div class="expand-player-controls">
                            <div class="progress-container">
                                <span class="time-label" id="expand-time-current">00:00</span>
                                <div class="progress-bar-wrapper" id="expand-progress-wrapper"><div class="progress-bar" id="expand-progress-bar"><div class="progress-handle"></div></div></div>
                                <span class="time-label" id="expand-time-total">00:00</span>
                            </div>
                            <div class="expand-control-buttons-row">
                                <button class="control-btn" id="expand-btn-shuffle" title="셔플" aria-label="셔플 켜기 또는 끄기"><i class="fas fa-random"></i></button>
                                <button class="control-btn" id="expand-btn-prev" title="이전 곡" aria-label="이전 곡"><i class="fas fa-step-backward"></i></button>
                                <button class="control-btn play-btn" id="expand-btn-play-pause" title="재생/일시정지" aria-label="재생 또는 일시정지"><i class="fas fa-play"></i></button>
                                <button class="control-btn" id="expand-btn-next" title="다음 곡" aria-label="다음 곡"><i class="fas fa-step-forward"></i></button>
                                <button class="control-btn" id="expand-btn-repeat" title="반복" aria-label="반복 모드 변경"><i class="fas fa-redo"></i></button>
                            </div>
                            <div class="expand-secondary-actions">
                                <button class="btn btn-secondary btn-sm" id="expand-btn-like" type="button"><i class="fas fa-heart"></i> 좋아요</button>
                                <button class="btn btn-secondary btn-sm" id="expand-btn-add-playlist" type="button"><i class="fas fa-plus"></i> 플레이리스트</button>
                                <a class="btn btn-secondary btn-sm" id="expand-btn-song-detail" href="<?php echo $basePath; ?>index.php"><i class="fas fa-info-circle"></i> 상세</a>
                            </div>
                        </div>
                    </div>
                </div>
            <?php endif; ?>
        </div>

        <button type="button" class="scroll-top-btn" id="scroll-top-btn" data-no-pjax aria-label="페이지 상단으로 이동" title="상단으로 이동" hidden>
            <i class="fa-solid fa-arrow-up" aria-hidden="true"></i>
        </button>

        <button type="button" class="feedback-fab" id="feedback-open-btn" data-no-pjax aria-label="피드백 또는 오류 신고 열기">
            <i class="fa-solid fa-comment-dots" aria-hidden="true"></i>
            <span>피드백</span>
        </button>
        <div id="feedback-modal" class="modal-overlay feedback-modal-overlay" hidden aria-hidden="true">
            <div class="modal-box feedback-modal-box" role="dialog" aria-modal="true" aria-labelledby="feedback-modal-title">
                <div class="modal-header">
                    <h3 id="feedback-modal-title"><i class="fa-solid fa-comment-dots"></i> 피드백 / 오류 신고</h3>
                    <button class="modal-close-btn" id="feedback-modal-close" type="button" aria-label="피드백 창 닫기"><i class="fas fa-times"></i></button>
                </div>
                <form class="modal-content feedback-form" id="feedback-form" autocomplete="off">
                    <input type="hidden" name="csrf_token" value="<?php echo h(getCsrfToken()); ?>">
                    <input type="hidden" name="page_url" id="feedback-page-url" value="">
                    <input type="hidden" name="page_title" id="feedback-page-title" value="">
                    <input type="hidden" name="song_id" id="feedback-song-id" value="">
                    <label class="feedback-field">
                        <span>유형</span>
                        <select name="feedback_type" id="feedback-type" required>
                            <option value="bug">오류 신고</option>
                            <option value="usability">사용 불편</option>
                            <option value="feature">기능 제안</option>
                            <option value="other">기타</option>
                        </select>
                    </label>
                    <label class="feedback-field">
                        <span>제목</span>
                        <input type="text" name="title" id="feedback-title" maxlength="160" minlength="3" required placeholder="예: 모바일에서 재생 버튼이 눌리지 않음">
                    </label>
                    <label class="feedback-field">
                        <span>내용</span>
                        <textarea name="body" id="feedback-body" rows="6" maxlength="3000" minlength="8" required placeholder="어떤 화면에서 어떤 문제가 있었는지 적어주세요."></textarea>
                    </label>
                    <p class="feedback-context" id="feedback-context-text">현재 페이지 정보만 함께 전달합니다. 파일 경로나 브라우저 저장값은 수집하지 않습니다.</p>
                    <p class="download-status-text" id="feedback-status-text" role="status" aria-live="polite"></p>
                    <div class="modal-actions feedback-modal-actions">
                        <button type="button" class="btn btn-secondary" id="feedback-cancel-btn">취소</button>
                        <button type="submit" class="btn btn-primary" id="feedback-submit-btn"><i class="fa-solid fa-paper-plane"></i> 보내기</button>
                    </div>
                </form>
            </div>
        </div>

        <div id="metadata-request-modal" class="modal-overlay feedback-modal-overlay metadata-request-modal-overlay" hidden aria-hidden="true">
            <div class="modal-box feedback-modal-box metadata-request-modal-box" role="dialog" aria-modal="true" aria-labelledby="metadata-request-modal-title">
                <div class="modal-header">
                    <h3 id="metadata-request-modal-title"><i class="fa-solid fa-pen-to-square"></i> 음악 정보 수정 요청</h3>
                    <button class="modal-close-btn" id="metadata-request-modal-close" type="button" aria-label="수정 요청 창 닫기"><i class="fas fa-times"></i></button>
                </div>
                <form class="modal-content feedback-form metadata-request-form" id="metadata-request-form" autocomplete="off">
                    <input type="hidden" name="csrf_token" value="<?php echo h(getCsrfToken()); ?>">
                    <input type="hidden" name="song_id" id="metadata-request-song-id" value="">
                    <input type="hidden" name="page_url" id="metadata-request-page-url" value="">
                    <p class="feedback-context metadata-request-context">
                        <strong>요청 대상</strong>
                        <span id="metadata-request-current"></span>
                    </p>
                    <label class="feedback-field">
                        <span>수정 유형</span>
                        <select name="request_type" id="metadata-request-type" required>
                            <option value="title">제목</option>
                            <option value="artist">아티스트</option>
                            <option value="album">앨범</option>
                            <option value="genre">장르</option>
                            <option value="year">발매연도</option>
                            <option value="lyrics">가사</option>
                            <option value="cover">앨범아트</option>
                            <option value="other">기타</option>
                        </select>
                    </label>
                    <label class="feedback-field">
                        <span>제안 내용</span>
                        <textarea name="suggested_value" id="metadata-request-suggested" rows="5" maxlength="3000" placeholder="올바른 제목, 아티스트, 앨범명, 가사 일부 등 제안할 내용을 입력하세요."></textarea>
                    </label>
                    <label class="feedback-field">
                        <span>요청 사유</span>
                        <textarea name="reason" id="metadata-request-reason" rows="4" maxlength="1200" placeholder="왜 수정이 필요한지 간단히 적어 주세요."></textarea>
                    </label>
                    <p class="feedback-context">곡 ID와 현재 표시 정보만 전달됩니다. 실제 음악 파일 경로나 서버 내부 경로는 수집하지 않습니다.</p>
                    <p class="download-status-text" id="metadata-request-status-text" role="status" aria-live="polite"></p>
                    <div class="modal-actions feedback-modal-actions">
                        <button type="button" class="btn btn-secondary" id="metadata-request-cancel-btn">취소</button>
                        <button type="submit" class="btn btn-primary" id="metadata-request-submit-btn"><i class="fa-solid fa-paper-plane"></i> 요청 보내기</button>
                    </div>
                </form>
            </div>
        </div>

        <div id="playlist-modal" class="modal-overlay" style="display: none;">
            <div class="modal-box">
                <div class="modal-header"><h3>플레이리스트에 추가</h3><button class="modal-close-btn" id="modal-close" type="button" aria-label="닫기"><i class="fas fa-times"></i></button></div>
                <div class="modal-content"><input type="hidden" id="modal-song-id" value=""><div class="playlist-selection-list" id="modal-playlist-options"><p class="loading-text">플레이리스트 목록을 불러오는 중...</p></div></div>
            </div>
        </div>
        <div id="download-modal" class="modal-overlay download-modal-overlay" hidden>
            <div class="modal-box download-modal-box" role="dialog" aria-modal="true" aria-labelledby="download-modal-title">
                <div class="modal-header">
                    <h3 id="download-modal-title"><i class="fas fa-download"></i> 음원 다운로드</h3>
                    <button class="modal-close-btn" id="download-modal-close" type="button" aria-label="다운로드 창 닫기"><i class="fas fa-times"></i></button>
                </div>
                <div class="modal-content download-modal-content">
                    <div class="download-track-summary">
                        <div class="download-track-title" id="download-track-title">선택한 곡</div>
                        <div class="download-track-meta" id="download-track-meta">파일 정보를 불러오는 중입니다.</div>
                    </div>
                    <label class="download-field-label" for="download-filename-input">파일 이름</label>
                    <input type="text" id="download-filename-input" class="download-filename-input" autocomplete="off" spellcheck="false">
                    <div class="download-location-row">
                        <div>
                            <div class="download-field-label">저장 위치</div>
                            <p class="download-location-text" id="download-location-text">브라우저 지원 여부를 확인하는 중입니다.</p>
                        </div>
                        <button type="button" class="btn btn-secondary btn-sm" id="download-pick-location-btn">
                            <i class="fas fa-folder-open"></i> 위치 선택
                        </button>
                    </div>
                    <div class="download-progress-wrap" id="download-progress-wrap" hidden>
                        <div class="download-progress-meta">
                            <span id="download-progress-label">대기 중</span>
                            <span id="download-progress-percent">0%</span>
                        </div>
                        <div class="download-progress-track">
                            <div class="download-progress-bar" id="download-progress-bar"></div>
                        </div>
                    </div>
                    <p class="download-status-text" id="download-status-text"></p>
                </div>
                <div class="modal-actions download-modal-actions">
                    <button type="button" class="btn btn-secondary" id="download-cancel-btn">취소</button>
                    <button type="button" class="btn btn-primary" id="download-start-btn">
                        <i class="fas fa-download"></i> 저장
                    </button>
                </div>
            </div>
        </div>
        <div class="queue-panel" id="queue-panel" style="display: none;">
            <div class="queue-header"><h3>재생 대기열(<span id="queue-count">0</span>)</h3><div class="queue-header-actions"><button type="button" class="btn btn-secondary btn-sm" id="btn-clear-queue"><i class="fas fa-trash-alt"></i> 비우기</button><button type="button" class="close-btn" id="queue-close" aria-label="닫기"><i class="fas fa-times"></i></button></div></div>
            <div class="queue-body" id="queue-list-container"></div>
        </div>
        <script>
            window.APP_BASE_PATH = "<?php echo $basePath; ?>";
            window.APP_ROOT_PATH = "<?php echo h(appRootUrlPath()); ?>";
            window.APP_BASE_PATH = window.APP_ROOT_PATH || window.APP_BASE_PATH;
            window.APP_ASSET_VERSION = "<?php echo h(appAssetVersion()); ?>";
            window.APP_COVER_REVISION = "<?php echo h(function_exists('coverGlobalRevision') ? coverGlobalRevision() : ''); ?>";
            window.APP_PLAYER_SETTINGS = <?php echo json_encode($playerSettings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); ?>;
            window.APP_PWA_SETTINGS = <?php echo json_encode($pwaSettings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); ?>;
            window.APP_SIDEBAR_SETTINGS = <?php echo json_encode(getSidebarSettings(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); ?>;
            window.APP_CSRF_TOKEN = "<?php echo h(getCsrfToken()); ?>";
            (function() {
                const serverTheme = "<?php echo getSetting('theme_mode', 'melon'); ?>";
                let preferredTheme = '';
                try {
                    const raw = localStorage.getItem('melon_lite_user_preferences_v1');
                    const prefs = raw ? JSON.parse(raw) : null;
                    const mode = prefs && ['system', 'light', 'dark'].includes(prefs.themeMode) ? prefs.themeMode : '';
                    if (mode === 'system') {
                        preferredTheme = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
                    } else if (mode) {
                        preferredTheme = mode;
                    }
                } catch (err) {
                    preferredTheme = '';
                }
                localStorage.setItem('melon_lite_theme', preferredTheme || serverTheme);
            })();
            document.addEventListener('DOMContentLoaded', function () {
                document.querySelectorAll('form[method="POST"], form[method="post"]').forEach(function (form) {
                    if (!form.querySelector('input[name="csrf_token"]')) {
                        const input = document.createElement('input');
                        input.type = 'hidden';
                        input.name = 'csrf_token';
                        input.value = window.APP_CSRF_TOKEN || '';
                        form.appendChild(input);
                    }
                });
            });
        </script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/api-client.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/sidebar.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/playlist.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/tags.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/notes.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/search.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/player.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/download.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/announcements.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/feedback.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/metadata-request.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/user-settings.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/home-customizer.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/onboarding.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/scroll-top.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/accessibility.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/app-navigation.js'); ?>"></script>
        <script charset="UTF-8" src="<?php echo assetUrl($basePath, 'assets/js/pwa.js'); ?>"></script>
    </body>
    </html>
    <?php
}
