@props([
    'video',
    'nextVideo'      => null,
    'previousVideo'  => null,
    'playlist'       => null,
    'playlistVideos' => null,
])

@php
    // Force 16:9 for every video — orientation classes intentionally disabled
    $orientationClass = '';
    $hlsUrl  = $video->has_hls ? route('videos.hls', ['video' => $video, 'file' => 'master.m3u8']) : null;
    $mp4Url  = route('videos.stream', $video) . '?v=' . $video->updated_at->timestamp;
    $nextUrl = $nextVideo  && $playlist ? route('videos.show', $nextVideo) .'?playlist='.$playlist->share_token  : null;
    $prevUrl = $previousVideo && $playlist ? route('videos.show', $previousVideo).'?playlist='.$playlist->share_token : null;

    // Build playlist data for client-side shuffle
    $plData = null;
    if ($playlist && $playlistVideos && $playlistVideos->count() > 0) {
        $plData = [
            'playlistId'     => $playlist->share_token,
            'currentVideoId' => $video->id,
            'videos'         => $playlistVideos->map(fn($v) => [
                'id'      => $v->id,
                'showUrl' => route('videos.show', $v) . '?playlist=' . $playlist->id,
            ])->values()->all(),
        ];
    }
@endphp

{{-- ══════════════════════════════════════════════
     PLAYER WRAP — theater class toggled by JS
══════════════════════════════════════════════ --}}
<div class="ytp-wrap {{ $orientationClass }}" id="ytpWrap"
     data-progress-url="{{ route('videos.viewProgress', $video) }}"
     data-video-id="{{ $video->id }}">
<div class="ytp" id="videoContainer" tabindex="0">

    {{-- ── Video element ── --}}
    <video id="videoPlayer" playsinline preload="auto" autoplay muted></video>

    {{-- ── Slot for type-specific overlays (coach notes, etc.) ── --}}
    {{ $overlay ?? '' }}

    {{-- ── Play/pause ripple (double-tap seek feedback) ── --}}
    <div class="ytp-dbl-left"  id="ytpDblLeft"><i class="bi bi-arrow-counterclockwise"></i><span>10</span></div>
    <div class="ytp-dbl-right" id="ytpDblRight"><i class="bi bi-arrow-clockwise"></i><span>10</span></div>

    {{-- ── Spinner ── --}}
    <div class="ytp-spinner" id="ytpSpinner">
        <div class="ytp-spinner-circle"></div>
    </div>

    {{-- ── Play overlay (shows when paused, hides when playing) ── --}}
    <div class="ytp-large-play-btn" id="ytpLargePlay">
        <i class="bi bi-play-fill"></i>
    </div>

    {{-- ── Gradient fade at bottom ── --}}
    <div class="ytp-gradient-bottom"></div>

    {{-- ════════════════════════════════════════
         CONTROLS
    ════════════════════════════════════════ --}}
    <div class="ytp-chrome-bottom" id="ytpControls">

        {{-- Progress bar --}}
        <div class="ytp-progress-bar-container" id="ytpProgressContainer">
            <div class="ytp-progress-bar" id="ytpProgressBar">
                <div class="ytp-load-progress"  id="ytpBuffered"></div>
                <div class="ytp-play-progress"  id="ytpPlayed"></div>
                <div class="ytp-scrubber-container">
                    <div class="ytp-scrubber-button" id="ytpScrubber"></div>
                </div>
                <div class="ytp-hover-time" id="ytpHoverTime"></div>
            </div>
        </div>

        {{-- Chrome bottom row --}}
        <div class="ytp-chrome-controls">

            {{-- Left --}}
            <div class="ytp-left-controls">

                {{-- Play / Pause --}}
                <button class="ytp-button ytp-play-btn" id="ytpPlayBtn" title="Play (k)">
                    <svg class="ytp-svg-play"  viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
                    <svg class="ytp-svg-pause" viewBox="0 0 24 24" style="display:none"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
                </button>

                {{-- Prev / Next playlist --}}
                @if($previousVideo || ($playlist && $playlistVideos && $playlistVideos->count() > 1))
                <button class="ytp-button" id="ytpPrevBtn" title="Previous"
                    onclick="navigatePrev()">
                    <svg viewBox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6 8.5 6V6z"/></svg>
                </button>
                @endif
                @if($nextVideo || ($playlist && $playlistVideos && $playlistVideos->count() > 1))
                <button class="ytp-button" id="ytpNextBtn" title="Next (shift+n)"
                    onclick="navigateNext()">
                    <svg viewBox="0 0 24 24"><path d="m6 18 8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
                </button>
                @endif

                {{-- Volume --}}
                <div class="ytp-volume-area" id="ytpVolumeArea">
                    <button class="ytp-button ytp-mute-btn" id="ytpMuteBtn" title="Mute (m)">
                        <svg class="ytp-svg-vol3" viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
                        <svg class="ytp-svg-vol2" viewBox="0 0 24 24" style="display:none"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/></svg>
                        <svg class="ytp-svg-vol1" viewBox="0 0 24 24" style="display:none"><path d="M7 9v6h4l5 5V4l-5 5H7z"/></svg>
                        <svg class="ytp-svg-vol0" viewBox="0 0 24 24" style="display:none"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z"/></svg>
                    </button>
                    <div class="ytp-volume-slider-wrap" id="ytpVolumeWrap">
                        <input type="range" class="ytp-volume-range" id="ytpVolume"
                               min="0" max="100" step="1" value="50">
                    </div>
                </div>

                {{-- Time display --}}
                <div class="ytp-time-display">
                    <span id="ytpCurrent">0:00</span>
                    <span class="ytp-time-sep"> / </span>
                    <span id="ytpDuration">0:00</span>
                </div>

            </div>{{-- /left --}}

            {{-- Right --}}
            <div class="ytp-right-controls">

                {{-- Settings --}}
                <div class="ytp-settings-wrap" id="ytpSettingsWrap">
                    <button class="ytp-button ytp-settings-btn" id="ytpSettingsBtn" title="Settings">
                        <svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94s-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
                    </button>
                    <div class="ytp-settings-panel" id="ytpSettingsPanel">
                        {{-- Mini player toggle — desktop-only, persisted in localStorage --}}
                        <div class="ytp-settings-item" id="ytpMiniToggleRow" onclick="(function(el){var on=!(window._ytpMiniEnabled&&window._ytpMiniEnabled());window._ytpMiniSetEnabled(on);el.querySelector('.ytp-settings-val').textContent=on?'On':'Off';})(this)">
                            <svg viewBox="0 0 24 24"><path d="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z"/></svg>
                            <span>Mini player</span>
                            <span class="ytp-settings-val">On</span>
                        </div>
                        <div class="ytp-settings-item" id="ytpSpeedRow">
                            <svg viewBox="0 0 24 24"><path d="M10 8v8l6-4-6-4zm6.5 4A6.5 6.5 0 1 1 9 6.04V4.02A8.5 8.5 0 1 0 18.5 12H16.5z"/></svg>
                            <span>Playback speed</span>
                            <span class="ytp-settings-val" id="ytpSpeedLabel">Normal</span>
                            <svg class="ytp-chevron" viewBox="0 0 24 24"><path d="M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
                        </div>
                        <div class="ytp-speed-panel" id="ytpSpeedPanel">
                            <div class="ytp-speed-back" id="ytpSpeedBack">
                                <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
                                Playback speed
                            </div>
                            @foreach([['0.25','0.25'],['0.5','0.5'],['0.75','0.75'],['1','Normal'],['1.25','1.25'],['1.5','1.5'],['1.75','1.75'],['2','2']] as [$val,$label])
                            <div class="ytp-speed-option {{ $val === '1' ? 'active' : '' }}" data-speed="{{ $val }}">
                                <svg class="ytp-speed-check" viewBox="0 0 24 24"><path d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
                                {{ $label }}
                            </div>
                            @endforeach
                        </div>
                        {{-- Autoplay toggle (playlist only) --}}
                        @if($playlist)
                        <div class="ytp-settings-item ytp-toggle-row" id="ytpAutoplayRow">
                            <svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg>
                            <span>Autoplay</span>
                            <span class="ytp-settings-val ytp-toggle-val" id="ytpAutoplayVal">On</span>
                            <div class="ytp-toggle-switch" id="ytpAutoplaySwitch"><div class="ytp-toggle-thumb"></div></div>
                        </div>
                        {{-- Shuffle toggle (playlist with >1 video) --}}
                        @if($playlistVideos && $playlistVideos->count() > 1)
                        <div class="ytp-settings-item ytp-toggle-row" id="ytpShuffleRow">
                            <svg viewBox="0 0 24 24"><path d="M10.59 9.17 5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/></svg>
                            <span>Shuffle</span>
                            <span class="ytp-settings-val ytp-toggle-val" id="ytpShuffleVal">Off</span>
                            <div class="ytp-toggle-switch" id="ytpShuffleSwitch"><div class="ytp-toggle-thumb"></div></div>
                        </div>
                        @endif
                        @endif
                    </div>
                </div>

                {{-- Loop — outside gear, immediately to its right --}}
                <button class="ytp-button ytp-loop-btn" id="ytpLoopBtn" title="Loop">
                    <svg viewBox="0 0 24 24"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg>
                </button>

                {{-- Picture-in-Picture --}}
                <button class="ytp-button ytp-pip-btn" id="ytpPipBtn" title="Miniplayer (i)">
                    <svg viewBox="0 0 24 24"><path d="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z"/></svg>
                </button>

                {{-- Theater mode --}}
                <button class="ytp-button ytp-theater-btn" id="ytpTheaterBtn" title="Theater mode (t)">
                    <svg class="ytp-svg-theater-off" viewBox="0 0 24 24"><path d="M19 6H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H5V8h14v8z"/></svg>
                    <svg class="ytp-svg-theater-on" viewBox="0 0 24 24" style="display:none"><path d="M19 7H5c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2zm0 8H5V9h14v6z"/></svg>
                </button>

                {{-- Fullscreen --}}
                <button class="ytp-button ytp-fs-btn" id="ytpFsBtn" title="Full screen (f)">
                    <svg class="ytp-svg-fs-enter" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
                    <svg class="ytp-svg-fs-exit" viewBox="0 0 24 24" style="display:none"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg>
                </button>

            </div>{{-- /right --}}
        </div>{{-- /chrome-controls --}}
    </div>{{-- /chrome-bottom --}}

</div>{{-- /ytp --}}
</div>{{-- /ytp-wrap --}}


@once
<style>
/* ══════════════════════════════════════════════════════════
   YTP PLAYER — YouTube-identical custom player
══════════════════════════════════════════════════════════ */
.ytp-wrap {
    position: relative;
    width: 100%;
    background: #000;
    border-radius: 12px;
    overflow: hidden;
    /* default aspect ratio; overridden per orientation */
    aspect-ratio: 16/9;
    max-height: 70vh;
}
.ytp-wrap.portrait  { aspect-ratio: 9/16; max-height: 80vh; width: auto; max-width: 100%; margin: 0 auto; }
.ytp-wrap.square    { aspect-ratio: 1/1;  max-height: 75vh; max-width: 75vh; margin: 0 auto; }
.ytp-wrap.ultrawide { aspect-ratio: 21/9; max-height: 65vh; }

/* Theater mode */
.ytp-wrap.theater {
    max-height: 80vh;
    aspect-ratio: unset;
    height: 80vh;
    border-radius: 0;
}

/* Fullscreen */
.ytp-wrap.ytp-fullscreen {
    position: fixed !important;
    inset: 0;
    z-index: 99999;
    max-height: 100vh;
    height: 100vh;
    width: 100vw;
    border-radius: 0;
    aspect-ratio: unset;
    margin: 0 !important;
}

/* ── Inner player ── */
.ytp {
    position: relative;
    width: 100%;
    height: 100%;
    background: #000;
    cursor: pointer;
    outline: none;
    user-select: none;
    overflow: hidden;
    font-family: Roboto, Arial, sans-serif;
}
.ytp:focus { outline: none; }

/* ── Video element ── */
#videoPlayer {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #000;
}

/* ── Gradient bottom ── */
.ytp-gradient-bottom {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 98px;
    background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.7));
    pointer-events: none;
    transition: opacity .25s;
}

/* ── Large play overlay ── */
.ytp-large-play-btn {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s;
}
.ytp-large-play-btn i {
    font-size: 72px;
    color: rgba(255,255,255,.9);
    text-shadow: 0 0 30px rgba(0,0,0,.6);
}
.ytp-large-play-btn.visible { opacity: 1; }

/* ── Spinner ── */
.ytp-spinner {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s;
}
.ytp-spinner.active { opacity: 1; }
.ytp-spinner-circle {
    width: 48px;
    height: 48px;
    border: 4px solid rgba(255,255,255,.2);
    border-top-color: #fff;
    border-radius: 50%;
    animation: ytpSpin .8s linear infinite;
}
@keyframes ytpSpin { to { transform: rotate(360deg); } }

/* ── Double-tap feedback ── */
.ytp-dbl-left, .ytp-dbl-right {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 30%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    color: #fff;
    font-size: 14px;
    font-weight: 500;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s;
    border-radius: 50%;
}
.ytp-dbl-left  { left: 0; }
.ytp-dbl-right { right: 0; }
.ytp-dbl-left i, .ytp-dbl-right i { font-size: 40px; }
.ytp-dbl-left.show, .ytp-dbl-right.show { opacity: 1; }

/* ══ CONTROLS ══ */
.ytp-chrome-bottom {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 0 12px 8px;
    transition: opacity .25s, transform .25s;
    transform: translateY(0);
}
/* hidden state */
.ytp.controls-hidden .ytp-chrome-bottom { opacity: 0; transform: translateY(4px); pointer-events: none; }
.ytp.controls-hidden .ytp-gradient-bottom { opacity: 0; }

/* ── Progress bar ── */
.ytp-progress-bar-container {
    padding: 4px 0;
    cursor: pointer;
    margin-bottom: 4px;
}
.ytp-progress-bar {
    position: relative;
    height: 3px;
    background: rgba(255,255,255,.2);
    border-radius: 2px;
    transition: height .1s;
}
.ytp-progress-bar-container:hover .ytp-progress-bar,
.ytp-progress-bar.dragging { height: 5px; }

.ytp-load-progress {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    background: rgba(255,255,255,.4);
    border-radius: 2px;
    width: 0;
    pointer-events: none;
}
.ytp-play-progress {
    position: absolute;
    top: 0; left: 0; bottom: 0;
    background: #f00;
    border-radius: 2px;
    width: 0;
    pointer-events: none;
}
.ytp-scrubber-container {
    position: absolute;
    top: 50%;
    width: 0;
    pointer-events: none;
}
.ytp-scrubber-button {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: #f00;
    /* Center the knob on the track: -50% X sits it on the play head, -50% Y keeps it
       vertically centered on the bar's mid-line (container top:50%) at any bar height. */
    transform: translate(-50%, -50%) scale(0);
    transition: transform .1s;
}
.ytp-progress-bar-container:hover .ytp-scrubber-button,
.ytp-progress-bar.dragging .ytp-scrubber-button { transform: translate(-50%, -50%) scale(1); }

.ytp-hover-time {
    position: absolute;
    bottom: 16px;
    background: rgba(28,28,28,.9);
    color: #fff;
    font-size: 12px;
    padding: 3px 6px;
    border-radius: 4px;
    pointer-events: none;
    opacity: 0;
    transform: translateX(-50%);
    white-space: nowrap;
    transition: opacity .1s;
}
.ytp-progress-bar-container:hover .ytp-hover-time { opacity: 1; }

/* ── Chrome controls row ── */
.ytp-chrome-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 36px;
}
.ytp-left-controls, .ytp-right-controls {
    display: flex;
    align-items: center;
    gap: 4px;
}

/* ── Buttons ── */
.ytp-button {
    background: none;
    border: none;
    color: #fff;
    cursor: pointer;
    padding: 0;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: background .15s;
    flex-shrink: 0;
}
.ytp-button:hover { background: rgba(255,255,255,.1); }
.ytp-button svg { width: 22px; height: 22px; fill: #fff; pointer-events: none; }
.ytp-button:focus { outline: none; }

/* play btn slightly larger icon */
.ytp-play-btn svg { width: 26px; height: 26px; }

/* ── Volume area ── */
.ytp-volume-area {
    display: flex;
    align-items: center;
    gap: 0;
}
.ytp-volume-slider-wrap {
    overflow: hidden;
    width: 0;
    transition: width .2s;
    display: flex;
    align-items: center;
}
.ytp-volume-area:hover .ytp-volume-slider-wrap,
.ytp-volume-area:focus-within .ytp-volume-slider-wrap { width: 60px; }

.ytp-volume-range {
    -webkit-appearance: none;
    appearance: none;
    width: 52px;
    height: 3px;
    border-radius: 2px;
    background: linear-gradient(to right, #fff var(--vol, 50%), rgba(255,255,255,.3) var(--vol, 50%));
    outline: none;
    cursor: pointer;
    margin: 0 4px;
}
.ytp-volume-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: #fff;
    cursor: pointer;
}
.ytp-volume-range::-moz-range-thumb {
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: #fff;
    border: none;
    cursor: pointer;
}

/* ── Time display ── */
.ytp-time-display {
    font-size: 13px;
    color: #fff;
    white-space: nowrap;
    padding: 0 6px;
    line-height: 36px;
    font-weight: 400;
}
.ytp-time-sep { opacity: .6; margin: 0 2px; }

/* ── Settings panel ── */
.ytp-settings-wrap { position: relative; }
.ytp-settings-panel {
    display: none;
    position: absolute;
    bottom: 44px;
    right: 0;
    background: rgba(28,28,28,.95);
    border-radius: 12px;
    min-width: 200px;
    overflow: hidden;
    box-shadow: 0 4px 24px rgba(0,0,0,.6);
    z-index: 100;
}
.ytp-settings-panel.open { display: block; }
.ytp-settings-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    color: #fff;
    font-size: 13px;
    cursor: pointer;
    transition: background .15s;
    white-space: nowrap;
}
.ytp-settings-item:hover { background: rgba(255,255,255,.1); }
.ytp-settings-item svg { width: 20px; height: 20px; fill: #fff; flex-shrink: 0; }
.ytp-settings-item .ytp-settings-val {
    margin-left: auto;
    color: rgba(255,255,255,.7);
    font-size: 12px;
    margin-right: 4px;
}
.ytp-chevron { width: 18px; height: 18px; flex-shrink: 0; }

.ytp-speed-panel { display: none; }
.ytp-speed-panel.open { display: block; }
.ytp-speed-back {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    color: #fff;
    font-size: 13px;
    cursor: pointer;
    border-bottom: 1px solid rgba(255,255,255,.15);
    font-weight: 600;
}
.ytp-speed-back:hover { background: rgba(255,255,255,.1); }
.ytp-speed-back svg { width: 20px; height: 20px; fill: #fff; }
.ytp-speed-option {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    color: #fff;
    font-size: 13px;
    cursor: pointer;
    transition: background .15s;
}
.ytp-speed-option:hover { background: rgba(255,255,255,.1); }
.ytp-speed-check { width: 18px; height: 18px; fill: #fff; opacity: 0; flex-shrink: 0; }
.ytp-speed-option.active .ytp-speed-check { opacity: 1; }

/* ── Mobile ── */
@media (max-width: 768px) {
    .ytp-wrap { border-radius: 0; max-height: 56vw; margin: 0; width: 100%; }
    .ytp-wrap.portrait  { max-height: 75vh; width: auto; max-width: 100%; }
    .ytp-wrap.square    { max-height: 85vw; max-width: 85vw; }
    .ytp-wrap.ultrawide { max-height: 50vw; }
    .video-view-page .ytp-wrap ~ * { padding-left: 16px; padding-right: 16px; }
}

@media (max-width: 576px) {
    .ytp-button { width: 32px; height: 32px; }
    .ytp-button svg { width: 18px; height: 18px; }
    .ytp-time-display { font-size: 11px; padding: 0 4px; }
    .ytp-pip-btn, .ytp-theater-btn { display: none; }
}

/* Hide PIP if not supported – handled by JS */
.ytp-pip-btn.unsupported { display: none; }

/* Standalone loop button */
.ytp-loop-btn svg { opacity: .75; transition: opacity .15s, fill .15s; }
.ytp-loop-btn:hover svg { opacity: 1; }
.ytp-loop-btn.is-on svg { fill: #e61e1e; opacity: 1; }

/* Settings panel toggle rows */
.ytp-toggle-row { border-top: 1px solid rgba(255,255,255,.08); }
.ytp-toggle-row svg { opacity: .75; }
.ytp-toggle-val {
    font-size: 11px !important;
    opacity: .6;
    transition: color .15s, opacity .15s;
}
.ytp-toggle-row.is-on .ytp-toggle-val { color: #f00; opacity: 1; }
.ytp-toggle-row.is-on svg { fill: #f00; opacity: 1; }

/* Toggle switch pill */
.ytp-toggle-switch {
    width: 28px; height: 16px;
    background: rgba(255,255,255,.2);
    border-radius: 8px;
    position: relative;
    flex-shrink: 0;
    transition: background .2s;
    margin-left: 6px;
}
.ytp-toggle-row.is-on .ytp-toggle-switch { background: #e61e1e; }
.ytp-toggle-thumb {
    position: absolute;
    top: 2px; left: 2px;
    width: 12px; height: 12px;
    background: #fff;
    border-radius: 50%;
    transition: transform .2s;
    box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.ytp-toggle-row.is-on .ytp-toggle-thumb { transform: translateX(12px); }

/* ── Theater mode layout (landscape / ultrawide) ── */
.video-layout-container.theater-mode {
    flex-wrap: wrap;
}
.video-layout-container.theater-mode > .ytp-wrap {
    width: 100%;
    flex-shrink: 0;
}
.video-layout-container.theater-mode .yt-video-section {
    flex: 1;
    min-width: 0;
}
@media (max-width: 1300px) {
    .video-layout-container.theater-mode > .yt-sidebar-container { width: 300px; }
}
@media (min-width: 1301px) {
    .video-layout-container.theater-mode > .yt-sidebar-container { width: 400px; }
}

/* ── Theater mode layout (portrait) — stack everything below ── */
.video-layout-container.theater-mode-portrait {
    flex-direction: column;
}
.video-layout-container.theater-mode-portrait > .ytp-wrap {
    width: 100%;
    flex-shrink: 0;
}
.video-layout-container.theater-mode-portrait .yt-video-section,
.video-layout-container.theater-mode-portrait > .yt-sidebar-container {
    width: 100%;
}
</style>
@endonce

@once
<script>
/* ══════════════════════════════════════════════════════
   YTP PLAYER ENGINE
══════════════════════════════════════════════════════ */
(function () {

// ── DOM refs ──────────────────────────────────────────
const wrap        = document.getElementById('ytpWrap');
const player      = document.getElementById('videoContainer');
const video       = document.getElementById('videoPlayer');
const playBtn     = document.getElementById('ytpPlayBtn');
const muteBtn     = document.getElementById('ytpMuteBtn');
const volRange    = document.getElementById('ytpVolume');
const volWrap     = document.getElementById('ytpVolumeWrap');
const timeCur     = document.getElementById('ytpCurrent');
const timeDur     = document.getElementById('ytpDuration');
const controls    = document.getElementById('ytpControls');
const progCont    = document.getElementById('ytpProgressContainer');
const progBar     = document.getElementById('ytpProgressBar');
const buffered    = document.getElementById('ytpBuffered');
const played      = document.getElementById('ytpPlayed');
const scrubber    = document.getElementById('ytpScrubber');
const hoverTime   = document.getElementById('ytpHoverTime');
const spinner     = document.getElementById('ytpSpinner');
const largePlay   = document.getElementById('ytpLargePlay');
const dblLeft     = document.getElementById('ytpDblLeft');
const dblRight    = document.getElementById('ytpDblRight');
const settingsBtn = document.getElementById('ytpSettingsBtn');
const settingsPanel = document.getElementById('ytpSettingsPanel');
const speedRow    = document.getElementById('ytpSpeedRow');
const speedPanel  = document.getElementById('ytpSpeedPanel');
const speedBack   = document.getElementById('ytpSpeedBack');
const speedLabel  = document.getElementById('ytpSpeedLabel');
const speedOpts   = document.querySelectorAll('.ytp-speed-option');
const fsBtn       = document.getElementById('ytpFsBtn');
const pipBtn      = document.getElementById('ytpPipBtn');
const theaterBtn  = document.getElementById('ytpTheaterBtn');
const loopBtn     = document.getElementById('ytpLoopBtn');

// ── State ─────────────────────────────────────────────
let hideTimer           = null;
let isDragging          = false;
let isTheater           = false;
let isFullscreen        = false;
let currentSpeed        = 1;
let lastTap             = 0;
let tapTimer            = null;
let userSeeking         = false; // true from seekTo() until 'seeked' fires
let wasPlayingBeforeSeek = false; // remember play state so we can resume after seek

// ── HLS source ────────────────────────────────────────
const HLS_URL   = @json($hlsUrl);
const MP4_URL   = @json($mp4Url);
const NEXT_URL  = @json($nextUrl);
const PREV_URL  = @json($prevUrl);
const PL_DATA   = @json($plData);  // null when not in a playlist

// ── Playlist shuffle/autoplay state ───────────────────
const shuffleRow  = document.getElementById('ytpShuffleRow');
const shuffleVal  = document.getElementById('ytpShuffleVal');
const autoplayRow = document.getElementById('ytpAutoplayRow');
const autoplayVal = document.getElementById('ytpAutoplayVal');

const plId = PL_DATA?.playlistId ?? null;

let shuffleOn  = plId ? localStorage.getItem('ytpShuffleOn_'  + plId) === '1' : false;
let autoplayOn = plId ? localStorage.getItem('ytpAutoplay') !== '0' : false;

function applyShuffleState() {
    if (!shuffleRow) return;
    shuffleRow.classList.toggle('is-on', shuffleOn);
    if (shuffleVal) shuffleVal.textContent = shuffleOn ? 'On' : 'Off';
}
function applyAutoplayState() {
    if (!autoplayRow) return;
    autoplayRow.classList.toggle('is-on', autoplayOn);
    if (autoplayVal) autoplayVal.textContent = autoplayOn ? 'On' : 'Off';
}

if (shuffleRow) {
    applyShuffleState();
    shuffleRow.addEventListener('click', e => {
        e.stopPropagation();
        shuffleOn = !shuffleOn;
        localStorage.setItem('ytpShuffleOn_' + plId, shuffleOn ? '1' : '0');
        if (shuffleOn) buildShuffledOrder();
        else localStorage.removeItem('ytpShuffledOrder_' + plId);
        applyShuffleState();
    });
}
if (autoplayRow) {
    applyAutoplayState();
    autoplayRow.addEventListener('click', e => {
        e.stopPropagation();
        autoplayOn = !autoplayOn;
        localStorage.setItem('ytpAutoplay', autoplayOn ? '1' : '0');
        applyAutoplayState();
    });
}

function buildShuffledOrder() {
    if (!PL_DATA) return;
    const vids = PL_DATA.videos;
    const curIdx = vids.findIndex(v => v.id === PL_DATA.currentVideoId);
    const others = vids.map((_, i) => i).filter(i => i !== curIdx);
    for (let i = others.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [others[i], others[j]] = [others[j], others[i]];
    }
    const order = [curIdx, ...others];
    localStorage.setItem('ytpShuffledOrder_' + plId, JSON.stringify(order));
    return order;
}

function getShuffledNextUrl() {
    if (!PL_DATA) return NEXT_URL;
    const vids   = PL_DATA.videos;
    const curIdx = vids.findIndex(v => v.id === PL_DATA.currentVideoId);
    const stored = localStorage.getItem('ytpShuffledOrder_' + plId);
    const order  = stored ? JSON.parse(stored) : buildShuffledOrder();
    const posNow = order.indexOf(curIdx);
    const posNext = (posNow + 1) % order.length;
    // Don't wrap around unless loop-all
    if (!isLooping && posNext === 0) return null;
    return vids[order[posNext]]?.showUrl ?? null;
}
function getShuffledPrevUrl() {
    if (!PL_DATA) return PREV_URL;
    const vids   = PL_DATA.videos;
    const curIdx = vids.findIndex(v => v.id === PL_DATA.currentVideoId);
    const stored = localStorage.getItem('ytpShuffledOrder_' + plId);
    const order  = stored ? JSON.parse(stored) : buildShuffledOrder();
    const posNow = order.indexOf(curIdx);
    const posPrev = (posNow - 1 + order.length) % order.length;
    return vids[order[posPrev]]?.showUrl ?? PREV_URL;
}

window._ytpHls        = null;
window._ytpMasterHls  = @json($hlsUrl);
window._ytpMasterMp4  = @json($mp4Url);
window._ytpWasPlaying = false;
video.addEventListener('play',  function () { window._ytpWasPlaying = true;  });
video.addEventListener('pause', function () { window._ytpWasPlaying = false; });

function initSource() {
    video.muted    = true;
    video.autoplay = true;
    /* Resume handoff from the mini player: ?t=<sec> seeks the video to that
       position once metadata is ready. One-shot — only the initial load. */
    try {
        var _qs = new URLSearchParams(location.search);
        var _t  = parseInt(_qs.get('t') || '0', 10);
        if (_t > 0) {
            video.addEventListener('loadedmetadata', function () {
                if (_t < (video.duration || Infinity)) {
                    try { video.currentTime = _t; } catch (e) {}
                }
            }, { once: true });
        }
    } catch (e) {}
    if (HLS_URL && window.Hls && Hls.isSupported()) {
        window._ytpHls = new Hls({ startLevel: -1 });
        // Register MANIFEST_PARSED before loadSource to avoid cache race condition
        window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() {
            video.muted = true;
            video.play().catch(function(){});
        });
        window._ytpHls.loadSource(HLS_URL);
        window._ytpHls.attachMedia(video);
    } else if (HLS_URL && video.canPlayType('application/vnd.apple.mpegurl')) {
        // Native HLS (Safari) — set src then play on loadedmetadata
        video.src = HLS_URL;
        video.load();
        video.addEventListener('loadedmetadata', function() {
            video.muted = true;
            video.play().catch(function(){});
        }, { once: true });
    } else {
        // Plain MP4 — play on loadedmetadata (canplay/MANIFEST_PARSED don't apply)
        video.src = MP4_URL;
        video.load();
        video.addEventListener('loadedmetadata', function() {
            video.muted = true;
            video.play().catch(function(){});
        }, { once: true });
    }
}

// Reinitialize source after SPA transition — called by playlist overlay scripts
window._ytpLoadSource = function(hlsUrl, mp4Url) {
    window._ytpMasterHls = hlsUrl || null;
    window._ytpMasterMp4 = mp4Url || null;
    const _vol   = video.volume;
    const _muted = video.muted;
    if (window._ytpHls) { window._ytpHls.destroy(); window._ytpHls = null; }
    video.muted    = true;
    video.autoplay = true;
    if (hlsUrl && window.Hls && Hls.isSupported()) {
        window._ytpHls = new Hls({ startLevel: -1 });
        window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() {
            video.volume = _vol;
            video.muted  = _muted;
            video.play().catch(function(){});
        });
        window._ytpHls.loadSource(hlsUrl);
        window._ytpHls.attachMedia(video);
    } else if (hlsUrl && video.canPlayType('application/vnd.apple.mpegurl')) {
        video.src = hlsUrl;
        video.load();
        video.volume = _vol;
        video.addEventListener('loadedmetadata', function() {
            video.muted = _muted;
            video.play().catch(function(){});
        }, { once: true });
    } else {
        video.src = mp4Url;
        video.load();
        video.volume = _vol;
        video.addEventListener('loadedmetadata', function() {
            video.muted = _muted;
            video.play().catch(function(){});
        }, { once: true });
    }
};

// Load HLS.js from CDN then init
function loadHlsJs(cb) {
    if (window.Hls) { cb(); return; }
    const s = document.createElement('script');
    s.src = 'https://cdn.jsdelivr.net/npm/hls.js@1.5/dist/hls.min.js';
    s.onload = cb;
    document.head.appendChild(s);
}

// ── Helpers ───────────────────────────────────────────
function fmt(s) {
    if (!isFinite(s) || isNaN(s)) return '0:00';
    s = Math.floor(s);
    const h = Math.floor(s / 3600);
    const m = Math.floor((s % 3600) / 60);
    const sec = s % 60;
    if (h > 0) return h + ':' + String(m).padStart(2,'0') + ':' + String(sec).padStart(2,'0');
    return m + ':' + String(sec).padStart(2,'0');
}

function updatePlayIcon() {
    const playSvg  = playBtn.querySelector('.ytp-svg-play');
    const pauseSvg = playBtn.querySelector('.ytp-svg-pause');
    if (video.paused) {
        playSvg.style.display  = '';
        pauseSvg.style.display = 'none';
    } else {
        playSvg.style.display  = 'none';
        pauseSvg.style.display = '';
    }
}

function updateVolumeIcon() {
    const svgs = ['ytp-svg-vol3','ytp-svg-vol2','ytp-svg-vol1','ytp-svg-vol0'];
    const vol = video.volume;
    const muted = video.muted || vol === 0;
    svgs.forEach(c => muteBtn.querySelector('.'+c).style.display = 'none');
    if (muted)         muteBtn.querySelector('.ytp-svg-vol0').style.display = '';
    else if (vol > .5) muteBtn.querySelector('.ytp-svg-vol3').style.display = '';
    else if (vol > .1) muteBtn.querySelector('.ytp-svg-vol2').style.display = '';
    else               muteBtn.querySelector('.ytp-svg-vol1').style.display = '';
    volRange.value = muted ? 0 : Math.round(vol * 100);
    volRange.style.setProperty('--vol', (muted ? 0 : vol * 100) + '%');
}

function updateProgress() {
    if (!video.duration) return;
    const pct = (video.currentTime / video.duration) * 100;
    played.style.width = pct + '%';
    scrubber.parentElement.style.left = pct + '%';
    timeCur.textContent = fmt(video.currentTime);

    // buffered
    if (video.buffered.length > 0) {
        const bufEnd = video.buffered.end(video.buffered.length - 1);
        buffered.style.width = ((bufEnd / video.duration) * 100) + '%';
    }
}

// ── Controls visibility ───────────────────────────────
function showControls() {
    player.classList.remove('controls-hidden');
    resetHideTimer();
}
function resetHideTimer() {
    clearTimeout(hideTimer);
    if (!video.paused) {
        hideTimer = setTimeout(() => player.classList.add('controls-hidden'), 3000);
    }
}
function onActivity() { showControls(); }

// ── Play / Pause ──────────────────────────────────────
function togglePlay() {
    if (video.paused) {
        video.play();
        largePlay.classList.remove('visible');
    } else {
        video.pause();
        largePlay.classList.add('visible');
    }
}
playBtn.addEventListener('click', e => { e.stopPropagation(); togglePlay(); showControls(); });

// ── Click on video to play/pause, dbl-click seek/fullscreen ──
let clickTimer = null;
player.addEventListener('click', e => {
    if (e.target === settingsBtn || settingsPanel.contains(e.target)) return;
    if (progCont.contains(e.target)) return;
    clearTimeout(clickTimer);
    clickTimer = setTimeout(() => { togglePlay(); }, 200);
});
player.addEventListener('dblclick', e => {
    clearTimeout(clickTimer);
    const rect = player.getBoundingClientRect();
    const x = e.clientX - rect.left;
    if (x < rect.width * .4) {
        seekRelative(-10);
        flashDbl(dblLeft);
    } else if (x > rect.width * .6) {
        seekRelative(10);
        flashDbl(dblRight);
    } else {
        toggleFullscreen();
    }
});

function flashDbl(el) {
    el.classList.add('show');
    setTimeout(() => el.classList.remove('show'), 600);
}

// ── Mouse move / touch on player ─────────────────────
player.addEventListener('mousemove', onActivity);
player.addEventListener('touchstart', onActivity, { passive: true });

// ── Volume ────────────────────────────────────────────
muteBtn.addEventListener('click', e => {
    e.stopPropagation();
    video.muted = !video.muted;
    if (!video.muted && video.volume === 0) video.volume = 0.2;
    updateVolumeIcon();
    localStorage.setItem('ytpMuted', video.muted ? '1' : '0');
    showControls();
});
volRange.addEventListener('input', e => {
    e.stopPropagation();
    const v = parseInt(e.target.value) / 100;
    video.volume = v;
    video.muted  = v === 0;
    updateVolumeIcon();
    localStorage.setItem('ytpVolume', e.target.value);
    localStorage.setItem('ytpMuted', v === 0 ? '1' : '0');
});
volRange.addEventListener('click', e => e.stopPropagation());

// ── Progress bar scrubbing ────────────────────────────
function seekTo(pct) {
    if (!video.duration) return;
    if (!userSeeking) wasPlayingBeforeSeek = !video.paused;
    userSeeking = true;
    video.currentTime = pct * video.duration;
    updateProgress();
}
function progressPct(e) {
    const rect = progBar.getBoundingClientRect();
    return Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
}
function touchProgressPct(e) {
    const rect = progBar.getBoundingClientRect();
    return Math.max(0, Math.min(1, (e.touches[0].clientX - rect.left) / rect.width));
}

progCont.addEventListener('mousemove', e => {
    const pct = progressPct(e);
    hoverTime.textContent = fmt(pct * (video.duration || 0));
    hoverTime.style.left  = (pct * 100) + '%';
    if (isDragging) {
        seekTo(pct);
        played.style.width = (pct * 100) + '%';
        scrubber.parentElement.style.left = (pct * 100) + '%';
    }
});
progCont.addEventListener('mousedown', e => {
    e.preventDefault();
    isDragging = true;
    progBar.classList.add('dragging');
    seekTo(progressPct(e));
});
document.addEventListener('mouseup', () => {
    if (isDragging) { isDragging = false; progBar.classList.remove('dragging'); }
});
document.addEventListener('mousemove', e => {
    if (isDragging) {
        const rect = progBar.getBoundingClientRect();
        const pct  = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
        seekTo(pct);
    }
});
progCont.addEventListener('touchstart', e => {
    isDragging = true;
    progBar.classList.add('dragging');
    seekTo(touchProgressPct(e));
}, { passive: true });
progCont.addEventListener('touchmove', e => {
    if (isDragging) seekTo(touchProgressPct(e));
}, { passive: true });
progCont.addEventListener('touchend', e => { e.preventDefault(); isDragging = false; progBar.classList.remove('dragging'); });

// ── Settings panel ────────────────────────────────────
settingsBtn.addEventListener('click', e => {
    e.stopPropagation();
    const open = settingsPanel.classList.toggle('open');
    if (!open) { speedPanel.classList.remove('open'); speedRow.style.display = ''; }
    /* Sync the mini-player toggle row's label to the current preference each
       time the gear opens, so reloading the page or toggling from the music
       player keeps the indicator honest. */
    if (open) {
        const miniRow = document.getElementById('ytpMiniToggleRow');
        if (miniRow) {
            const v = miniRow.querySelector('.ytp-settings-val');
            const on = !window._ytpMiniEnabled || window._ytpMiniEnabled();
            if (v) v.textContent = on ? 'On' : 'Off';
        }
    }
    showControls();
    clearTimeout(hideTimer); // keep controls visible while settings open
});
document.addEventListener('click', e => {
    if (!document.getElementById('ytpSettingsWrap').contains(e.target)) {
        settingsPanel.classList.remove('open');
        speedPanel.classList.remove('open');
        speedRow.style.display = '';
    }
});
if (speedRow) {
    speedRow.addEventListener('click', e => {
        e.stopPropagation();
        speedRow.style.display = 'none';
        speedPanel.classList.add('open');
    });
}
if (speedBack) {
    speedBack.addEventListener('click', e => {
        e.stopPropagation();
        speedPanel.classList.remove('open');
        speedRow.style.display = '';
    });
}
speedOpts.forEach(opt => {
    opt.addEventListener('click', e => {
        e.stopPropagation();
        const speed = parseFloat(opt.dataset.speed);
        video.playbackRate = speed;
        currentSpeed = speed;
        speedOpts.forEach(o => o.classList.remove('active'));
        opt.classList.add('active');
        speedLabel.textContent = speed === 1 ? 'Normal' : speed;
        settingsPanel.classList.remove('open');
        speedPanel.classList.remove('open');
        speedRow.style.display = '';
    });
});

// ── Fullscreen ────────────────────────────────────────
function toggleFullscreen() {
    if (document.fullscreenElement) {
        document.exitFullscreen();
    } else {
        wrap.requestFullscreen && wrap.requestFullscreen();
    }
}
fsBtn.addEventListener('click', e => { e.stopPropagation(); toggleFullscreen(); showControls(); });
document.addEventListener('fullscreenchange', () => {
    isFullscreen = !!document.fullscreenElement;
    wrap.classList.toggle('ytp-fullscreen', isFullscreen);
    const enter = fsBtn.querySelector('.ytp-svg-fs-enter');
    const exit  = fsBtn.querySelector('.ytp-svg-fs-exit');
    enter.style.display = isFullscreen ? 'none' : '';
    exit.style.display  = isFullscreen ? '' : 'none';
    if (screen.orientation && screen.orientation.lock) {
        if (isFullscreen) screen.orientation.lock('landscape').catch(function(){});
        else              screen.orientation.unlock();
    }
});

// ── Theater mode ──────────────────────────────────────
if (theaterBtn) {
    theaterBtn.addEventListener('click', e => {
        e.stopPropagation();
        isTheater = !isTheater;
        wrap.classList.toggle('theater', isTheater);
        theaterBtn.querySelector('.ytp-svg-theater-off').style.display = isTheater ? 'none' : '';
        theaterBtn.querySelector('.ytp-svg-theater-on').style.display  = isTheater ? '' : 'none';

        const layout       = document.querySelector('.video-layout-container');
        const videoSection = document.querySelector('.yt-video-section');

        if (layout && videoSection) {
            const isPortrait = wrap.classList.contains('portrait');
            if (isTheater) {
                layout.insertBefore(wrap, layout.firstChild);
                // Portrait videos are tall/narrow — stack everything below.
                // Landscape/ultrawide — keep comments and sidebar side by side.
                layout.classList.add(isPortrait ? 'theater-mode-portrait' : 'theater-mode');
            } else {
                videoSection.insertBefore(wrap, videoSection.firstChild);
                layout.classList.remove('theater-mode', 'theater-mode-portrait');
            }
        }
        showControls();
    });
}

// ── Loop ──────────────────────────────────────────────
let isLooping = localStorage.getItem('ytpLoop') === '1';
video.loop = isLooping;
function applyLoopState() {
    if (!loopBtn) return;
    loopBtn.classList.toggle('is-on', isLooping);
    loopBtn.title = isLooping ? 'Loop: On' : 'Loop';
}
applyLoopState();
if (loopBtn) {
    loopBtn.addEventListener('click', e => {
        e.stopPropagation();
        isLooping = !isLooping;
        video.loop = isLooping;
        localStorage.setItem('ytpLoop', isLooping ? '1' : '0');
        applyLoopState();
        showControls();
    });
}

// ── Wake Lock (keep screen on while playing) ──────────
let wakeLock = null;
async function requestWakeLock() {
    if (!('wakeLock' in navigator) || wakeLock) return;
    try { wakeLock = await navigator.wakeLock.request('screen'); } catch(e) {}
}
function releaseWakeLock() {
    if (wakeLock) { wakeLock.release(); wakeLock = null; }
}
document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'visible' && !video.paused) requestWakeLock();
});

// ── Picture-in-Picture ────────────────────────────────
if (pipBtn) {
    if (!document.pictureInPictureEnabled) {
        pipBtn.classList.add('unsupported');
    } else {
        pipBtn.addEventListener('click', e => {
            e.stopPropagation();
            if (document.pictureInPictureElement) {
                document.exitPictureInPicture();
            } else {
                video.requestPictureInPicture().catch(() => {});
            }
            showControls();
        });
    }
}

// ── Video events ──────────────────────────────────────
video.addEventListener('play',     () => { updatePlayIcon(); resetHideTimer(); largePlay.classList.remove('visible'); requestWakeLock(); });
video.addEventListener('pause',    () => {
    if (userSeeking) return; // suppress mid-seek pause events
    updatePlayIcon(); showControls(); largePlay.classList.add('visible'); clearTimeout(hideTimer); releaseWakeLock();
});
video.addEventListener('seeked',   () => {
    userSeeking = false;
    if (wasPlayingBeforeSeek) {
        // Resume if the browser/HLS.js paused the video during seeking
        if (video.paused) video.play().catch(() => {});
        largePlay.classList.remove('visible');
        resetHideTimer();
    }
});
video.addEventListener('timeupdate', updateProgress);
video.addEventListener('progress',   updateProgress);
video.addEventListener('durationchange', () => { timeDur.textContent = fmt(video.duration); });
video.addEventListener('waiting',  () => spinner.classList.add('active'));
video.addEventListener('playing',  () => spinner.classList.remove('active'));
video.addEventListener('canplay',  () => spinner.classList.remove('active'));
function navigateNext() {
    const url = shuffleOn ? getShuffledNextUrl() : NEXT_URL;
    if (window._ytpNavOverride?.next) { window._ytpNavOverride.next(url); return; }
    if (url) window.location.href = url;
}
function navigatePrev() {
    const url = shuffleOn ? getShuffledPrevUrl() : PREV_URL;
    if (window._ytpNavOverride?.prev) { window._ytpNavOverride.prev(url); return; }
    if (url) window.location.href = url;
}
window._ytpNav = { next: navigateNext, prev: navigatePrev };

video.addEventListener('ended', () => {
    updatePlayIcon();
    largePlay.classList.add('visible');
    clearTimeout(hideTimer);
    releaseWakeLock();
    if (window._plOnVideoEnd) {
        window._plOnVideoEnd();
    } else if (autoplayOn || isLooping) {
        navigateNext();
    }
});
video.addEventListener('volumechange', updateVolumeIcon);

// ── Keyboard shortcuts ────────────────────────────────
document.addEventListener('keydown', e => {
    const tag = document.activeElement.tagName;
    if (tag === 'INPUT' || tag === 'TEXTAREA' || document.activeElement.isContentEditable) return;

    switch (e.key) {
        case ' ':
        case 'k': case 'K':
            e.preventDefault(); togglePlay(); showControls(); break;
        case 'm': case 'M':
            e.preventDefault();
            video.muted = !video.muted;
            if (!video.muted && video.volume === 0) video.volume = 0.5;
            updateVolumeIcon();
            localStorage.setItem('ytpMuted', video.muted ? '1' : '0');
            showControls(); break;
        case 'f': case 'F':
            e.preventDefault(); toggleFullscreen(); break;
        case 't': case 'T':
            e.preventDefault(); theaterBtn && theaterBtn.click(); break;
        case 'i': case 'I':
            e.preventDefault(); pipBtn && !pipBtn.classList.contains('unsupported') && pipBtn.click(); break;
        case 'j': case 'J':
            e.preventDefault(); seekRelative(-10); flashDbl(dblLeft); showControls(); break;
        case 'l': case 'L':
            e.preventDefault(); seekRelative(10); flashDbl(dblRight); showControls(); break;
        case 'ArrowLeft':
            e.preventDefault(); seekRelative(-5); showControls(); break;
        case 'ArrowRight':
            e.preventDefault(); seekRelative(5); showControls(); break;
        case 'ArrowUp':
            e.preventDefault();
            video.volume = Math.min(1, video.volume + 0.05);
            video.muted  = false;
            updateVolumeIcon();
            showControls(); break;
        case 'ArrowDown':
            e.preventDefault();
            video.volume = Math.max(0, video.volume - 0.05);
            updateVolumeIcon();
            showControls(); break;
        default:
            if (e.key >= '0' && e.key <= '9') {
                e.preventDefault();
                if (video.duration) video.currentTime = (parseInt(e.key) / 10) * video.duration;
                showControls();
            }
    }
});

function seekRelative(secs) {
    if (!video.duration) return;
    video.currentTime = Math.max(0, Math.min(video.duration, video.currentTime + secs));
    updateProgress();
}

// ── Init ──────────────────────────────────────────────
function init() {
    const savedVol   = localStorage.getItem('ytpVolume');
    const savedMuted = localStorage.getItem('ytpMuted');
    video.volume = savedVol ? parseInt(savedVol) / 100 : 0.5;
    video.muted  = true; // start muted so autoplay is never blocked
    updateVolumeIcon();

    // After first play, restore user's preferred mute state
    video.addEventListener('playing', function restoreSound() {
        if (savedMuted !== '1') {
            video.muted = false;
            updateVolumeIcon();
        }
    }, { once: true });

    // If user returned from mini-player, seek to the saved timestamp
    const miniRaw = sessionStorage.getItem('ytpMiniState');
    let miniSeekTime = 0;
    if (miniRaw) {
        try {
            const ms = JSON.parse(miniRaw);
            if (ms && ms.time > 0) miniSeekTime = ms.time;
        } catch(e) {}
        sessionStorage.removeItem('ytpMiniState');
    }

    // canplay is a belt-and-suspenders fallback — also mute before play
    video.addEventListener('canplay', function autoStart() {
        video.removeEventListener('canplay', autoStart);
        if (miniSeekTime > 0) {
            video.currentTime = miniSeekTime;
            miniSeekTime = 0;
        }
        if (video.paused) {
            video.muted = true;
            video.play().catch(() => {});
        }
    });

    // Update duration when metadata is ready
    video.addEventListener('loadedmetadata', () => {
        timeDur.textContent = fmt(video.duration);
    });

    // Load source
    loadHlsJs(initSource);

    // Initial state
    largePlay.classList.add('visible');
    showControls();

    // Scroll-based mini player: watch when #ytpWrap leaves the viewport.
    // Desktop-only — on mobile the fixed bottom-nav + locked scroll model
    // make a floating overlay disruptive.
    if (window.IntersectionObserver && window._miniPlayer && window.innerWidth > 768) {
        var _scrollRoot  = null; /* desktop: window scrolls */
        var _scrollMiniOn = false; /* guard against rapid toggling / initial fire */
        new IntersectionObserver(function (entries) {
            var e0 = entries[0];
            /* Only activate after the video has actually started playing (_ytpWasPlaying).
               Using !video.paused was unreliable: autoplay fires asynchronously and the
               initial IntersectionObserver callback could run before HLS.js even attaches,
               teleporting the element before it ever played in the main player. */
            var miniAllowed = !window._ytpMiniEnabled || window._ytpMiniEnabled();
            if (!e0.isIntersecting && !_scrollMiniOn && window._ytpWasPlaying && miniAllowed && !window._miniPlayer.isNavMode()) {
                _scrollMiniOn = true;
                window._miniPlayer.activateScroll(
                    document.title.replace(/\s*\|.*$/, '').trim(),
                    window.location.href
                );
            } else if (e0.isIntersecting && _scrollMiniOn) {
                _scrollMiniOn = false;
                window._miniPlayer.deactivateScroll();
            }
        }, { root: _scrollRoot, threshold: 0.15 }).observe(wrap);

        /* User clicked the X on the mini while still on the video page —
           reset the flag so a subsequent scroll-away re-activates it. */
        window.addEventListener('miniplayer:scroll-closed', function () {
            _scrollMiniOn = false;
        });
    }
}

document.addEventListener('DOMContentLoaded', init);

// ── View-progress heartbeat ──────────────────────────────
(function() {
    const _csrf = document.querySelector('meta[name="csrf-token"]')?.content || '';
    let _hbVideoId = null;
    let _hbUrl     = null;
    let _hbLast    = 0;
    let _hbCompleted = false;

    function _refreshHbTarget() {
        const wrap = document.getElementById('ytpWrap');
        if (!wrap) return;
        const vid = parseInt(wrap.dataset.videoId || '0', 10);
        if (vid && vid !== _hbVideoId) {
            _hbVideoId   = vid;
            _hbUrl       = wrap.dataset.progressUrl || null;
            _hbLast      = 0;
            _hbCompleted = false;
        }
    }
    function _sendHb(completed) {
        if (!_hbUrl) return;
        const v = document.getElementById('videoPlayer');
        if (!v) return;
        const cur = Math.floor(v.currentTime || 0);
        if (!completed && cur <= _hbLast) return;
        const body = new URLSearchParams({ watched_seconds: cur, completed: completed ? '1' : '0' });
        try {
            if (completed && navigator.sendBeacon) {
                const blob = new Blob([body.toString() + '&_token=' + _csrf], { type: 'application/x-www-form-urlencoded' });
                navigator.sendBeacon(_hbUrl, blob);
            } else {
                fetch(_hbUrl, {
                    method: 'POST',
                    headers: { 'X-CSRF-TOKEN': _csrf, 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' },
                    body: body.toString(),
                    keepalive: true,
                    credentials: 'same-origin',
                }).catch(function() {});
            }
            _hbLast = cur;
            if (completed) _hbCompleted = true;
        } catch (e) {}
    }
    function _hbBind() {
        const v = document.getElementById('videoPlayer');
        if (!v || v._hbBound) return;
        v._hbBound = true;
        _refreshHbTarget();
        v.addEventListener('ended',    () => _sendHb(true));
        v.addEventListener('pause',    () => _sendHb(false));
    }
    setInterval(function() { _refreshHbTarget(); if (!_hbCompleted) _sendHb(false); }, 5000);
    document.addEventListener('visibilitychange', function() {
        if (document.visibilityState === 'hidden') _sendHb(false);
    });
    window.addEventListener('pagehide', function() { _sendHb(false); });
    document.addEventListener('DOMContentLoaded', _hbBind);
    // also rebind after SPA source swaps
    const _origLoad = window._ytpLoadSource;
    if (typeof _origLoad === 'function') {
        window._ytpLoadSource = function() {
            _refreshHbTarget();
            return _origLoad.apply(this, arguments);
        };
    }
})();

})();
</script>
@endonce
