<?php
$pageTitle = '婚礼司仪数字人';
require_once 'header.php';
?>

<main class="site-main">
    <section class="page-banner dh-banner">
        <div class="container">
            <h1>🤵 婚礼司仪数字人</h1>
            <p>AI主持人「喜乐」—— 专业、温暖、富有感染力的婚礼主持
                <a href="digital_human_screen.php" target="_blank" style="display:inline-block;margin-left:16px;padding:6px 18px;background:rgba(212,168,83,0.2);border:1px solid rgba(212,168,83,0.5);border-radius:20px;color:#d4a853;font-size:13px;text-decoration:none;vertical-align:middle;">📺 大屏展示模式</a>
            </p>
        </div>
    </section>

    <section class="dh-section">
        <div class="container">
            <!-- 主视频播放器区域 -->
            <div class="video-center-stage">
                <div class="main-video-wrapper" id="mainVideoWrapper">
                    <video id="mainVideo" preload="metadata" muted playsinline webkit-playsinline x5-playsinline x5-video-player-type="h5" x5-video-player-fullscreen="false">
                        <source id="mainVideoSource" type="video/mp4">
                    </video>
                    <div class="video-overlay" id="videoOverlay">
                        <div class="overlay-top">
                            <div class="current-phase-badge" id="currentPhaseBadge">
                                <span class="phase-icon">🎊</span>
                                <span class="phase-text">开场白</span>
                            </div>
                            <div class="emotion-indicator-mini" id="emotionIndicatorMini">
                                <span class="emotion-dot"></span>
                                <span class="emotion-text">温暖</span>
                            </div>
                        </div>
                        <div class="overlay-center" id="overlayCenter">
                            <div class="welcome-state">
                                <div class="avatar-mini" id="hostAvatarMini">
                                    <div class="mini-face">
                                        <div class="mini-eyes">
                                            <span class="mini-eye"></span>
                                            <span class="mini-eye"></span>
                                        </div>
                                        <div class="mini-mouth" id="miniMouth"></div>
                                    </div>
                                </div>
                                <p class="welcome-text" id="welcomeText">点击左侧时间轴选择婚礼环节</p>
                                <p class="welcome-sub" id="welcomeSub">或从视频库选择视频播放</p>
                            </div>
                        </div>
                    </div>
                    <!-- 视频控制栏 -->
                    <div class="video-controls-bar">
                        <button class="ctrl-btn ctrl-btn-pause" id="btnMainPause" title="暂停"><i class="fas fa-pause"></i></button>
                        <button class="ctrl-btn ctrl-btn-stop" id="btnMainStop" title="停止"><i class="fas fa-stop"></i></button>
                        <div class="progress-track" id="progressTrack">
                            <div class="phase-markers" id="phaseMarkers"></div>
                            <div class="progress-filled" id="progressFilled"></div>
                            <div class="progress-handle" id="progressHandle"></div>
                        </div>
                        <div class="time-display" id="timeDisplay">00:00 / 00:00</div>
                        <!-- 音量控制组件 -->
                        <div class="volume-control" id="volumeControl">
                            <button class="ctrl-btn ctrl-btn-volume" id="btnMute" title="静音"><i class="fas fa-volume-up" id="volumeIcon"></i></button>
                            <div class="volume-slider-wrapper" id="volumeSliderWrapper">
                                <div class="volume-slider-track" id="volumeSliderTrack">
                                    <div class="volume-slider-filled" id="volumeSliderFilled"></div>
                                    <div class="volume-slider-handle" id="volumeSliderHandle"></div>
                                </div>
                            </div>
                            <div class="volume-indicator" id="volumeIndicator">
                                <div class="vol-bar vol-bar-1"></div>
                                <div class="vol-bar vol-bar-2"></div>
                                <div class="vol-bar vol-bar-3"></div>
                                <div class="vol-bar vol-bar-4"></div>
                                <div class="vol-bar vol-bar-5"></div>
                            </div>
                            <span class="volume-percent" id="volumePercent">100%</span>
                        </div>
                        <button class="ctrl-btn" id="btnFullscreen" title="全屏"><i class="fas fa-expand"></i></button>
                    </div>
                </div>
            </div>

            <!-- 综合控制面板 -->
            <div class="control-dashboard">
                <!-- 左侧：环节时间轴 + 视频库 -->
                <div class="dashboard-left">
                    <!-- 环节时间轴 -->
                    <div class="panel-card timeline-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-stream"></i> 婚礼环节时间轴</h3>
                            <span class="panel-badge" id="timelineBadge">8个环节</span>
                        </div>
                        <div class="timeline-track" id="timelineTrack">
                            <div class="timeline-progress" id="timelineProgress"></div>
                            <div class="timeline-phases" id="timelinePhases"></div>
                        </div>
                    </div>

                    <!-- 环节视频窗口矩阵 -->
                    <div class="panel-card phase-videos-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-th-large"></i> 环节视频窗口</h3>
                            <span class="panel-badge" id="phaseVideosBadge">8路视频</span>
                        </div>
                        <div class="phase-videos-grid" id="phaseVideosGrid"></div>
                    </div>

                    <!-- 时间线控制面板 -->
                    <div class="panel-card timeline-ctrl-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-clock"></i> 时间线控制</h3>
                            <div style="display:flex;gap:6px;align-items:center;">
                                <span class="sync-status-dot" id="syncStatusDot" title="同步状态"></span>
                                <span class="panel-badge" id="timelineCtrlBadge">已停止</span>
                            </div>
                        </div>
                        <div class="timeline-ctrl-body">
                            <div class="timeline-time-row">
                                <span class="tl-label">已运行</span>
                                <span class="tl-value" id="tlElapsed">00:00:00</span>
                                <span class="tl-label">当前环节</span>
                                <span class="tl-value" id="tlCurrentPhase">--</span>
                                <span class="tl-label">同步偏差</span>
                                <span class="tl-value" id="tlSyncDrift">0.0s</span>
                            </div>
                            <div class="timeline-ctrl-btns">
                                <button class="tl-ctrl-btn tl-start" id="btnTimelineStart" title="启动时间线"><i class="fas fa-play"></i> 启动</button>
                                <button class="tl-ctrl-btn tl-pause" id="btnTimelinePause" title="暂停时间线" disabled><i class="fas fa-pause"></i> 暂停</button>
                                <button class="tl-ctrl-btn tl-stop" id="btnTimelineStop" title="停止时间线" disabled><i class="fas fa-stop"></i> 停止</button>
                                <button class="tl-ctrl-btn tl-calibrate" id="btnTimelineCalibrate" title="手动校准"><i class="fas fa-crosshairs"></i> 校准</button>
                            </div>
                        </div>
                    </div>

                    <!-- 视频库 -->
                    <div class="panel-card video-library-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-film"></i> 视频库</h3>
                            <div class="library-tabs">
                            <button class="lib-tab active" data-lib="opening">🎬 片头</button>
                            <button class="lib-tab" data-lib="host">🎤 主持</button>
                            <button class="lib-tab" data-lib="ending">🎞️ 片尾</button>
                        </div>
                        </div>
                        <div class="library-content" id="libContentOpening">
                            <div class="upload-row">
                                <input type="file" id="fileInputOpening" accept="video/*" style="display:none;">
                                <button class="upload-btn-small" id="btnUploadOpening"><i class="fas fa-cloud-upload-alt"></i> 上传片头视频</button>
                            </div>
                            <div class="video-grid" id="openingVideoList"></div>
                        </div>
                        <div class="library-content" id="libContentHost" style="display:none;">
                            <div class="upload-row">
                                <input type="file" id="fileInputHost" accept="video/*" style="display:none;">
                                <button class="upload-btn-small" id="btnUploadHost"><i class="fas fa-cloud-upload-alt"></i> 上传主持视频</button>
                            </div>
                            <div class="video-grid" id="hostVideoList"></div>
                        </div>
                        <div class="library-content" id="libContentEnding" style="display:none;">
                            <div class="upload-row">
                                <input type="file" id="fileInputEnding" accept="video/*" style="display:none;">
                                <button class="upload-btn-small" id="btnUploadEnding"><i class="fas fa-cloud-upload-alt"></i> 上传片尾视频</button>
                            </div>
                            <div class="video-grid" id="endingVideoList"></div>
                            <div class="ending-config-panel" id="endingConfigPanel">
                                <h4><i class="fas fa-cog"></i> 片尾配置</h4>
                                <div class="config-row">
                                    <label>结束语</label>
                                    <textarea id="endingText" rows="3" placeholder="请输入结束语...">感谢各位来宾的光临，祝新人新婚快乐，永结同心！</textarea>
                                </div>
                                <div class="config-row">
                                    <label>制作单位</label>
                                    <input type="text" id="endingStudio" value="礼典世家婚礼策划">
                                </div>
                                <div class="config-row">
                                    <label>日期</label>
                                    <input type="date" id="endingDate">
                                </div>
                                <div class="config-row">
                                    <label>动画效果</label>
                                    <select id="endingAnimation">
                                        <option value="fade">淡入淡出</option>
                                        <option value="slide">滑动进入</option>
                                        <option value="scale">缩放出现</option>
                                        <option value="particle">粒子特效</option>
                                    </select>
                                </div>
                                <div class="config-row">
                                    <label>显示时长(秒)</label>
                                    <input type="number" id="endingDuration" value="30" min="10" max="120">
                                </div>
                                <div class="config-row">
                                    <label>背景音乐</label>
                                    <input type="file" id="endingBgMusic" accept="audio/*" style="display:none;">
                                    <button class="upload-btn-small" id="btnUploadBgMusic"><i class="fas fa-music"></i> 上传背景音乐</button>
                                    <span id="bgMusicName" style="font-size:12px;color:#999;margin-left:8px;"></span>
                                </div>
                                <div class="config-row">
                                    <label>自动触发</label>
                                    <label class="toggle-switch">
                                        <input type="checkbox" id="autoEnding" checked>
                                        <span class="toggle-slider"></span>
                                    </label>
                                    <span style="font-size:12px;color:#999;margin-left:8px;">最后环节结束时自动播放</span>
                                </div>
                                <div class="config-actions">
                                    <button class="save-btn" id="btnSaveEndingConfig"><i class="fas fa-save"></i> 保存配置</button>
                                    <button class="preview-btn" id="btnPreviewEnding"><i class="fas fa-play"></i> 预览片尾</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- 右侧：视频信息 + 快捷操作 -->
                <div class="dashboard-right">
                    <!-- 当前视频信息 -->
                    <div class="panel-card info-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-info-circle"></i> 当前环节</h3>
                        </div>
                        <div class="info-body" id="infoBody">
                            <div class="info-placeholder">
                                <i class="fas fa-hand-pointer"></i>
                                <p>点击左侧时间轴选择婚礼环节</p>
                            </div>
                        </div>
                    </div>

                    <!-- 快捷操作 -->
                    <div class="panel-card quick-actions-panel">
                        <div class="panel-header">
                            <h3><i class="fas fa-bolt"></i> 快捷操作</h3>
                        </div>
                        <div class="actions-body">
                            <button class="action-btn primary" id="btnSyncToScreen">
                                <i class="fas fa-desktop"></i> 同步到大屏
                            </button>
                            <button class="action-btn" id="btnClearSession">
                                <i class="fas fa-redo"></i> 重置会话
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
</main>

<style>
/* ===== 基础布局 ===== */
.dh-banner { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); color: #fff; }
.dh-section { padding: 24px 0 60px; }

/* ===== 视频中心舞台 ===== */
.video-center-stage { margin-bottom: 24px; }
.main-video-wrapper {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #0a0a1a;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 8px 32px rgba(0,0,0,0.3);
}
.main-video-wrapper video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* 视频遮罩层 */
.video-overlay {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    pointer-events: none;
    z-index: 2;
}
.overlay-top {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 16px 20px;
}
.current-phase-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 16px;
    background: rgba(212,168,83,0.9);
    color: #fff;
    border-radius: 20px;
    font-size: 14px;
    font-weight: 500;
    backdrop-filter: blur(4px);
}
.emotion-indicator-mini {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 12px;
    background: rgba(0,0,0,0.5);
    border-radius: 20px;
    font-size: 12px;
    color: #fff;
    backdrop-filter: blur(4px);
}
.emotion-indicator-mini .emotion-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #27ae60;
}

/* 欢迎状态 */
.overlay-center {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    transition: opacity 0.3s;
}
.welcome-state {
    text-align: center;
    color: #fff;
}
.avatar-mini {
    width: 80px; height: 100px;
    margin: 0 auto 16px;
    position: relative;
}
.mini-face {
    width: 50px; height: 50px;
    background: #ffdbac;
    border-radius: 50%;
    margin: 0 auto;
    position: relative;
}
.mini-eyes {
    display: flex;
    justify-content: center;
    gap: 12px;
    padding-top: 18px;
}
.mini-eye {
    width: 5px; height: 5px;
    background: #333;
    border-radius: 50%;
    display: block;
}
.mini-mouth {
    width: 14px; height: 7px;
    border-bottom: 2px solid #c0392b;
    border-radius: 0 0 50% 50%;
    margin: 8px auto 0;
}
.avatar-mini.speaking .mini-mouth {
    animation: miniSpeak 0.3s infinite alternate;
}
@keyframes miniSpeak {
    0% { height: 4px; }
    100% { height: 10px; }
}
.welcome-text { font-size: 18px; font-weight: 500; margin-bottom: 6px; opacity: 0.9; }
.welcome-sub { font-size: 13px; opacity: 0.6; }

/* 视频控制栏 */
.video-controls-bar {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    background: linear-gradient(transparent, rgba(0,0,0,0.85));
    z-index: 3;
    pointer-events: auto;
}
.ctrl-btn {
    width: 36px; height: 36px;
    border: none;
    background: rgba(255,255,255,0.15);
    color: #fff;
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
}
.ctrl-btn:hover { background: rgba(212,168,83,0.8); transform: scale(1.1); }
.ctrl-btn-pause {
    background: rgba(52,152,219,0.25);
    border: 1px solid rgba(52,152,219,0.4);
}
.ctrl-btn-pause:hover {
    background: rgba(52,152,219,0.8) !important;
    border-color: rgba(52,152,219,0.9);
}
.ctrl-btn-pause.is-resume {
    background: rgba(39,174,96,0.25);
    border: 1px solid rgba(39,174,96,0.4);
}
.ctrl-btn-pause.is-resume:hover {
    background: rgba(39,174,96,0.8) !important;
    border-color: rgba(39,174,96,0.9);
}
.ctrl-btn-stop {
    background: rgba(231,76,60,0.25);
    border: 1px solid rgba(231,76,60,0.4);
}
.ctrl-btn-stop:hover {
    background: rgba(231,76,60,0.8) !important;
    border-color: rgba(231,76,60,0.9);
}
.progress-track {
    flex: 1;
    position: relative;
    height: 6px;
    background: rgba(255,255,255,0.2);
    border-radius: 3px;
    cursor: pointer;
}
.phase-markers {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    pointer-events: none;
}
.phase-marker {
    position: absolute;
    top: -4px;
    width: 3px;
    height: 14px;
    background: #d4a853;
    border-radius: 2px;
    transform: translateX(-50%);
}
.phase-marker::after {
    content: attr(data-name);
    position: absolute;
    bottom: 18px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 10px;
    color: #d4a853;
    white-space: nowrap;
    background: rgba(0,0,0,0.6);
    padding: 2px 6px;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.2s;
}
.progress-track:hover .phase-marker::after { opacity: 1; }
.progress-filled {
    position: absolute;
    top: 0; left: 0;
    height: 100%;
    background: #d4a853;
    border-radius: 3px;
    transition: width 0.1s;
}
.progress-handle {
    position: absolute;
    top: 50%;
    width: 14px; height: 14px;
    background: #d4a853;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    opacity: 0;
    transition: opacity 0.2s;
}
.progress-track:hover .progress-handle { opacity: 1; }
.time-display {
    color: #fff;
    font-size: 12px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    min-width: 90px;
    text-align: center;
}

/* ===== 音量控制组件 ===== */
.volume-control {
    display: flex;
    align-items: center;
    gap: 6px;
    position: relative;
}
.ctrl-btn-volume {
    background: rgba(255,255,255,0.15) !important;
    border: 1px solid rgba(255,255,255,0.2) !important;
}
.ctrl-btn-volume:hover {
    background: rgba(212,168,83,0.8) !important;
}
.ctrl-btn-volume.is-muted {
    background: rgba(231,76,60,0.25) !important;
    border: 1px solid rgba(231,76,60,0.4) !important;
}
.ctrl-btn-volume.is-muted:hover {
    background: rgba(231,76,60,0.8) !important;
}
.volume-slider-wrapper {
    width: 80px;
    height: 24px;
    display: flex;
    align-items: center;
    cursor: pointer;
    position: relative;
}
.volume-slider-track {
    width: 100%;
    height: 4px;
    background: rgba(255,255,255,0.2);
    border-radius: 2px;
    position: relative;
}
.volume-slider-filled {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    background: #d4a853;
    border-radius: 2px;
    transition: width 0.05s;
}
.volume-slider-handle {
    position: absolute;
    top: 50%;
    width: 12px;
    height: 12px;
    background: #d4a853;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    opacity: 0;
    transition: opacity 0.2s;
    box-shadow: 0 0 4px rgba(0,0,0,0.3);
}
.volume-slider-wrapper:hover .volume-slider-handle { opacity: 1; }
.volume-indicator {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 16px;
}
.vol-bar {
    width: 3px;
    background: rgba(255,255,255,0.2);
    border-radius: 1px;
    transition: all 0.15s;
}
.vol-bar-1 { height: 4px; }
.vol-bar-2 { height: 7px; }
.vol-bar-3 { height: 10px; }
.vol-bar-4 { height: 13px; }
.vol-bar-5 { height: 16px; }
.vol-bar.active { background: #d4a853; }
.vol-bar.muted { background: rgba(231,76,60,0.5); }
.volume-percent {
    color: rgba(255,255,255,0.7);
    font-size: 11px;
    min-width: 32px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* ===== 环节视频窗口矩阵 ===== */
.phase-videos-panel { margin-bottom: 20px; }
.phase-videos-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    padding: 14px 18px;
}
.phase-video-window {
    position: relative;
    aspect-ratio: 16 / 9;
    background: #0a0a1a;
    border-radius: 8px;
    overflow: hidden;
    cursor: pointer;
    border: 2px solid transparent;
    transition: all 0.3s ease;
}
.phase-video-window:hover {
    border-color: rgba(212,168,83,0.5);
    transform: scale(1.02);
}
.phase-video-window.active {
    border-color: #d4a853;
    box-shadow: 0 0 12px rgba(212,168,83,0.4);
}
.phase-video-window.completed {
    border-color: #27ae60;
}
.phase-video-window video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.phase-video-window .window-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(transparent 40%, rgba(0,0,0,0.8));
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 6px 8px;
    transition: background 0.3s;
}
.phase-video-window.active .window-overlay {
    background: linear-gradient(transparent 30%, rgba(212,168,83,0.3));
}
.phase-video-window .window-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: #fff;
    font-weight: 500;
}
.phase-video-window .window-icon {
    font-size: 13px;
}
.phase-video-window .window-status {
    position: absolute;
    top: 4px;
    right: 6px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgba(255,255,255,0.3);
    transition: all 0.3s;
}
.phase-video-window.active .window-status {
    background: #d4a853;
    box-shadow: 0 0 6px rgba(212,168,83,0.6);
    animation: statusPulse 1.5s infinite;
}
.phase-video-window.completed .window-status {
    background: #27ae60;
}
.phase-video-window .window-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.3);
    font-size: 24px;
}
.phase-video-window .window-progress {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: rgba(255,255,255,0.1);
}
.phase-video-window .window-progress-bar {
    height: 100%;
    background: #d4a853;
    transition: width 0.2s;
    width: 0%;
}
@keyframes statusPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

/* ===== 综合控制面板 ===== */
.control-dashboard {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 20px;
}

/* 面板卡片 */
.panel-card {
    background: #fff;
    border-radius: 16px;
    box-shadow: 0 2px 12px rgba(0,0,0,0.06);
    overflow: hidden;
}
.panel-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    border-bottom: 1px solid #f0f0f0;
}
.panel-header h3 {
    font-size: 14px;
    color: #333;
    margin: 0;
    font-weight: 600;
}
.panel-badge {
    font-size: 11px;
    padding: 2px 8px;
    background: #f0f0f0;
    border-radius: 10px;
    color: #888;
}

/* ===== 左侧：时间轴 ===== */
.timeline-panel { margin-bottom: 20px; }
.timeline-track {
    position: relative;
    padding: 20px 18px;
}
.timeline-progress {
    position: absolute;
    top: 32px;
    left: 18px; right: 18px;
    height: 4px;
    background: #eee;
    border-radius: 2px;
}
.timeline-progress::after {
    content: '';
    position: absolute;
    top: 0; left: 0;
    height: 100%;
    width: var(--progress, 0%);
    background: #b64a2a;
    border-radius: 2px;
    transition: width 0.3s;
}
.timeline-phases {
    display: flex;
    justify-content: space-between;
    position: relative;
    z-index: 1;
}
.timeline-phase {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    transition: all 0.2s;
}
.timeline-phase-dot {
    width: 32px; height: 32px;
    border-radius: 50%;
    background: #fff;
    border: 2px solid #ddd;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    transition: all 0.3s;
    position: relative;
}
.timeline-phase-dot .dot-icon {
    position: relative;
    z-index: 2;
}
.timeline-phase-dot .dot-progress-ring {
    position: absolute;
    top: -4px; left: -4px;
    width: calc(100% + 8px);
    height: calc(100% + 8px);
    border-radius: 50%;
    background: conic-gradient(#3498db var(--progress, 0%), transparent var(--progress, 0%));
    opacity: 0;
    transition: all 0.3s;
    z-index: 1;
}
.timeline-phase.active .timeline-phase-dot .dot-progress-ring {
    opacity: 1;
    box-shadow: 0 0 8px rgba(52, 152, 219, 0.5);
}
.timeline-phase.completed .timeline-phase-dot .dot-progress-ring {
    background: conic-gradient(#27ae60 100%, transparent 100%);
    opacity: 1;
    box-shadow: 0 0 8px rgba(39, 174, 96, 0.5);
}
.timeline-phase:hover .timeline-phase-dot { transform: scale(1.15); border-color: #b64a2a; }
.timeline-phase.active .timeline-phase-dot {
    background: #b64a2a;
    border-color: #b64a2a;
    color: #fff;
    box-shadow: 0 0 0 4px rgba(182,74,42,0.15);
}
.timeline-phase.completed .timeline-phase-dot {
    background: #27ae60;
    border-color: #27ae60;
    color: #fff;
}
.timeline-phase-name {
    font-size: 11px;
    color: #666;
    text-align: center;
    max-width: 60px;
    line-height: 1.3;
}
.timeline-phase.active .timeline-phase-name { color: #b64a2a; font-weight: 500; }
.timeline-phase.completed .timeline-phase-name { color: #27ae60; }

/* ===== 左侧：视频库 ===== */
.library-tabs {
    display: flex;
    gap: 4px;
}
.lib-tab {
    padding: 5px 10px;
    border: 1px solid #eee;
    border-radius: 6px;
    background: #fff;
    cursor: pointer;
    font-size: 12px;
    transition: all 0.2s;
}
.lib-tab:hover { border-color: #b64a2a; color: #b64a2a; }
.lib-tab.active { background: #b64a2a; color: #fff; border-color: #b64a2a; }

.library-content { padding: 14px 18px; }
.upload-row { margin-bottom: 12px; }
.upload-btn-small {
    padding: 8px 14px;
    border: 1px dashed #ccc;
    border-radius: 8px;
    background: #fafafa;
    cursor: pointer;
    font-size: 12px;
    color: #666;
    transition: all 0.2s;
    width: 100%;
}
.upload-btn-small:hover { border-color: #3498db; color: #3498db; background: #f0f8ff; }

.video-grid {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 260px;
    overflow-y: auto;
}
.video-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: #f9f9f9;
    border-radius: 10px;
    border: 1px solid transparent;
    cursor: pointer;
    transition: all 0.2s;
}
.video-card:hover { background: #f0f0f0; border-color: #ddd; }
.video-card.active { background: #fdf2f0; border-color: #b64a2a; }
.video-card .thumb {
    width: 48px; height: 36px;
    background: #ddd;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    flex-shrink: 0;
}
.video-card .info { flex: 1; min-width: 0; }
.video-card .name {
    font-size: 13px;
    color: #333;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.video-card .meta {
    font-size: 11px;
    color: #999;
    margin-top: 2px;
}
.video-card .actions {
    display: flex;
    gap: 6px;
    opacity: 0;
    transition: opacity 0.2s;
}
.video-card:hover .actions { opacity: 1; }
.video-card .actions button {
    width: 28px; height: 28px;
    border: none;
    background: #f0f0f0;
    border-radius: 6px;
    cursor: pointer;
    color: #666;
    font-size: 11px;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
}
.video-card .actions .select-btn {
    background: rgba(52,152,219,0.1);
    color: #3498db;
    border: 1px solid rgba(52,152,219,0.3);
}
.video-card .actions .select-btn:hover {
    background: #3498db;
    color: #fff;
    border-color: #3498db;
}
.video-card .actions .delete-btn {
    background: rgba(231,76,60,0.1);
    color: #e74c3c;
    border: 1px solid rgba(231,76,60,0.3);
}
.video-card .actions .delete-btn:hover {
    background: #e74c3c;
    color: #fff;
    border-color: #e74c3c;
}

/* ===== 右侧：信息面板 ===== */
.info-panel { margin-bottom: 16px; }
.info-body {
    padding: 16px 18px;
    min-height: 120px;
}
.info-placeholder {
    text-align: center;
    color: #bbb;
    padding: 20px 0;
}
.info-placeholder i { font-size: 24px; margin-bottom: 8px; display: block; }
.info-placeholder p { margin: 0; font-size: 13px; }
.info-content { line-height: 1.6; }
.info-content .info-row {
    display: flex;
    justify-content: space-between;
    padding: 8px 0;
    border-bottom: 1px solid #f5f5f5;
    font-size: 13px;
}
.info-content .info-row:last-child { border-bottom: none; }
.info-content .info-label { color: #888; }
.info-content .info-value { color: #333; font-weight: 500; }

/* ===== 右侧：快捷操作 ===== */
.actions-body {
    padding: 14px 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.action-btn {
    padding: 10px 14px;
    border: 1px solid #eee;
    border-radius: 10px;
    background: #fff;
    cursor: pointer;
    font-size: 13px;
    color: #666;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}
.action-btn:hover { background: #f5f5f5; }
.action-btn.primary {
    background: #b64a2a;
    color: #fff;
    border-color: #b64a2a;
}
.action-btn.primary:hover { background: #a04020; }

/* ===== 响应式 ===== */
@media (max-width: 1024px) {
    .control-dashboard { grid-template-columns: 1fr; }
    .dashboard-right { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
    .phase-videos-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 768px) {
    .video-center-stage { margin-bottom: 16px; }
    .main-video-wrapper { border-radius: 12px; }
    .dashboard-right { grid-template-columns: 1fr; }
    .timeline-track { padding: 14px 12px; }
    .timeline-phases { gap: 8px; flex-wrap: wrap; justify-content: center; }
    .timeline-phase { min-width: 60px; }
    .timeline-phase-dot { width: 36px; height: 36px; }
    .timeline-phase-name { font-size: 10px; max-width: 56px; }
    .overlay-top { padding: 10px 12px; }
    .current-phase-badge { font-size: 12px; padding: 4px 12px; }
    .emotion-indicator-mini { font-size: 11px; padding: 3px 10px; }
    .welcome-text { font-size: 15px; }
    .welcome-sub { font-size: 12px; }
    .phase-videos-grid { grid-template-columns: repeat(2, 1fr); padding: 12px; }
    .phase-video-window .window-label { font-size: 10px; }
    .phase-video-window .window-placeholder { font-size: 18px; }
    .volume-slider-wrapper { width: 60px; }
    .volume-indicator { display: none; }
    .volume-percent { display: none; }
    .timeline-time-row { flex-wrap: wrap; gap: 10px; }
    .timeline-ctrl-btns { flex-wrap: wrap; }
    .tl-ctrl-btn { padding: 10px 12px; font-size: 12px; flex: 1; min-width: 80px; }
    .timeline-ctrl-body { padding: 12px 14px; }
    .panel-header { padding: 12px 14px; }
    .panel-header h3 { font-size: 13px; }
    .library-content { padding: 12px 14px; }
    .library-tabs { flex-wrap: wrap; gap: 6px; }
    .lib-tab { padding: 6px 10px; font-size: 12px; flex: 1; text-align: center; min-width: 60px; }
    .video-card { padding: 8px 10px; gap: 8px; }
    .video-card .thumb { width: 42px; height: 30px; font-size: 16px; }
    .video-card .name { font-size: 12px; }
    .video-card .meta { font-size: 10px; }
    .video-card .actions { opacity: 1; }
    .video-card .actions button { width: 28px; height: 28px; font-size: 11px; }
    .info-body { padding: 12px 14px; min-height: 80px; }
    .info-content .info-row { font-size: 12px; padding: 6px 0; }
    .actions-body { padding: 12px 14px; gap: 8px; }
    .action-btn { padding: 10px 14px; font-size: 13px; min-height: 44px; }
    .ending-config-panel { padding: 12px; margin-top: 12px; }
    .ending-config-panel h4 { font-size: 13px; margin-bottom: 10px; }
    .config-row { flex-direction: column; gap: 6px; margin-bottom: 10px; }
    .config-row label { width: auto; padding-top: 0; font-size: 11px; }
    .config-row input[type="text"],
    .config-row input[type="number"],
    .config-row input[type="date"],
    .config-row select,
    .config-row textarea {
        width: 100%;
        font-size: 13px;
        padding: 8px 10px;
    }
    .config-actions { margin-top: 10px; flex-direction: column; gap: 8px; }
    .save-btn, .preview-btn { width: 100%; padding: 10px 14px; font-size: 13px; min-height: 44px; }
    .ending-overlay { padding: 20px; }
    .ending-text { font-size: 20px !important; }
    .ending-studio { font-size: 14px; }
    .ending-date { font-size: 13px; }
    .ending-close-btn { width: 36px; height: 36px; font-size: 16px; }
}
@media (max-width: 480px) {
    .video-controls-bar { gap: 6px; padding: 8px 10px; }
    .time-display { font-size: 11px; min-width: 70px; }
    .ctrl-btn { width: 34px; height: 34px; font-size: 12px; }
    .phase-videos-grid { grid-template-columns: repeat(2, 1fr); gap: 6px; padding: 10px; }
    .volume-slider-wrapper { width: 50px; }
    .timeline-phase-dot { width: 32px; height: 32px; font-size: 13px; }
    .timeline-phase-name { font-size: 9px; max-width: 50px; }
    .overlay-top { padding: 8px 10px; }
    .current-phase-badge { font-size: 11px; padding: 3px 10px; }
    .emotion-indicator-mini { font-size: 10px; padding: 2px 8px; }
    .welcome-text { font-size: 14px; }
    .welcome-sub { font-size: 11px; }
    .avatar-mini { width: 64px; height: 80px; }
    .mini-face { width: 42px; height: 42px; }
    .library-tabs { gap: 4px; }
    .lib-tab { font-size: 11px; padding: 5px 8px; min-width: 54px; }
    .tl-ctrl-btn { font-size: 11px; padding: 8px 10px; min-width: 70px; }
    .video-card { padding: 6px 8px; gap: 6px; }
    .video-card .thumb { width: 36px; height: 26px; font-size: 14px; }
    .video-card .name { font-size: 11px; }
    .video-card .meta { font-size: 9px; }
    .video-card .actions button { width: 26px; height: 26px; font-size: 10px; }
    .info-body { padding: 10px 12px; }
    .info-content .info-row { font-size: 11px; padding: 5px 0; }
    .action-btn { padding: 8px 12px; font-size: 12px; }
    .ending-text { font-size: 16px !important; }
    .ending-studio { font-size: 12px; }
    .ending-date { font-size: 11px; }
}

/* ===== 时间线控制面板 ===== */
.timeline-ctrl-panel { margin-bottom: 20px; }
.timeline-ctrl-body { padding: 14px 18px; }
.timeline-time-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
    flex-wrap: wrap;
}
.tl-label {
    font-size: 11px;
    color: #999;
}
.tl-value {
    font-size: 14px;
    font-weight: 600;
    color: #333;
    font-variant-numeric: tabular-nums;
    min-width: 60px;
}
.tl-value.drift-ok { color: #27ae60; }
.tl-value.drift-warn { color: #f39c12; }
.tl-value.drift-error { color: #e74c3c; }
.timeline-ctrl-btns {
    display: flex;
    gap: 8px;
}
.tl-ctrl-btn {
    padding: 8px 16px;
    border: 1px solid #eee;
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
    font-size: 12px;
    color: #666;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    gap: 5px;
}
.tl-ctrl-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.tl-ctrl-btn:not(:disabled):hover { background: #f5f5f5; }
.tl-start:not(:disabled) { background: #27ae60; color: #fff; border-color: #27ae60; }
.tl-start:not(:disabled):hover { background: #219a52; }
.tl-pause:not(:disabled) { background: #f39c12; color: #fff; border-color: #f39c12; }
.tl-pause:not(:disabled):hover { background: #d68910; }
.tl-stop:not(:disabled) { background: #e74c3c; color: #fff; border-color: #e74c3c; }
.tl-stop:not(:disabled):hover { background: #c0392b; }
.tl-calibrate:not(:disabled) { background: #3498db; color: #fff; border-color: #3498db; }
.tl-calibrate:not(:disabled):hover { background: #2980b9; }

.sync-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #ccc;
    display: inline-block;
}
.sync-status-dot.synced { background: #27ae60; animation: syncPulse 2s infinite; }
.sync-status-dot.drifting { background: #f39c12; animation: syncPulse 0.8s infinite; }
.sync-status-dot.error { background: #e74c3c; animation: syncPulse 0.4s infinite; }
@keyframes syncPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

/* ===== 环节绑定指示器 ===== */
.timeline-phase .bind-indicator {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #ccc;
    margin-top: 2px;
}
.timeline-phase.has-video .bind-indicator { background: #3498db; }
.timeline-phase.active .bind-indicator { background: #fff; }

/* ===== 过渡环节样式 ===== */
.timeline-phase.transition-phase {
    position: relative;
}
.timeline-phase.transition-phase .timeline-phase-dot {
    width: 24px;
    height: 24px;
    border-color: #9b59b6;
    background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
    animation: transitionGlow 2s infinite;
}
.timeline-phase.transition-phase:hover .timeline-phase-dot {
    transform: scale(1.2);
    border-color: #9b59b6;
}
.timeline-phase.transition-phase.active .timeline-phase-dot {
    background: #9b59b6;
    border-color: #9b59b6;
    box-shadow: 0 0 0 4px rgba(155,89,182,0.2);
}
.timeline-phase.transition-phase .timeline-phase-name {
    color: #9b59b6;
    font-size: 10px;
}
.timeline-phase.transition-phase.active .timeline-phase-name {
    color: #9b59b6;
}
.timeline-phase.transition-phase.completed .timeline-phase-name {
    color: #9b59b6;
}
.timeline-phase.transition-phase.completed .timeline-phase-dot {
    background: #9b59b6;
    border-color: #9b59b6;
}
.timeline-phase.transition-phase.active .timeline-phase-dot .dot-progress-ring {
    background: conic-gradient(#9b59b6 var(--progress, 0%), transparent var(--progress, 0%));
}
.timeline-phase.transition-phase.completed .timeline-phase-dot .dot-progress-ring {
    background: conic-gradient(#9b59b6 100%, transparent 100%);
}
@keyframes transitionGlow {
    0%, 100% { box-shadow: 0 0 6px rgba(155,89,182,0.4); }
    50% { box-shadow: 0 0 12px rgba(155,89,182,0.8); }
}

/* 过渡环节视频窗口样式 */
.phase-video-window.transition-window {
    border-color: rgba(155,89,182,0.3);
}
.phase-video-window.transition-window:hover {
    border-color: #9b59b6;
}
.phase-video-window.transition-window.active {
    border-color: #9b59b6;
    box-shadow: 0 0 12px rgba(155,89,182,0.4);
}
.phase-video-window.transition-window.active .window-overlay {
    background: linear-gradient(transparent 30%, rgba(155,89,182,0.3));
}
.phase-video-window.transition-window.active .window-status {
    background: #9b59b6;
    box-shadow: 0 0 6px rgba(155,89,182,0.6);
}

/* ===== 定格留念暂停遮罩 ===== */
.pause-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0,0,0,0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 10;
    pointer-events: none;
}
.pause-content {
    text-align: center;
    animation: fadeInScale 0.3s ease-out;
}
.pause-icon {
    font-size: 48px;
    margin-bottom: 16px;
    animation: pulse 1.5s infinite;
}
.pause-text {
    font-size: 28px;
    font-weight: 600;
    color: #d4a853;
    text-shadow: 0 2px 4px rgba(0,0,0,0.5);
    margin-bottom: 8px;
}
.pause-hint {
    font-size: 14px;
    color: rgba(255,255,255,0.7);
}
@keyframes fadeInScale {
    from {
        opacity: 0;
        transform: scale(0.9);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}
@keyframes pulse {
    0%, 100% {
        opacity: 1;
        transform: scale(1);
    }
    50% {
        opacity: 0.7;
        transform: scale(1.1);
    }
}

/* ===== 片尾配置面板 ===== */
.ending-config-panel {
    margin-top: 16px;
    padding: 16px;
    background: rgba(0,0,0,0.1);
    border-radius: 8px;
    border: 1px solid rgba(212,168,83,0.2);
}
.ending-config-panel h4 {
    margin: 0 0 12px 0;
    font-size: 14px;
    color: #d4a853;
    display: flex;
    align-items: center;
    gap: 6px;
}
.config-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 10px;
}
.config-row label {
    width: 70px;
    font-size: 12px;
    color: #999;
    flex-shrink: 0;
    padding-top: 4px;
}
.config-row input[type="text"],
.config-row input[type="number"],
.config-row input[type="date"],
.config-row select,
.config-row textarea {
    flex: 1;
    padding: 6px 8px;
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: 4px;
    background: rgba(0,0,0,0.2);
    color: #fff;
    font-size: 12px;
    outline: none;
}
.config-row textarea {
    resize: vertical;
    min-height: 60px;
}
.config-row input:focus,
.config-row select:focus,
.config-row textarea:focus {
    border-color: #d4a853;
}
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
}
.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}
.toggle-slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #444;
    transition: 0.3s;
    border-radius: 22px;
}
.toggle-slider:before {
    position: absolute;
    content: "";
    height: 18px;
    width: 18px;
    left: 2px;
    bottom: 2px;
    background-color: #fff;
    transition: 0.3s;
    border-radius: 50%;
}
.toggle-switch input:checked + .toggle-slider {
    background-color: #d4a853;
}
.toggle-switch input:checked + .toggle-slider:before {
    transform: translateX(18px);
}
.config-actions {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}
.save-btn, .preview-btn {
    flex: 1;
    padding: 8px 12px;
    border: none;
    border-radius: 4px;
    font-size: 12px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    transition: all 0.2s;
}
.save-btn {
    background: #27ae60;
    color: #fff;
}
.save-btn:hover {
    background: #2ecc71;
}
.preview-btn {
    background: #3498db;
    color: #fff;
}
.preview-btn:hover {
    background: #2980b9;
}

/* ===== 片尾播放遮罩层 ===== */
.ending-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0,0,0,0.85);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 20;
    pointer-events: auto;
    opacity: 0;
    transition: opacity 1s ease;
}
.ending-overlay.show {
    opacity: 1;
}
.ending-content {
    text-align: center;
    max-width: 80%;
}
.ending-text {
    font-size: 32px;
    font-weight: 300;
    color: #fff;
    text-shadow: 0 2px 10px rgba(0,0,0,0.5);
    margin-bottom: 30px;
    line-height: 1.6;
}
.ending-text.fade-in {
    animation: endingFadeIn 2s ease-out forwards;
}
.ending-text.slide-in {
    animation: endingSlideIn 1.5s ease-out forwards;
}
.ending-text.scale-in {
    animation: endingScaleIn 1.5s ease-out forwards;
}
.ending-studio {
    font-size: 18px;
    color: #d4a853;
    margin-bottom: 8px;
}
.ending-date {
    font-size: 14px;
    color: rgba(255,255,255,0.6);
}
.ending-close-btn {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 40px;
    height: 40px;
    background: rgba(255,255,255,0.1);
    border: 1px solid rgba(255,255,255,0.2);
    border-radius: 50%;
    color: rgba(255,255,255,0.8);
    font-size: 18px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s;
    z-index: 30;
}
.ending-close-btn:hover {
    background: rgba(255,255,255,0.2);
    border-color: rgba(255,255,255,0.4);
    color: #fff;
}
.ending-scroll {
    position: absolute;
    bottom: 100px;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
    animation: scrollUp 20s linear forwards;
}
.ending-scroll-item {
    margin-bottom: 16px;
    font-size: 16px;
    color: rgba(255,255,255,0.8);
}
.ending-scroll-item.title {
    font-size: 24px;
    color: #d4a853;
    font-weight: 600;
}
.ending-particles {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    pointer-events: none;
    overflow: hidden;
}
.ending-particle {
    position: absolute;
    width: 6px;
    height: 6px;
    background: #d4a853;
    border-radius: 50%;
    opacity: 0;
}
@keyframes endingFadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes endingSlideIn {
    from { opacity: 0; transform: translateY(30px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes endingScaleIn {
    from { opacity: 0; transform: scale(0.8); }
    to { opacity: 1; transform: scale(1); }
}
@keyframes scrollUp {
    from { transform: translateX(-50%) translateY(100px); opacity: 0; }
    10% { opacity: 1; }
    90% { opacity: 1; }
    to { transform: translateX(-50%) translateY(-200px); opacity: 0; }
}
@keyframes particleFloat {
    0% { opacity: 0; transform: translateY(100vh) scale(0); }
    10% { opacity: 1; transform: translateY(80vh) scale(1); }
    90% { opacity: 1; transform: translateY(20vh) scale(1); }
    100% { opacity: 0; transform: translateY(-10vh) scale(0); }
}
</style>

<script>
(function() {
    // ==================== 状态变量 ====================
    var currentPhase = null;
    var currentEmotion = 'warm';
    var isLoading = false;
    var emotionLabels = {
        warm: '温暖', excited: '热烈', romantic: '浪漫',
        solemn: '庄重', joyful: '欢快', quiet: '安静'
    };
    var emotionColors = {
        warm: '#27ae60', excited: '#e74c3c', romantic: '#e91e63',
        solemn: '#2c3e50', joyful: '#f39c12', quiet: '#95a5a6'
    };
    var phaseIcons = {
        opening: '🎊', t1: '✨', bride_entrance: '👰', t2: '✨', vow: '💕', t3: '✨', 
        ring: '💍', t4: '✨', parent_tea: '🍵', t5: '✨', toast: '🥂', t6: '✨', 
        game: '🎮', t7: '✨', closing: '🎉'
    };
    var phaseNames = {
        opening: '开场白', t1: 'T1-过度', bride_entrance: '新娘入场', t2: 'T2-过度', 
        vow: '誓言环节', t3: 'T3-过度', ring: '交换戒指', t4: 'T4-过度', 
        parent_tea: '敬茶改口', t5: 'T5-过度', toast: '祝酒环节', t6: 'T6-过度', 
        game: '互动游戏', t7: 'T7-过度', closing: '圆满礼成'
    };

    var currentVolume = 1.0;
    var previousVolume = 1.0;
    var isMuted = false;
    var isVolumeDragging = false;
    var phaseVideoElements = {};
    var lastSyncPhase = null;
    var syncCheckRAF = null;
    var phaseVideoWindowsInitialized = false;

    function escapeHtml(str) {
        if (!str) return '';
        var div = document.createElement('div');
        div.textContent = str;
        return div.innerHTML;
    }

    // ==================== DOM 引用 ====================
    var mainVideo = document.getElementById('mainVideo');
    var videoOverlay = document.getElementById('videoOverlay');
    var overlayCenter = document.getElementById('overlayCenter');
    var currentPhaseBadge = document.getElementById('currentPhaseBadge');
    var emotionIndicatorMini = document.getElementById('emotionIndicatorMini');
    var timelineProgress = document.getElementById('timelineProgress');
    var timelinePhases = document.getElementById('timelinePhases');
    var progressFilled = document.getElementById('progressFilled');
    var progressHandle = document.getElementById('progressHandle');
    var phaseMarkers = document.getElementById('phaseMarkers');
    var timeDisplay = document.getElementById('timeDisplay');
    var infoBody = document.getElementById('infoBody');
    var phaseVideosGrid = document.getElementById('phaseVideosGrid');
    var btnMute = document.getElementById('btnMute');
    var volumeIcon = document.getElementById('volumeIcon');
    var volumeSliderWrapper = document.getElementById('volumeSliderWrapper');
    var volumeSliderTrack = document.getElementById('volumeSliderTrack');
    var volumeSliderFilled = document.getElementById('volumeSliderFilled');
    var volumeSliderHandle = document.getElementById('volumeSliderHandle');
    var volumeIndicator = document.getElementById('volumeIndicator');
    var volumePercent = document.getElementById('volumePercent');

    // ==================== 时间线管理系统 ====================
    var TimelineManager = {
        startTime: null,
        pausedElapsed: 0,
        isRunning: false,
        isPaused: false,
        totalDuration: 0,
        phases: [],
        phaseVideoMap: {},
        syncDrift: 0,
        lastCalibrationTime: 0,
        calibrationInterval: 5000,
        maxDrift: 0.5,
        tickRAF: null,

        init: function(phases) {
            this.phases = phases;
            this.totalDuration = 0;
            var self = this;
            phases.forEach(function(p) {
                p.duration = p.duration || 300;
                p.startTime = self.totalDuration;
                p.endTime = self.totalDuration + p.duration;
                self.totalDuration += p.duration;
            });
            this.updateUI();
            this.updateBadges();
        },
        
        updateBadges: function() {
            var totalPhases = this.phases.length;
            var mainPhases = this.phases.filter(function(p) { return !p.isTransition; }).length;
            var transitionPhases = this.phases.filter(function(p) { return p.isTransition; }).length;
            document.getElementById('timelineBadge').textContent = totalPhases + '个环节 (' + mainPhases + '主+' + transitionPhases + '过渡)';
            document.getElementById('phaseVideosBadge').textContent = totalPhases + '路视频';
        },

        start: function() {
            if (this.isRunning && !this.isPaused) return;
            if (this.isPaused) {
                this.startTime = Date.now() - this.pausedElapsed * 1000;
                this.isPaused = false;
            } else {
                this.startTime = Date.now();
                this.pausedElapsed = 0;
            }
            this.isRunning = true;
            this.isPaused = false;
            this.lastCalibrationTime = Date.now();
            this.startTick();
            this.updateControlButtons();
            document.getElementById('timelineCtrlBadge').textContent = '运行中';
            document.getElementById('syncStatusDot').className = 'sync-status-dot synced';
        },

        pause: function() {
            if (!this.isRunning || this.isPaused) return;
            this.pausedElapsed = (Date.now() - this.startTime) / 1000;
            this.isPaused = true;
            this.stopTick();
            this.updateControlButtons();
            document.getElementById('timelineCtrlBadge').textContent = '已暂停';
            document.getElementById('syncStatusDot').className = 'sync-status-dot drifting';
        },

        stop: function() {
            this.isRunning = false;
            this.isPaused = false;
            this.startTime = null;
            this.pausedElapsed = 0;
            this.syncDrift = 0;
            this.stopTick();
            this.updateControlButtons();
            document.getElementById('timelineCtrlBadge').textContent = '已停止';
            document.getElementById('syncStatusDot').className = 'sync-status-dot';
            document.getElementById('tlElapsed').textContent = '00:00:00';
            document.getElementById('tlCurrentPhase').textContent = '--';
            document.getElementById('tlSyncDrift').textContent = '0.0s';
            document.getElementById('tlSyncDrift').className = 'tl-value drift-ok';
            if (typeof EndingManager !== 'undefined') {
                EndingManager._endingTriggered = false;
                EndingManager.stop();
            }
        },

        reset: function() {
            this.stop();
            this.phaseVideoMap = {};
            this.phases.forEach(function(p) {
                p.startTime = 0;
                p.endTime = p.duration;
            });
            var self = this;
            this.totalDuration = 0;
            this.phases.forEach(function(p) {
                p.startTime = self.totalDuration;
                p.endTime = self.totalDuration + p.duration;
                self.totalDuration += p.duration;
            });
            this.updateUI();
            this.updateBadges();
        },

        getElapsed: function() {
            if (!this.isRunning) return 0;
            if (this.isPaused) return this.pausedElapsed;
            return (Date.now() - this.startTime) / 1000;
        },

        getPhaseAtTime: function(elapsed) {
            for (var i = 0; i < this.phases.length; i++) {
                var p = this.phases[i];
                if (elapsed >= p.startTime && elapsed < p.endTime) {
                    return p;
                }
            }
            if (elapsed >= this.totalDuration) {
                return this.phases[this.phases.length - 1];
            }
            return this.phases[0];
        },

        getPhaseProgress: function(phaseCode) {
            var elapsed = this.getElapsed();
            for (var i = 0; i < this.phases.length; i++) {
                var p = this.phases[i];
                if (p.code === phaseCode) {
                    if (elapsed < p.startTime) return 0;
                    if (elapsed >= p.endTime) return 100;
                    return ((elapsed - p.startTime) / p.duration) * 100;
                }
            }
            return 0;
        },

        getOverallProgress: function() {
            var elapsed = this.getElapsed();
            return Math.min((elapsed / this.totalDuration) * 100, 100);
        },

        jumpToPhase: function(phaseCode) {
            var phase = null;
            for (var i = 0; i < this.phases.length; i++) {
                if (this.phases[i].code === phaseCode) {
                    phase = this.phases[i];
                    break;
                }
            }
            if (!phase) return;
            var targetElapsed = phase.startTime;
            this.startTime = Date.now() - targetElapsed * 1000;
            this.pausedElapsed = targetElapsed;
            this.syncDrift = 0;
            lastSyncPhase = null;
            this.onPhaseChange(phaseCode);
            this.syncVideoToTimeline();
        },

        bindVideo: function(phaseCode, videoUrl, videoId) {
            this.phaseVideoMap[phaseCode] = { url: videoUrl, videoId: videoId };
            this.updatePhaseVideoWindow(phaseCode);
            var savedBindings = JSON.parse(localStorage.getItem('hostVideoBindings') || '{}');
            savedBindings[phaseCode] = { url: videoUrl, videoId: videoId };
            localStorage.setItem('hostVideoBindings', JSON.stringify(savedBindings));
        },

        unbindVideo: function(phaseCode) {
            delete this.phaseVideoMap[phaseCode];
            this.updatePhaseVideoWindow(phaseCode);
            var savedBindings = JSON.parse(localStorage.getItem('hostVideoBindings') || '{}');
            delete savedBindings[phaseCode];
            localStorage.setItem('hostVideoBindings', JSON.stringify(savedBindings));
        },

        syncVideoToTimeline: function() {
            if (currentVideoType === 'opening') return;
            var elapsed = this.getElapsed();
            var phase = this.getPhaseAtTime(elapsed);
            if (!phase) return;

            var boundVideo = this.phaseVideoMap[phase.code];
            if (boundVideo && boundVideo.url) {
                if (ceremonyVideoUrl !== boundVideo.url) {
                    loadVideoToPlayer(boundVideo.url, 'host');
                }
                var phaseElapsed = elapsed - phase.startTime;
                var videoTime = Math.min(phaseElapsed, mainVideo.duration || phaseElapsed);
                if (Math.abs(mainVideo.currentTime - videoTime) > this.maxDrift) {
                    mainVideo.currentTime = videoTime;
                }
            } else if (mainVideo.duration) {
                var targetTime = (elapsed / this.totalDuration) * mainVideo.duration;
                targetTime = Math.min(targetTime, mainVideo.duration);
                if (Math.abs(mainVideo.currentTime - targetTime) > this.maxDrift) {
                    mainVideo.currentTime = targetTime;
                }
            }
        },

        calibrate: function() {
            this.syncDrift = 0;
            this.lastCalibrationTime = Date.now();
            this.syncVideoToTimeline();
            document.getElementById('syncStatusDot').className = 'sync-status-dot synced';
        },

        checkSync: function() {
            if (!this.isRunning || this.isPaused) return;
            if (currentVideoType === 'opening') return;

            var elapsed = this.getElapsed();
            var phase = this.getPhaseAtTime(elapsed);
            if (!phase || !mainVideo.duration) return;

            var boundVideo = this.phaseVideoMap[phase.code];
            var expectedVideoTime;
            if (boundVideo) {
                expectedVideoTime = elapsed - phase.startTime;
            } else {
                expectedVideoTime = (elapsed / this.totalDuration) * mainVideo.duration;
            }

            this.syncDrift = Math.abs(mainVideo.currentTime - expectedVideoTime);
            var driftEl = document.getElementById('tlSyncDrift');
            var dotEl = document.getElementById('syncStatusDot');

            if (this.syncDrift <= 0.3) {
                driftEl.textContent = this.syncDrift.toFixed(1) + 's';
                driftEl.className = 'tl-value drift-ok';
                dotEl.className = 'sync-status-dot synced';
            } else if (this.syncDrift <= this.maxDrift) {
                driftEl.textContent = this.syncDrift.toFixed(1) + 's';
                driftEl.className = 'tl-value drift-warn';
                dotEl.className = 'sync-status-dot drifting';
            } else {
                driftEl.textContent = this.syncDrift.toFixed(1) + 's';
                driftEl.className = 'tl-value drift-error';
                dotEl.className = 'sync-status-dot error';
                this.syncVideoToTimeline();
            }

            if (Date.now() - this.lastCalibrationTime > this.calibrationInterval) {
                if (this.syncDrift > 0.2) {
                    this.syncVideoToTimeline();
                }
                this.lastCalibrationTime = Date.now();
            }
        },

        onPhaseChange: function(phaseCode) {
            if (currentPhase === phaseCode) return;
            currentPhase = phaseCode;
            var phase = ceremonyPhases.find(function(p) { return p.code === phaseCode; });
            if (!phase) return;

            renderTimeline();
            updateOverlayPhase(phase);
            updateEmotionIndicator(phase.emotion);
            updateInfoPanel(phase);
            updateWelcomeState(phase);
            updatePhaseVideoWindows(phaseCode);

            var boundVideo = this.phaseVideoMap[phaseCode];
            if (boundVideo && boundVideo.url) {
                if (ceremonyVideoUrl !== boundVideo.url) {
                    loadVideoToPlayer(boundVideo.url, 'host');
                }
            }

            document.getElementById('tlCurrentPhase').textContent = phase.name;
        },

        startTick: function() {
            if (this.tickRAF) cancelAnimationFrame(this.tickRAF);
            var self = this;
            function tick() {
                if (!self.isRunning || self.isPaused) return;
                var elapsed = self.getElapsed();
                if (elapsed >= self.totalDuration) {
                    self.stop();
                    return;
                }
                self.onTick();
                self.tickRAF = requestAnimationFrame(tick);
            }
            this.tickRAF = requestAnimationFrame(tick);
        },

        stopTick: function() {
            if (this.tickRAF) {
                cancelAnimationFrame(this.tickRAF);
                this.tickRAF = null;
            }
        },

        onTick: function() {
            var elapsed = this.getElapsed();
            var hours = Math.floor(elapsed / 3600);
            var mins = Math.floor((elapsed % 3600) / 60);
            var secs = Math.floor(elapsed % 60);
            document.getElementById('tlElapsed').textContent =
                String(hours).padStart(2, '0') + ':' + String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');

            var phase = this.getPhaseAtTime(elapsed);
            if (phase && phase.code !== currentPhase) {
                this.onPhaseChange(phase.code);
            }

            var overallProgress = this.getOverallProgress();
            timelineProgress.style.setProperty('--progress', overallProgress + '%');

            ceremonyPhases.forEach(function(p) {
                var progress = TimelineManager.getPhaseProgress(p.code);
                updatePhaseWindowProgress(p.code, progress);
                updateTimelinePhaseProgress(p.code, progress);
            });

            this.checkSync();

            if (typeof EndingManager !== 'undefined' && EndingManager.config && EndingManager.config.autoTrigger) {
                if (elapsed >= this.totalDuration - 5 && !EndingManager._endingTriggered) {
                    EndingManager.play();
                }
            }
        },

        updateControlButtons: function() {
            var btnStart = document.getElementById('btnTimelineStart');
            var btnPause = document.getElementById('btnTimelinePause');
            var btnStop = document.getElementById('btnTimelineStop');
            var btnCalibrate = document.getElementById('btnTimelineCalibrate');

            btnStart.disabled = (this.isRunning && !this.isPaused);
            btnPause.disabled = (!this.isRunning || this.isPaused);
            btnStop.disabled = !this.isRunning;
            btnCalibrate.disabled = !this.isRunning;

            if (this.isPaused) {
                btnStart.innerHTML = '<i class="fas fa-play"></i> 继续';
            } else {
                btnStart.innerHTML = '<i class="fas fa-play"></i> 启动';
            }
        },

        updatePhaseVideoWindow: function(phaseCode) {
            var el = phaseVideoElements[phaseCode];
            if (!el || !el.window) return;
            var boundVideo = this.phaseVideoMap[phaseCode];
            var video = el.video;
            var placeholder = el.window.querySelector('.window-placeholder');

            if (boundVideo && boundVideo.url) {
                video.src = boundVideo.url;
                video.muted = true;
                video.style.display = 'block';
                video.load();
                if (placeholder) placeholder.style.display = 'none';
            } else {
                video.src = '';
                video.style.display = 'none';
                if (placeholder) placeholder.style.display = '';
            }
        },

        updateUI: function() {
            this.updateControlButtons();
        },

        reset: function() {
            this.stop();
            this.phaseVideoMap = {};
            var self = this;
            this.phases.forEach(function(p) {
                p.startTime = 0;
                p.endTime = p.duration;
            });
            this.totalDuration = 0;
            this.phases.forEach(function(p) {
                p.startTime = self.totalDuration;
                p.endTime = self.totalDuration + p.duration;
                self.totalDuration += p.duration;
            });
            this.phases.forEach(function(p) {
                self.updatePhaseVideoWindow(p.code);
            });
            this.updateUI();
            this.updateBadges();
        }
    };

    // ==================== API 工具 ====================
    function apiCall(method, path, params, callback) {
        var xhr = new XMLHttpRequest();
        var url = 'api/api_router.php?path=' + path;
        if (method === 'GET' && params) {
            var qs = Object.keys(params).map(function(k) { return k + '=' + encodeURIComponent(params[k]); }).join('&');
            url += '&' + qs;
        }
        xhr.open(method, url, true);
        xhr.timeout = 30000;
        xhr.onload = function() {
            if (xhr.status === 200) {
                try { callback(null, JSON.parse(xhr.responseText)); }
                catch(e) { callback(e); }
            } else { callback(new Error('HTTP ' + xhr.status)); }
        };
        xhr.onerror = function() { callback(new Error('网络错误')); };
        xhr.ontimeout = function() { callback(new Error('请求超时')); };
        if (method === 'POST' && params && !(params instanceof FormData)) {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(Object.keys(params).map(function(k) { return k + '=' + encodeURIComponent(params[k]); }).join('&'));
        } else if ((method === 'DELETE' || method === 'PUT') && params) {
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.send(JSON.stringify(params));
        } else { xhr.send(); }
    }

    function setLoading(loading) {
        isLoading = loading;
    }

    // ==================== 音量控制 ====================
    function initVolumeControl() {
        mainVideo.volume = currentVolume;
        updateVolumeUI(currentVolume);

        btnMute.addEventListener('click', function() {
            if (isMuted) {
                isMuted = false;
                mainVideo.volume = previousVolume;
                currentVolume = previousVolume;
            } else {
                isMuted = true;
                previousVolume = currentVolume > 0 ? currentVolume : 0.5;
                mainVideo.volume = 0;
                currentVolume = 0;
            }
            updateVolumeUI(currentVolume);
        });

        volumeSliderWrapper.addEventListener('mousedown', onVolumeSliderStart);
        volumeSliderWrapper.addEventListener('touchstart', onVolumeSliderStart, { passive: false });
        document.addEventListener('mousemove', onVolumeSliderMove);
        document.addEventListener('touchmove', onVolumeSliderMove, { passive: false });
        document.addEventListener('mouseup', onVolumeSliderEnd);
        document.addEventListener('touchend', onVolumeSliderEnd);
    }

    function onVolumeSliderStart(e) {
        e.preventDefault();
        isVolumeDragging = true;
        updateVolumeFromEvent(e);
    }

    function onVolumeSliderMove(e) {
        if (!isVolumeDragging) return;
        e.preventDefault();
        updateVolumeFromEvent(e);
    }

    function onVolumeSliderEnd() {
        if (!isVolumeDragging) return;
        isVolumeDragging = false;
    }

    function updateVolumeFromEvent(e) {
        var rect = volumeSliderTrack.getBoundingClientRect();
        var clientX = e.touches ? e.touches[0].clientX : e.clientX;
        var percent = (clientX - rect.left) / rect.width;
        percent = Math.max(0, Math.min(1, percent));
        currentVolume = percent;
        isMuted = percent === 0;
        if (percent > 0) previousVolume = percent;
        mainVideo.volume = percent;
        updateVolumeUI(percent);
    }

    function updateVolumeUI(volume) {
        var percent = Math.round(volume * 100);
        volumeSliderFilled.style.width = (volume * 100) + '%';
        volumeSliderHandle.style.left = (volume * 100) + '%';
        volumePercent.textContent = percent + '%';

        if (isMuted || volume === 0) {
            volumeIcon.className = 'fas fa-volume-mute';
            btnMute.classList.add('is-muted');
            btnMute.title = '取消静音';
        } else if (volume < 0.5) {
            volumeIcon.className = 'fas fa-volume-down';
            btnMute.classList.remove('is-muted');
            btnMute.title = '静音';
        } else {
            volumeIcon.className = 'fas fa-volume-up';
            btnMute.classList.remove('is-muted');
            btnMute.title = '静音';
        }

        var bars = volumeIndicator.querySelectorAll('.vol-bar');
        var activeBars = 0;
        if (volume === 0 || isMuted) {
            activeBars = 0;
        } else if (volume <= 0.2) {
            activeBars = 1;
        } else if (volume <= 0.4) {
            activeBars = 2;
        } else if (volume <= 0.6) {
            activeBars = 3;
        } else if (volume <= 0.8) {
            activeBars = 4;
        } else {
            activeBars = 5;
        }

        bars.forEach(function(bar, index) {
            bar.classList.remove('active', 'muted');
            if (isMuted || volume === 0) {
                bar.classList.add('muted');
            } else if (index < activeBars) {
                bar.classList.add('active');
            }
        });
    }

    // ==================== 环节时间轴 ====================
    var ceremonyPhases = [];
    var ceremonyVideoUrl = null;
    var ceremonyUserId = 'user_' + Date.now();
    var currentVideoId = null;
    var currentVideoTab = 'opening';
    var currentVideoType = 'opening';

    function initTimeline() {
        var phases = [
            { code: 'opening', name: '开场白', emotion: 'warm', desc: '婚礼正式开始，欢迎来宾', host: '各位来宾，大家好！欢迎来到这场美丽的婚礼，我是今天的主持人喜乐。', duration: 300, isTransition: false },
            { code: 't1', name: 'T1-过度', emotion: 'warm', desc: '开场过渡暖场视频', host: '让我们稍作等待，精彩即将继续...', duration: 120, isTransition: true },
            { code: 'bride_entrance', name: '新娘入场', emotion: 'romantic', desc: '新娘挽父亲手臂入场', host: '现在，让我们用最热烈的掌声，迎接今天最美丽的新娘入场！', duration: 360, isTransition: false },
            { code: 't2', name: 'T2-过度', emotion: 'romantic', desc: '告白过渡视频', host: '爱情是最美的诗篇...', duration: 90, isTransition: true },
            { code: 'vow', name: '誓言环节', emotion: 'romantic', desc: '新人互诉爱的誓言', host: '接下来，请新人面对面，互诉心中最真挚的爱的誓言。', duration: 420, isTransition: false },
            { code: 't3', name: 'T3-过度', emotion: 'warm', desc: '交换戒指准备视频', host: '让我们期待这神圣的时刻...', duration: 60, isTransition: true },
            { code: 'ring', name: '交换戒指', emotion: 'solemn', desc: '交换婚戒，许下承诺', host: '请交换婚戒，将这份永恒的承诺戴在彼此的无名指上。', duration: 300, isTransition: false },
            { code: 't4', name: 'T4-过度', emotion: 'warm', desc: '感恩父母过渡视频', host: '感恩父母的养育之恩...', duration: 150, isTransition: true },
            { code: 'parent_tea', name: '敬茶改口', emotion: 'warm', desc: '向双方父母敬茶', host: '现在请新人向双方父母敬茶，感谢父母的养育之恩。', duration: 480, isTransition: false },
            { code: 't5', name: 'T5-过度', emotion: 'joyful', desc: '祝福过渡视频', host: '让我们共同举杯祝福...', duration: 90, isTransition: true },
            { code: 'toast', name: '祝酒环节', emotion: 'joyful', desc: '全场举杯庆祝', host: '让我们共同举杯，为新人送上最美好的祝福！', duration: 300, isTransition: false },
            { code: 't6', name: 'T6-过度', emotion: 'joyful', desc: '生活花絮视频', host: '让我们欣赏新人的美好瞬间...', duration: 180, isTransition: true },
            { code: 'game', name: '互动游戏', emotion: 'joyful', desc: '宾客互动环节', host: '接下来是欢乐的互动游戏时间，让我们一起嗨起来！', duration: 600, isTransition: false },
            { code: 't7', name: 'T7-过度', emotion: 'warm', desc: '收尾过渡视频', host: '感谢各位来宾的见证...', duration: 120, isTransition: true },
            { code: 'closing', name: '圆满礼成', emotion: 'warm', desc: '婚礼圆满结束', host: '婚礼圆满礼成！感谢各位来宾的见证，祝新人幸福美满！', duration: 300, isTransition: false }
        ];
        ceremonyPhases = phases;
        TimelineManager.init(phases);
        var savedBindings = JSON.parse(localStorage.getItem('hostVideoBindings') || '{}');
        Object.keys(savedBindings).forEach(function(phaseCode) {
            var binding = savedBindings[phaseCode];
            TimelineManager.phaseVideoMap[phaseCode] = binding;
        });
        renderTimeline();
        initPhaseVideoWindows();
    }

    function renderTimeline() {
        var html = '';
        ceremonyPhases.forEach(function(phase, index) {
            var isActive = currentPhase === phase.code;
            var hasVideo = !!TimelineManager.phaseVideoMap[phase.code];
            var isTransition = phase.isTransition || false;
            var progress = TimelineManager.getPhaseProgress(phase.code);
            var isCompleted = progress >= 100;
            html += '<div class="timeline-phase' + (isActive ? ' active' : '') + (hasVideo ? ' has-video' : '') + (isTransition ? ' transition-phase' : '') + (isCompleted ? ' completed' : '') + '" data-phase="' + phase.code + '">' +
                '<div class="timeline-phase-dot' + (isTransition ? ' transition-dot' : '') + '" data-progress="' + progress + '">' +
                '<span class="dot-icon">' + phaseIcons[phase.code] + '</span>' +
                '<div class="dot-progress-ring" style="--progress:' + progress + '%"></div>' +
                '</div>' +
                '<div class="timeline-phase-name">' + phase.name + '</div>' +
                '<div class="bind-indicator" title="' + (hasVideo ? '已绑定视频' : '未绑定视频') + '"></div>' +
                '</div>';
        });
        timelinePhases.innerHTML = html;

        timelinePhases.querySelectorAll('.timeline-phase').forEach(function(item) {
            item.addEventListener('click', function() {
                var phaseCode = this.dataset.phase;
                activatePhase(phaseCode);
            });
        });
    }

    // ==================== 环节视频窗口矩阵 ====================
    function initPhaseVideoWindows() {
        if (phaseVideoWindowsInitialized) return;
        var html = '';
        ceremonyPhases.forEach(function(phase, index) {
            var isTransition = phase.isTransition || false;
            html += '<div class="phase-video-window' + (isTransition ? ' transition-window' : '') + '" id="phaseWindow_' + phase.code + '" data-phase="' + phase.code + '" data-index="' + index + '">' +
                '<div class="window-placeholder">' + phaseIcons[phase.code] + '</div>' +
                '<video id="phaseVideo_' + phase.code + '" preload="metadata" muted playsinline webkit-playsinline x5-playsinline style="display:none;"></video>' +
                '<div class="window-overlay">' +
                '<div class="window-label"><span class="window-icon">' + phaseIcons[phase.code] + '</span> ' + phase.name + '</div>' +
                '</div>' +
                '<div class="window-status"></div>' +
                '<div class="window-progress"><div class="window-progress-bar" id="phaseWindowProgress_' + phase.code + '"></div></div>' +
                '</div>';
        });
        phaseVideosGrid.innerHTML = html;

        ceremonyPhases.forEach(function(phase) {
            phaseVideoElements[phase.code] = {
                window: document.getElementById('phaseWindow_' + phase.code),
                video: document.getElementById('phaseVideo_' + phase.code),
                progressBar: document.getElementById('phaseWindowProgress_' + phase.code)
            };
        });

        phaseVideosGrid.querySelectorAll('.phase-video-window').forEach(function(window) {
            window.addEventListener('click', function() {
                var phaseCode = this.dataset.phase;
                activatePhase(phaseCode);
                if (TimelineManager.isRunning) {
                    TimelineManager.jumpToPhase(phaseCode);
                }
            });
        });

        phaseVideoWindowsInitialized = true;
    }

    function updatePhaseVideoWindows(activePhaseCode) {
        ceremonyPhases.forEach(function(phase) {
            var el = phaseVideoElements[phase.code];
            if (!el || !el.window) return;
            el.window.classList.remove('active', 'completed');
            if (phase.code === activePhaseCode) {
                el.window.classList.add('active');
            } else {
                var progress = TimelineManager.getPhaseProgress(phase.code);
                if (progress >= 100) {
                    el.window.classList.add('completed');
                }
            }
        });
    }

    function updatePhaseWindowProgress(phaseCode, percent) {
        var el = phaseVideoElements[phaseCode];
        if (!el || !el.progressBar) return;
        el.progressBar.style.width = percent + '%';
    }

    function updateTimelinePhaseProgress(phaseCode, percent) {
        var dotEl = document.querySelector('.timeline-phase[data-phase="' + phaseCode + '"] .timeline-phase-dot');
        if (!dotEl) return;
        
        var progressRing = dotEl.querySelector('.dot-progress-ring');
        if (progressRing) {
            progressRing.style.setProperty('--progress', percent + '%');
        }
        
        // 更新完成状态
        var phaseEl = document.querySelector('.timeline-phase[data-phase="' + phaseCode + '"]');
        if (phaseEl) {
            if (percent >= 100) {
                phaseEl.classList.add('completed');
                phaseEl.classList.remove('active');
            } else if (phaseCode === currentPhase) {
                phaseEl.classList.add('active');
                phaseEl.classList.remove('completed');
            }
        }
    }

    function startPhaseWindowSync() {
        if (syncCheckRAF) cancelAnimationFrame(syncCheckRAF);
        syncPhaseWindows();
    }

    function stopPhaseWindowSync() {
        if (syncCheckRAF) {
            cancelAnimationFrame(syncCheckRAF);
            syncCheckRAF = null;
        }
    }

    function syncPhaseWindows() {
        if (currentVideoType === 'opening') {
            return;
        }

        if (TimelineManager.isRunning) {
            // 时间线运行中：进度由 onTick 驱动，此处仅维护 RAF 循环
            syncCheckRAF = requestAnimationFrame(syncPhaseWindows);
            return;
        }

        // 手动播放模式：根据主视频时间同步当前环节窗口进度
        if (mainVideo.duration && !mainVideo.paused && currentPhase && ceremonyVideoUrl) {
            var phase = ceremonyPhases.find(function(p) { return p.code === currentPhase; });
            if (phase) {
                var percent = (mainVideo.currentTime / mainVideo.duration) * 100;
                updatePhaseWindowProgress(currentPhase, Math.min(percent, 100));
            }
        }

        syncCheckRAF = requestAnimationFrame(syncPhaseWindows);
    }

    function activatePhase(phaseCode) {
        if (isLoading) return;
        currentPhase = phaseCode;
        var phase = ceremonyPhases.find(function(p) { return p.code === phaseCode; });
        if (!phase) return;

        renderTimeline();
        updateOverlayPhase(phase);
        updateEmotionIndicator(phase.emotion);
        updateInfoPanel(phase);
        updateWelcomeState(phase);
        updatePhaseVideoWindows(phaseCode);

        var boundVideo = TimelineManager.phaseVideoMap[phaseCode];
        if (boundVideo && boundVideo.url) {
            loadVideoToPlayer(boundVideo.url, 'host');
        }
    }

    function loadVideoToPlayer(url, videoType) {
        ceremonyVideoUrl = url;
        currentVideoType = videoType || 'host';
        mainVideo.muted = true;
        mainVideo.loop = (videoType === 'opening');
        mainVideo.volume = currentVolume;
        
        console.log('loadVideoToPlayer called:', { url: url, type: videoType, currentVolume: currentVolume });
        
        mainVideo.src = url;
        mainVideo.load();
        overlayCenter.style.display = '';
        var welcomeText = document.getElementById('welcomeText');
        var welcomeSub = document.getElementById('welcomeSub');
        if (welcomeText) welcomeText.textContent = '视频加载中...';
        if (welcomeSub) welcomeSub.textContent = '请稍候';

        var playAttempts = 0;
        var maxAttempts = 3;

        function tryPlay() {
            playAttempts++;
            var playPromise = mainVideo.play();
            if (playPromise && playPromise.then) {
                playPromise.then(function() {
                    console.log('视频播放成功');
                    overlayCenter.style.display = 'none';
                    if (currentVolume > 0) {
                        mainVideo.muted = false;
                    }
                }).catch(function(e) {
                    console.warn('视频播放尝试失败 (第' + playAttempts + '次):', e);
                    if (playAttempts < maxAttempts) {
                        setTimeout(tryPlay, 300);
                    } else {
                        console.warn('视频播放多次尝试失败，保持静音播放');
                        mainVideo.muted = true;
                        var retryPromise = mainVideo.play();
                        if (retryPromise && retryPromise.then) {
                            retryPromise.then(function() {
                                overlayCenter.style.display = 'none';
                            }).catch(function(e2) {
                                console.warn('静音播放也失败:', e2);
                                if (welcomeText) welcomeText.textContent = '视频播放失败';
                                if (welcomeSub) welcomeSub.textContent = '请点击播放按钮手动播放，或检查视频文件';
                                showResumeBtn();
                                overlayCenter.style.opacity = '1';
                            });
                        } else {
                            overlayCenter.style.display = 'none';
                        }
                    }
                });
            } else {
                setTimeout(function() {
                    if (!mainVideo.paused) {
                        overlayCenter.style.display = 'none';
                        if (currentVolume > 0) {
                            mainVideo.muted = false;
                        }
                    }
                }, 500);
            }
        }

        tryPlay();

        if (currentVideoType === 'opening') {
            stopPhaseWindowSync();
        } else {
            startPhaseWindowSync();
        }
    }

    function updateWelcomeState(phase) {
        var welcomeText = document.getElementById('welcomeText');
        var welcomeSub = document.getElementById('welcomeSub');
        if (welcomeText) welcomeText.textContent = phase.name;
        if (welcomeSub) welcomeSub.textContent = phase.host || phase.desc;
    }

    function updateOverlayPhase(phase) {
        currentPhaseBadge.innerHTML = '<span class="phase-icon">' + phaseIcons[phase.code] + '</span><span class="phase-text">' + escapeHtml(phase.name) + '</span>';
    }

    function updateEmotionIndicator(emotion) {
        currentEmotion = emotion;
        emotionIndicatorMini.querySelector('.emotion-dot').style.background = emotionColors[emotion] || '#27ae60';
        emotionIndicatorMini.querySelector('.emotion-text').textContent = emotionLabels[emotion] || emotion;
    }

    function updateInfoPanel(phase) {
        var boundVideo = TimelineManager.phaseVideoMap[phase.code];
        var hasVideo = !!boundVideo;
        var durationMin = Math.floor(phase.duration / 60);
        var durationSec = phase.duration % 60;
        var durationStr = durationMin + '分' + (durationSec > 0 ? durationSec + '秒' : '');
        var html = '<div class="info-content">' +
            '<div class="info-row"><span class="info-label">环节名称</span><span class="info-value">' + escapeHtml(phase.name) + '</span></div>' +
            '<div class="info-row"><span class="info-label">情感氛围</span><span class="info-value">' + escapeHtml(emotionLabels[phase.emotion]) + '</span></div>' +
            '<div class="info-row"><span class="info-label">环节时长</span><span class="info-value">' + durationStr + '</span></div>' +
            '<div class="info-row"><span class="info-label">时间区间</span><span class="info-value">' + formatTime(phase.startTime) + ' - ' + formatTime(phase.endTime) + '</span></div>' +
            '<div class="info-row"><span class="info-label">环节说明</span><span class="info-value">' + escapeHtml(phase.desc) + '</span></div>' +
            '<div class="info-row"><span class="info-label">主持词</span><span class="info-value" style="font-size:12px;line-height:1.5;">' + escapeHtml(phase.host) + '</span></div>' +
            '<div class="info-row"><span class="info-label">关联视频</span><span class="info-value">' + (hasVideo ? '<span style="color:#27ae60;">✓ 已绑定</span>' : '<span style="color:#999;">未绑定</span>') + '</span></div>' +
            '</div>';
        infoBody.innerHTML = html;
    }

    // ==================== 视频播放器控制 ====================
    function formatTime(seconds) {
        if (isNaN(seconds) || seconds < 0) seconds = 0;
        var mins = Math.floor(seconds / 60);
        var secs = Math.floor(seconds % 60);
        return String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');
    }

    function updateVideoProgress() {
        if (!mainVideo.duration) return;
        var percent = (mainVideo.currentTime / mainVideo.duration) * 100;
        progressFilled.style.width = percent + '%';
        progressHandle.style.left = percent + '%';
        timeDisplay.textContent = formatTime(mainVideo.currentTime) + ' / ' + formatTime(mainVideo.duration);
    }

    function showPauseBtn() {
        var btn = document.getElementById('btnMainPause');
        btn.innerHTML = '<i class="fas fa-pause"></i>';
        btn.title = '暂停';
        btn.classList.add('is-pause');
        btn.classList.remove('is-resume');
    }

    function showResumeBtn() {
        var btn = document.getElementById('btnMainPause');
        btn.innerHTML = '<i class="fas fa-play"></i>';
        btn.title = '继续';
        btn.classList.remove('is-pause');
        btn.classList.add('is-resume');
    }

    document.getElementById('btnMainPause').addEventListener('click', function() {
        if (this.classList.contains('is-pause')) {
            mainVideo.pause();
        } else {
            if (!ceremonyVideoUrl) return;
            mainVideo.play().catch(function() {});
        }
    });

    document.getElementById('btnMainStop').addEventListener('click', function() {
        mainVideo.pause();
        mainVideo.currentTime = 0;
        mainVideo.loop = false;
        showResumeBtn();
        overlayCenter.style.opacity = '1';
        overlayCenter.style.display = '';
        progressFilled.style.width = '0%';
        progressHandle.style.left = '0%';
        timeDisplay.textContent = '00:00 / ' + formatTime(mainVideo.duration || 0);
    });

    document.getElementById('btnFullscreen').addEventListener('click', function() {
        var wrapper = document.getElementById('mainVideoWrapper');
        if (wrapper.requestFullscreen) wrapper.requestFullscreen();
        else if (wrapper.webkitRequestFullscreen) wrapper.webkitRequestFullscreen();
    });

    mainVideo.addEventListener('play', function() {
        showPauseBtn();
        overlayCenter.style.opacity = '0';
    });
    mainVideo.addEventListener('pause', function() {
        showResumeBtn();
        overlayCenter.style.opacity = '1';
    });
    mainVideo.addEventListener('timeupdate', updateVideoProgress);
    mainVideo.addEventListener('ended', function() {
        if (currentVideoType === 'opening') {
            mainVideo.currentTime = 0;
            mainVideo.play().catch(function() {});
            return;
        }
        if (TimelineManager.isRunning && !TimelineManager.isPaused) {
            var elapsed = TimelineManager.getElapsed();
            var phase = TimelineManager.getPhaseAtTime(elapsed);
            if (phase && TimelineManager.phaseVideoMap[phase.code]) {
                mainVideo.currentTime = 0;
                mainVideo.play().catch(function() {});
            } else {
                showResumeBtn();
                overlayCenter.style.opacity = '1';
            }
        } else {
            showResumeBtn();
            overlayCenter.style.opacity = '1';
        }
    });
    mainVideo.addEventListener('error', function() {
        var errorMsg = '视频加载失败';
        if (mainVideo.error) {
            switch (mainVideo.error.code) {
                case 1: errorMsg = '视频加载被中止'; break;
                case 2: errorMsg = '网络错误，无法加载视频'; break;
                case 3: errorMsg = '视频解码失败，文件可能已损坏'; break;
                case 4: errorMsg = '视频格式不支持，请检查文件格式'; break;
            }
        }
        console.warn('视频播放错误:', mainVideo.error);
        showResumeBtn();
        overlayCenter.style.opacity = '1';
        var welcomeText = document.getElementById('welcomeText');
        var welcomeSub = document.getElementById('welcomeSub');
        if (welcomeText) welcomeText.textContent = '视频播放失败';
        if (welcomeSub) welcomeSub.textContent = errorMsg + '，请检查视频文件或重新选择';
    });

    document.getElementById('progressTrack').addEventListener('click', function(e) {
        if (!mainVideo.duration) return;
        var rect = this.getBoundingClientRect();
        var percent = (e.clientX - rect.left) / rect.width;
        mainVideo.currentTime = percent * mainVideo.duration;
    });

    // ==================== 时间线控制按钮 ====================
    document.getElementById('btnTimelineStart').addEventListener('click', function() {
        TimelineManager.start();
    });
    document.getElementById('btnTimelinePause').addEventListener('click', function() {
        TimelineManager.pause();
    });
    document.getElementById('btnTimelineStop').addEventListener('click', function() {
        if (!confirm('确定要停止时间线吗？当前进度将清空，但已绑定的视频不会删除。')) return;
        TimelineManager.stop();
        mainVideo.pause();
        mainVideo.currentTime = 0;
        showResumeBtn();
        overlayCenter.style.opacity = '1';
        overlayCenter.style.display = '';
        progressFilled.style.width = '0%';
        progressHandle.style.left = '0%';
        timeDisplay.textContent = '00:00 / ' + formatTime(mainVideo.duration || 0);
        currentPhase = null;
        renderTimeline();
        updatePhaseVideoWindows(null);
        ceremonyPhases.forEach(function(p) {
            updatePhaseWindowProgress(p.code, 0);
        });
        if (typeof EndingManager !== 'undefined') {
            EndingManager._endingTriggered = false;
            EndingManager.stop();
        }
    });
    document.getElementById('btnTimelineCalibrate').addEventListener('click', function() {
        TimelineManager.calibrate();
    });

    // ==================== 视频库管理 ====================
    function loadVideoList(type) {
        apiCall('GET', 'video/assets', { type: type, active_only: true }, function(err, resp) {
            var listId = type === 'opening' ? 'openingVideoList' : (type === 'host' ? 'hostVideoList' : 'endingVideoList');
            var container = document.getElementById(listId);
            if (err || !resp.success) {
                container.innerHTML = '<p style="text-align:center;color:#999;font-size:12px;">加载失败</p>';
                return;
            }
            var videoData = resp.data;
            if (videoData && videoData.data) videoData = videoData.data;
            if (!videoData || videoData.length === 0) {
                container.innerHTML = '<p style="text-align:center;color:#999;font-size:12px;">暂无视频，点击上方按钮上传</p>';
                return;
            }
            var html = '';
            videoData.forEach(function(video) {
                var isSelected = currentVideoId === video.video_id ? ' active' : '';
                html += '<div class="video-card' + isSelected + '" data-video-id="' + escapeHtml(video.video_id) + '" data-video-type="' + escapeHtml(type) + '">' +
                    '<div class="thumb">🎬</div>' +
                    '<div class="info">' +
                    '<div class="name">' + escapeHtml(video.video_name) + '</div>' +
                    '<div class="meta">' + escapeHtml(video.duration) + '秒 | ' + (video.file_size ? (video.file_size/1024/1024).toFixed(1) + 'MB' : '') + '</div>' +
                    '</div>' +
                    '<div class="actions">' +
                    '<button class="select-btn" data-video-id="' + escapeHtml(video.video_id) + '" data-video-type="' + escapeHtml(type) + '" title="选择"><i class="fas fa-check"></i></button>' +
                    '<button class="bind-btn" data-video-id="' + escapeHtml(video.video_id) + '" data-video-type="' + escapeHtml(type) + '" title="绑定到当前环节" style="background:rgba(52,152,219,0.1);color:#3498db;border:1px solid rgba(52,152,219,0.3);width:28px;height:28px;border-radius:6px;cursor:pointer;font-size:11px;display:flex;align-items:center;justify-content:center;transition:all 0.2s;"><i class="fas fa-link"></i></button>' +
                    '<button class="delete-btn" data-video-id="' + escapeHtml(video.video_id) + '" title="删除"><i class="fas fa-trash"></i></button>' +
                    '</div>' +
                    '</div>';
            });
            container.innerHTML = html;
        });
    }

    function selectVideo(videoId, type) {
        if (!videoId) return;
        currentVideoId = videoId;
        currentVideoTab = type;
        document.querySelectorAll('.video-card').forEach(function(c) { c.classList.remove('active'); });
        var card = document.querySelector('.video-card[data-video-id="' + videoId + '"]');
        if (card) card.classList.add('active');

        var streamUrl = 'stream_video.php?id=' + videoId;
        console.log('使用流式播放:', streamUrl);
        loadVideoToPlayer(streamUrl, type);
        if (type === 'ending' && typeof EndingManager !== 'undefined') {
            EndingManager.setVideoUrl(streamUrl, videoId);
        }
    }

    function bindVideoToPhase(videoId, phaseCode) {
        if (!phaseCode) {
            alert('请先选择一个婚礼环节');
            return;
        }
        var streamUrl = 'stream_video.php?id=' + videoId;
        TimelineManager.bindVideo(phaseCode, streamUrl, videoId);
        renderTimeline();
        var phase = ceremonyPhases.find(function(p) { return p.code === phaseCode; });
        if (phase) updateInfoPanel(phase);
        alert('已将视频绑定到环节: ' + (phase ? phase.name : phaseCode));
    }

    function deleteVideo(videoId) {
        if (!confirm('确定要删除这个视频吗？此操作无法撤销。')) return;
        apiCall('DELETE', 'video/asset/' + videoId, null, function(err, resp) {
            if (err) { 
                alert('删除失败: ' + err.message); 
                return; 
            }
            var success = resp.success || (resp.data && resp.data.success);
            if (!success) { 
                alert(resp.message || '删除失败'); 
                return; 
            }
            if (currentVideoId === videoId) { 
                currentVideoId = null; 
                ceremonyVideoUrl = null;
                if (mainVideo) {
                    mainVideo.pause();
                    mainVideo.src = '';
                    mainVideo.load();
                }
            }
            Object.keys(TimelineManager.phaseVideoMap).forEach(function(key) {
                if (TimelineManager.phaseVideoMap[key] && TimelineManager.phaseVideoMap[key].videoId === videoId) {
                    TimelineManager.unbindVideo(key);
                }
            });
            renderTimeline();
            loadVideoList(currentVideoTab);
            alert('视频删除成功');
        });
    }

    document.addEventListener('click', function(e) {
        var selectBtn = e.target.closest('.select-btn');
        if (selectBtn) {
            var vid = selectBtn.getAttribute('data-video-id');
            var vtype = selectBtn.getAttribute('data-video-type');
            if (vid) selectVideo(vid, vtype);
            return;
        }
        var bindBtn = e.target.closest('.bind-btn');
        if (bindBtn) {
            var vid = bindBtn.getAttribute('data-video-id');
            if (vid && currentPhase) bindVideoToPhase(vid, currentPhase);
            else alert('请先在时间轴上选择一个婚礼环节');
            return;
        }
        var card = e.target.closest('.video-card');
        if (card && !e.target.closest('.delete-btn') && !e.target.closest('.select-btn') && !e.target.closest('.bind-btn')) {
            var vid = card.getAttribute('data-video-id');
            var vtype = card.getAttribute('data-video-type') || currentVideoTab;
            if (vid) selectVideo(vid, vtype);
            return;
        }
        var delBtn = e.target.closest('.delete-btn');
        if (delBtn) {
            var vid = delBtn.getAttribute('data-video-id');
            if (vid) deleteVideo(vid);
        }
    });

    document.querySelectorAll('.lib-tab').forEach(function(btn) {
        btn.addEventListener('click', function() {
            document.querySelectorAll('.lib-tab').forEach(function(b) { b.classList.remove('active'); });
            document.querySelectorAll('.library-content').forEach(function(c) { c.style.display = 'none'; });
            this.classList.add('active');
            var lib = this.dataset.lib;
            document.getElementById('libContent' + lib.charAt(0).toUpperCase() + lib.slice(1)).style.display = '';
            loadVideoList(lib);
        });
    });

    function uploadVideo(type) {
        var inputId = type === 'opening' ? 'fileInputOpening' : (type === 'host' ? 'fileInputHost' : 'fileInputEnding');
        document.getElementById(inputId).click();
    }
    document.getElementById('btnUploadOpening').addEventListener('click', function() { uploadVideo('opening'); });
    document.getElementById('btnUploadHost').addEventListener('click', function() { uploadVideo('host'); });
    document.getElementById('btnUploadEnding').addEventListener('click', function() { uploadVideo('ending'); });
    document.getElementById('fileInputOpening').addEventListener('change', function(e) { handleFileUpload(e, 'opening'); });
    document.getElementById('fileInputHost').addEventListener('change', function(e) { handleFileUpload(e, 'host'); });
    document.getElementById('fileInputEnding').addEventListener('change', function(e) { handleFileUpload(e, 'ending'); });

    function handleFileUpload(event, type) {
        var file = event.target.files[0];
        if (!file) return;
        
        var fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
        if (fileSizeMB > 500) {
            alert('文件大小超过限制（最大500MB）');
            return;
        }
        
        var formData = new FormData();
        formData.append('video', file);
        formData.append('video_type', type);
        formData.append('video_name', file.name.replace(/\.[^/.]+$/, ''));
        
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'api/upload_video_php73.php', true);
        xhr.upload.onprogress = function(e) {
            if (e.lengthComputable) {
                var percent = Math.round((e.loaded / e.total) * 100);
                console.log('上传进度: ' + percent + '%');
            }
        };
        xhr.onload = function() {
            if (xhr.status === 200) {
                try {
                    var resp = JSON.parse(xhr.responseText);
                    if (resp.success) { 
                        alert('上传成功'); 
                        loadVideoList(type); 
                    } else { 
                        alert('上传失败: ' + (resp.message || '未知错误')); 
                    }
                } catch(e) { 
                    alert('上传失败: 响应格式错误'); 
                }
            } else { 
                alert('上传失败，HTTP ' + xhr.status); 
            }
        };
        xhr.onerror = function() { 
            alert('网络错误，请检查网络连接后重试'); 
        };
        xhr.ontimeout = function() { 
            alert('上传超时，请检查网络速度或尝试上传较小的文件'); 
        };
        xhr.timeout = 300000;
        xhr.send(formData);
        event.target.value = '';
    }

    // ==================== 快捷操作 ====================
    document.getElementById('btnSyncToScreen').addEventListener('click', function() {
        if (!currentVideoId) {
            alert('请先选择一个视频后再同步到大屏');
            return;
        }
        apiCall('GET', 'video/play/' + currentVideoId, null, function(err, resp) {
            var url = null;
            if (resp && resp.relative_url) {
                url = resp.relative_url;
            } else if (resp && resp.data && resp.data.url) {
                url = resp.data.url;
            } else if (resp && resp.url) {
                url = resp.url;
            }
            if (err || !resp.success || !url) { alert('获取视频播放地址失败'); return; }
            localStorage.setItem('currentVideoUrl', url);
            localStorage.setItem('currentVideoId', currentVideoId);
            localStorage.setItem('videoPlayCommand', 'play');
            localStorage.setItem('syncPhase', currentPhase || '');
            localStorage.setItem('syncTime', mainVideo.currentTime.toString());
            localStorage.setItem('timelineElapsed', TimelineManager.getElapsed().toString());
            localStorage.setItem('timelineRunning', TimelineManager.isRunning ? '1' : '0');
            alert('已同步到大屏，大屏会自动播放');
        });
    });

    document.getElementById('btnClearSession').addEventListener('click', function() {
        if (!confirm('确定要重置会话吗？当前进度将清空。')) return;
        apiCall('POST', 'digital-human/host/clear', null, function(err, resp) {
            if (!err && resp.success) {
                currentPhase = null;
                currentVideoId = null;
                ceremonyVideoUrl = null;
                currentVideoType = 'opening';
                lastSyncPhase = null;
                TimelineManager.reset();
                localStorage.removeItem('hostVideoBindings');
                mainVideo.pause();
                mainVideo.loop = false;
                mainVideo.src = '';
                mainVideo.load();
                overlayCenter.style.display = '';
                overlayCenter.style.opacity = '1';
                progressFilled.style.width = '0%';
                progressHandle.style.left = '0%';
                timeDisplay.textContent = '00:00 / 00:00';
                showResumeBtn();
                var welcomeText = document.getElementById('welcomeText');
                var welcomeSub = document.getElementById('welcomeSub');
                if (welcomeText) welcomeText.textContent = '点击左侧时间轴选择婚礼环节';
                if (welcomeSub) welcomeSub.textContent = '或从视频库选择视频播放';
                infoBody.innerHTML = '<div class="info-placeholder"><i class="fas fa-hand-pointer"></i><p>点击左侧时间轴选择婚礼环节</p></div>';
                renderTimeline();
                updatePhaseVideoWindows(null);
                ceremonyPhases.forEach(function(phase) {
                    updatePhaseWindowProgress(phase.code, 0);
                    var el = phaseVideoElements[phase.code];
                    if (el && el.video) {
                        el.video.src = '';
                        el.video.style.display = 'none';
                    }
                    if (el && el.window) {
                        var placeholder = el.window.querySelector('.window-placeholder');
                        if (placeholder) placeholder.style.display = '';
                    }
                });
                alert('会话已重置');
            }
        });
    });

    // ==================== 空格键暂停功能 ====================
    var isPausedManually = false;
    var pauseOverlay = null;
    
    function initKeyboardControls() {
        document.addEventListener('keydown', function(e) {
            var active = document.activeElement;
            if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT' || active.isContentEditable)) {
                return;
            }
            if (e.code === 'Space' && !e.repeat) {
                e.preventDefault();
                if (mainVideo.paused) {
                    if (isPausedManually) {
                        mainVideo.play().catch(function() {});
                        isPausedManually = false;
                        hidePauseOverlay();
                    }
                } else {
                    mainVideo.pause();
                    isPausedManually = true;
                    showPauseOverlay();
                }
            }
        });
    }
    
    function showPauseOverlay() {
        if (pauseOverlay) {
            pauseOverlay.style.display = 'flex';
            return;
        }
        pauseOverlay = document.createElement('div');
        pauseOverlay.className = 'pause-overlay';
        pauseOverlay.innerHTML = '<div class="pause-content"><div class="pause-icon">⏸️</div><div class="pause-text">定格留念</div><div class="pause-hint">按空格键继续</div></div>';
        document.getElementById('mainVideoWrapper').appendChild(pauseOverlay);
    }
    
    function hidePauseOverlay() {
        if (pauseOverlay) {
            pauseOverlay.style.display = 'none';
        }
    }

    // ==================== 片尾模块 ====================
    var EndingManager = {
        config: {
            text: '感谢各位来宾的光临，祝新人新婚快乐，永结同心！',
            studio: '礼典世家婚礼策划',
            date: '',
            animation: 'fade',
            duration: 30,
            bgMusic: null,
            autoTrigger: true
        },
        isPlaying: false,
        _endingTriggered: false,
        bgMusicAudio: null,
        endingOverlay: null,
        endingVideoUrl: null,

        init: function() {
            this.loadConfig();
            this.initUI();
            this.bindEvents();
        },

        loadConfig: function() {
            var self = this;
            apiCall('GET', 'ending/config', null, function(err, resp) {
                if (!err && resp.success && resp.data) {
                    var data = resp.data;
                    if (data.autoTrigger !== undefined) {
                        data.autoTrigger = data.autoTrigger !== '0' && data.autoTrigger !== false;
                    }
                    if (data.duration !== undefined) {
                        data.duration = parseInt(data.duration) || 30;
                    }
                    self.config = Object.assign(self.config, data);
                    self.updateUIForm();
                }
            });
            try {
                var saved = localStorage.getItem('endingConfig');
                if (saved) {
                    var parsed = JSON.parse(saved);
                    if (parsed.autoTrigger !== undefined) {
                        parsed.autoTrigger = parsed.autoTrigger !== '0' && parsed.autoTrigger !== false;
                    }
                    if (parsed.duration !== undefined) {
                        parsed.duration = parseInt(parsed.duration) || 30;
                    }
                    self.config = Object.assign(self.config, parsed);
                    self.updateUIForm();
                }
                var savedVideo = localStorage.getItem('endingVideoUrl');
                var savedVideoId = localStorage.getItem('endingVideoId');
                if (savedVideo) {
                    self.endingVideoUrl = savedVideo;
                    self.endingVideoId = savedVideoId || null;
                }
            } catch(e) {}
        },

        saveConfig: function() {
            var self = this;
            localStorage.setItem('endingConfig', JSON.stringify(this.config));
            apiCall('POST', 'ending/config', this.config, function(err, resp) {
                if (err || !resp.success) {
                    console.warn('片尾配置保存到服务器失败:', err);
                }
            });
        },

        updateUIForm: function() {
            var textEl = document.getElementById('endingText');
            var studioEl = document.getElementById('endingStudio');
            var dateEl = document.getElementById('endingDate');
            var animationEl = document.getElementById('endingAnimation');
            var durationEl = document.getElementById('endingDuration');
            var autoEl = document.getElementById('autoEnding');
            var bgMusicNameEl = document.getElementById('bgMusicName');

            if (textEl) textEl.value = this.config.text || '';
            if (studioEl) studioEl.value = this.config.studio || '';
            if (dateEl) dateEl.value = this.config.date || '';
            if (animationEl) animationEl.value = this.config.animation || 'fade';
            if (durationEl) durationEl.value = parseInt(this.config.duration) || 30;
            if (autoEl) autoEl.checked = this.config.autoTrigger !== false && this.config.autoTrigger !== '0';
            if (bgMusicNameEl && this.config.bgMusic) {
                bgMusicNameEl.textContent = '已上传背景音乐';
            }
        },

        initUI: function() {
            var dateInput = document.getElementById('endingDate');
            if (dateInput && !dateInput.value) {
                dateInput.value = new Date().toISOString().split('T')[0];
            }
        },

        bindEvents: function() {
            var self = this;

            document.getElementById('btnUploadBgMusic').addEventListener('click', function() {
                document.getElementById('endingBgMusic').click();
            });
            document.getElementById('endingBgMusic').addEventListener('change', function(e) {
                var file = e.target.files[0];
                if (!file) return;
                
                var fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
                if (fileSizeMB > 50) {
                    alert('背景音乐大小超过限制（最大50MB）');
                    return;
                }
                
                var formData = new FormData();
                formData.append('audio', file);
                formData.append('audio_name', file.name.replace(/\.[^/.]+$/, ''));
                
                var xhr = new XMLHttpRequest();
                xhr.open('POST', 'api/upload_audio.php', true);
                xhr.onload = function() {
                    if (xhr.status === 200) {
                        try {
                            var resp = JSON.parse(xhr.responseText);
                            if (resp.success && resp.data) {
                                self.config.bgMusic = resp.data.storage_path;
                                document.getElementById('bgMusicName').textContent = file.name;
                                self.saveConfig();
                                alert('背景音乐上传成功');
                            } else {
                                alert('上传失败: ' + (resp.message || '未知错误'));
                            }
                        } catch(e) {
                            alert('上传失败: 响应格式错误');
                        }
                    } else {
                        alert('上传失败，HTTP ' + xhr.status);
                    }
                };
                xhr.onerror = function() {
                    alert('网络错误，请检查网络连接');
                };
                xhr.timeout = 60000;
                xhr.send(formData);
                e.target.value = '';
            });

            document.getElementById('btnSaveEndingConfig').addEventListener('click', function() {
                self.config.text = document.getElementById('endingText').value;
                self.config.studio = document.getElementById('endingStudio').value;
                self.config.date = document.getElementById('endingDate').value;
                self.config.animation = document.getElementById('endingAnimation').value;
                self.config.duration = parseInt(document.getElementById('endingDuration').value) || 30;
                self.config.autoTrigger = document.getElementById('autoEnding').checked;
                self.saveConfig();
                alert('片尾配置已保存');
            });

            document.getElementById('btnPreviewEnding').addEventListener('click', function() {
                self.play();
            });
        },

        play: function() {
            if (this.isPlaying) return;
            var self = this;

            var wrapper = document.getElementById('mainVideoWrapper');
            if (!wrapper) return;

            this.isPlaying = true;
            this._endingTriggered = true;

            if (this.endingVideoUrl) {
                ceremonyVideoUrl = this.endingVideoUrl;
                currentVideoType = 'ending';
                mainVideo.muted = true;
                mainVideo.src = this.endingVideoUrl;
                mainVideo.loop = false;
                mainVideo.volume = currentVolume;
                mainVideo.load();
                var playPromise = mainVideo.play();
                if (playPromise && playPromise.then) {
                    playPromise.then(function() {
                        if (currentVolume > 0) {
                            mainVideo.muted = false;
                        }
                    }).catch(function(err) {
                        console.warn('片尾视频播放失败:', err);
                        setTimeout(function() {
                            mainVideo.play().catch(function(e2) {
                                console.warn('片尾视频重试播放失败:', e2);
                            });
                        }, 300);
                    });
                }
            } else {
                mainVideo.pause();
            }

            if (!this.endingOverlay) {
                this.endingOverlay = document.createElement('div');
                this.endingOverlay.className = 'ending-overlay';
                this.endingOverlay.addEventListener('click', function(e) {
                    if (e.target.classList.contains('ending-close-btn')) {
                        self.stop();
                    }
                });
                wrapper.appendChild(this.endingOverlay);
            }

            this.renderContent();

            setTimeout(function() {
                self.endingOverlay.classList.add('show');
            }, 50);

            if (this.config.bgMusic) {
                this.playBgMusic();
            }

            setTimeout(function() {
                self.stop();
            }, this.config.duration * 1000);
        },

        stop: function() {
            this.isPlaying = false;
            this._endingTriggered = false;
            if (this.endingOverlay) {
                this.endingOverlay.classList.remove('show');
                this.endingOverlay.innerHTML = '';
                if (this.endingOverlay.parentNode) {
                    this.endingOverlay.parentNode.removeChild(this.endingOverlay);
                }
                this.endingOverlay = null;
            }
            if (this.bgMusicAudio) {
                this.bgMusicAudio.pause();
                this.bgMusicAudio = null;
            }
            if (!TimelineManager.isRunning) {
                showResumeBtn();
                overlayCenter.style.opacity = '1';
                overlayCenter.style.display = '';
            }
        },

        renderContent: function() {
            var animationClass = '';
            switch (this.config.animation) {
                case 'fade': animationClass = 'fade-in'; break;
                case 'slide': animationClass = 'slide-in'; break;
                case 'scale': animationClass = 'scale-in'; break;
            }

            var html = '<button class="ending-close-btn" title="跳过片尾">✕</button>';
            html += '<div class="ending-content">';
            html += '<div class="ending-text ' + animationClass + '">' + escapeHtml(this.config.text) + '</div>';
            html += '<div class="ending-studio">' + escapeHtml(this.config.studio) + '</div>';
            if (this.config.date) {
                html += '<div class="ending-date">' + escapeHtml(this.config.date) + '</div>';
            }
            html += '</div>';

            if (this.config.animation === 'particle') {
                html += '<div class="ending-particles">';
                for (var i = 0; i < 50; i++) {
                    var delay = Math.random() * 5;
                    var left = Math.random() * 100;
                    var size = Math.random() * 6 + 4;
                    html += '<div class="ending-particle" style="left:' + left + '%; width:' + size + 'px; height:' + size + 'px; animation: particleFloat 8s ease-out ' + delay + 's infinite;"></div>';
                }
                html += '</div>';
            }

            this.endingOverlay.innerHTML = html;
        },

        playBgMusic: function() {
            if (!this.config.bgMusic) return;
            try {
                var audioUrl = this.config.bgMusic;
                if (audioUrl && !audioUrl.startsWith('data:') && !audioUrl.startsWith('http')) {
                    audioUrl = window.location.origin + '/' + audioUrl;
                }
                this.bgMusicAudio = new Audio(audioUrl);
                this.bgMusicAudio.volume = 0.5;
                this.bgMusicAudio.loop = true;
                this.bgMusicAudio.play().catch(function() {});
            } catch(e) {
                console.warn('播放背景音乐失败:', e);
            }
        },

        setVideoUrl: function(url, videoId) {
            this.endingVideoUrl = url;
            this.endingVideoId = videoId || null;
            try {
                localStorage.setItem('endingVideoUrl', url);
                if (videoId) {
                    localStorage.setItem('endingVideoId', videoId);
                }
            } catch(e) {}
        }
    };

    // ==================== 初始化 ====================
    initTimeline();
    initVolumeControl();
    startPhaseWindowSync();
    loadVideoList('opening');
    initKeyboardControls();
    EndingManager.init();
})();
</script>

<?php require_once 'footer.php'; ?>
