X Reels ++ NSFW ecchi ver. (Fork)

Transforms the X/Twitter feed into a full-screen viewer with keyboard & mouse wheel navigation, smart auto-scroll, playback speed control, and a robust follow-and-return action.

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         X Reels ++ NSFW ecchi ver. (Fork)
// @name:en         X Reels ++ NSFW ecchi ver. (Fork)
// @namespace    http://tampermonkey.net/
// @version      28.8.19
// @description  Transforms the X/Twitter feed into a full-screen viewer with keyboard & mouse wheel navigation, smart auto-scroll, playback speed control, and a robust follow-and-return action.
// @description:en  Transforms the X/Twitter feed into a full-screen viewer with keyboard & mouse wheel navigation, smart auto-scroll, playback speed control, and a robust follow-and-return action.
// @author       Kristijan1001
// @match        https://twitter.com/*
// @match        https://x.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=x.com
// @grant        none
// @license      MIT
// ==/UserScript==

// Fork Attribution
// - Original Author: Kristijan1001 (https://greasyfork.org/ja/users/916612-kristijan1001)
// - Fork/Publisher: sukimono (https://greasyfork.org/ja/users/1557622-sukimono)
// This fork adds UI/UX and performance improvements while keeping the MIT license.

(function() {
    'use strict';

    class TikTokFeed {
        constructor() {
            this.activePost = { id: null, element: null, mediaItems: [], currentMediaIndex: 0 };
            this.activePlaceholder = null;
            this.activeDisplayedMediaElement = null;
            this.isActive = false;
            this.isNavigating = false;
            this.lastScrollTime = 0;
            this.container = null;
            this.likeButton = null;
            this.followButton = null;
            this.exitButton = null;
            this.scrollUpButton = null;
            this.scrollDownButton = null;
            this.savedScrollPosition = 0;
            this.mutationObserver = null;
            this.isReturningFromFollow = false;
            this.savedOverflowY = document.documentElement.style.overflowY || '';
            this.dateButton = null;
            this.datePanel = null;

            // Updated: Parse potentially existing smart value (-1)
            this.autoScrollDelay = parseInt(localStorage.getItem('xreels_autoScrollDelay') || '0', 10);
            this.autoScrollTimeoutId = null;
            this.videoEndedListener = null; // New listener for Smart mode
            this.autoScrollWatchdogId = null; // Watchdog timer for auto-scroll

            this.hideControlsTimeoutId = null;
            this.hideInfoTimeoutId = null;
            this.galleryIndicatorTimeout = null;

            this.savedVolume = parseFloat(localStorage.getItem('xreels_volume') || '0.7');
            this.savedMuted = localStorage.getItem('xreels_muted') === 'true';
            this.savedPlaybackRate = parseFloat(localStorage.getItem('xreels_playbackRate')) || 1.0;

            this.boundHandleWheel = null;
            this.boundHandleKeydown = null;
            this.boundHandlePopState = null;

            // マウス移動によるオートプレイ保留管理
            this.pointerMoving = false;
            this.pointerIdleTimeoutId = null;
            this.deferredAutoAdvance = null;
            this.pointerMoveRafId = null;

            // ぼかしフィルターの状態
            this.blurEnabled = localStorage.getItem('xreels_blur') === 'true';

            // パネルとメインテキストの共通スケール設定(旧キーからのフォールバックも考慮)
            const legacyTextSize = localStorage.getItem('xreels_prevTextSize') || localStorage.getItem('xreels_nextTextSize');
            const legacyImagePx = parseInt(localStorage.getItem('xreels_prevImageSize') || localStorage.getItem('xreels_nextImageSize') || '0', 10);
            const legacyImagePercent = legacyImagePx ? Math.min(100, Math.max(20, Math.round((legacyImagePx / 350) * 100))) : null;

            this.panelSettings = {
                textSize: parseInt(localStorage.getItem('xreels_textSize') || legacyTextSize || '11', 10),
                imagePercent: parseInt(localStorage.getItem('xreels_imagePercent') || (legacyImagePercent ? legacyImagePercent.toString() : '60'), 10)
            };
            
            this.hiddenPosts = []; // フィルタリングで非表示にしたポストを記録
            this.uiHiddenMode = false; // UI非表示モード
            this.hideUIButton = null; // UI非表示ボタン
        }

        init() {
            console.log('🔥 X Reels Initializing...');
            if ('scrollRestoration' in history) {
                history.scrollRestoration = 'manual';
                console.log('History scroll restoration set to manual.');
            }
            this.setupEventListeners();
            setTimeout(() => this.addManualTrigger(), 1000);
            
            // 親ウィンドウでのみ全iframe起動ボタンを追加
            if (window.self === window.top) {
                setTimeout(() => this.addBulkLaunchButton(), 1500);
            }
        }

        addBulkLaunchButton() {
            let bulkButton = document.getElementById('xreels-bulk-launch');
            if (bulkButton) bulkButton.remove();

            bulkButton = document.createElement('div');
            bulkButton.id = 'xreels-bulk-launch';
            bulkButton.innerHTML = `
                <div class="bulk-icon">🚀</div>
                <div class="bulk-text">Launch All X Reels</div>
            `;

            bulkButton.style.cssText = `
                position: fixed; top: 70px; right: 20px; z-index: 1000001;
                display: flex; align-items: center; gap: 3px;
                padding: 4px 8px;
                background: linear-gradient(135deg, rgba(29, 161, 242, 0.5) 0%, rgba(26, 145, 218, 0.5) 100%);
                border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 6px;
                cursor: pointer; user-select: none;
                box-shadow: 0 2px 8px rgba(29, 161, 242, 0.25);
                backdrop-filter: blur(8px);
                transition: all 0.3s ease;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
                color: white; font-size: 10px; font-weight: 600;
                opacity: 0.6;
            `;

            const style = document.createElement('style');
            style.textContent = `
                #xreels-bulk-launch:hover {
                    transform: translateY(-1px) scale(1.02);
                    box-shadow: 0 4px 16px rgba(29, 161, 242, 0.4);
                    opacity: 1;
                }
                #xreels-bulk-launch:active {
                    transform: translateY(0) scale(0.98);
                }
                #xreels-bulk-launch .bulk-icon {
                    font-size: 12px;
                    animation: rocketPulse 1.5s ease-in-out infinite;
                }
                @keyframes rocketPulse {
                    0%, 100% { transform: scale(1); }
                    50% { transform: scale(1.1); }
                }
            `;
            if (!document.getElementById('xreels-bulk-style')) {
                style.id = 'xreels-bulk-style';
                document.head.appendChild(style);
            }

            bulkButton.addEventListener('click', () => this.launchAllIframeXReels());
            document.body.appendChild(bulkButton);
        }

        launchAllIframeXReels() {
            const iframes = document.querySelectorAll('iframe');
            let launchedCount = 0;

            iframes.forEach(iframe => {
                try {
                    const iframeWindow = iframe.contentWindow;
                    if (iframeWindow && iframeWindow.location.href.includes('x.com')) {
                        const trigger = iframeWindow.document.getElementById('tiktok-trigger');
                        if (trigger) {
                            trigger.click();
                            launchedCount++;
                        }
                    }
                } catch (e) {
                    console.log('Cannot access iframe:', e);
                }
            });

            console.log(`Launched X Reels in ${launchedCount} iframes`);
            
            // フィードバック表示
            if (launchedCount > 0) {
                const feedback = document.createElement('div');
                feedback.textContent = `Launched ${launchedCount} X Reels`;
                feedback.style.cssText = `
                    position: fixed; bottom: 80px; right: 20px; z-index: 1000002;
                    padding: 10px 16px; background: rgba(29, 161, 242, 0.9);
                    color: white; border-radius: 8px;
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
                    font-size: 13px; font-weight: 600;
                    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
                    animation: fadeInOut 2s ease;
                `;
                document.body.appendChild(feedback);
                setTimeout(() => feedback.remove(), 2000);
            }
        }

        addManualTrigger() {
            // iframe内でも動作するようにチェックを削除
            // if (window.self !== window.top) return;
            // if (window.location !== window.parent.location) return;

            let trigger = document.getElementById('tiktok-trigger');
            if (trigger) trigger.remove();

            trigger = document.createElement('div');
            trigger.id = 'tiktok-trigger';
            trigger.innerHTML = `
                <div class="trigger-icon">
                    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                        <path d="M19.59 10.59L12 3l-7.59 7.59-1.42-1.41L12 0l8.99 9-1.4 1.59z" fill="currentColor"/>
                        <path d="M12 3v18M3 12h18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                    </svg>
                </div>
                <div class="trigger-content">
                    <div class="trigger-title">X Reels</div>
                    <div class="trigger-subtitle">Press X</div>
                </div>
            `;

            trigger.style.cssText = `
                position: fixed; top: 20px; right: 20px; z-index: 1000000;
                display: flex; align-items: center; gap: 8px;
                padding: 8px 12px;
                background: linear-gradient(135deg, rgba(15, 15, 15, 0.7) 0%, rgba(30, 30, 30, 0.7) 100%);
                border: 1px solid rgba(255, 0, 80, 0.3); border-radius: 12px;
                cursor: pointer; user-select: none;
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.05) inset, 0 0 15px rgba(255, 0, 80, 0.15);
                backdrop-filter: blur(15px) saturate(160%);
                transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
                transform: translateY(0) scale(1); opacity: 0.8;
            `;

            const style = document.createElement('style');
            style.textContent = `
                #tiktok-trigger { animation: slideInDown 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
                #tiktok-trigger .trigger-icon { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #ff0050 0%, #ff4081 100%); border-radius: 8px; flex-shrink: 0; box-shadow: 0 3px 12px rgba(255, 0, 80, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1) inset; transition: all 0.3s ease; }
                #tiktok-trigger .trigger-icon svg { color: white; width: 18px; height: 18px; animation: iconPulse 2.5s ease-in-out infinite; }
                #tiktok-trigger .trigger-content { display: flex; flex-direction: column; gap: 1px; }
                #tiktok-trigger .trigger-title { color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 12px; font-weight: 700; letter-spacing: 0.3px; line-height: 1.2; }
                #tiktok-trigger .trigger-subtitle { color: rgba(255, 255, 255, 0.55); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 10px; font-weight: 500; letter-spacing: 0.2px; }
                #tiktok-trigger:hover { transform: translateY(-2px) scale(1.02); border-color: rgba(255, 0, 80, 0.5); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1) inset, 0 0 20px rgba(255, 0, 80, 0.25); opacity: 1; }
                #tiktok-trigger:hover .trigger-icon { transform: scale(1.08) rotate(5deg); box-shadow: 0 4px 14px rgba(255, 0, 80, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.2) inset; }
                #tiktok-trigger:hover .trigger-icon svg { animation: iconSpin 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
                #tiktok-trigger:active { transform: translateY(-1px) scale(0.98); transition: all 0.1s ease; }
                #tiktok-trigger::before { content: ''; position: absolute; inset: -2px; background: linear-gradient(135deg, #ff0050, #ff4081, #ff0050); border-radius: 14px; opacity: 0; transition: opacity 0.3s ease; z-index: -1; filter: blur(8px); }
                #tiktok-trigger:hover::before { opacity: 0.25; animation: gradientRotate 2s linear infinite; }
                @keyframes slideInDown { from { opacity: 0; transform: translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } }
                @keyframes iconPulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.9; } }
                @keyframes iconSpin { from { transform: rotate(0deg) scale(1); } to { transform: rotate(360deg) scale(1.08); } }
                @keyframes gradientRotate { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
                @media (max-width: 768px) { #tiktok-trigger { top: 12px; right: 12px; padding: 8px 12px; gap: 8px; } #tiktok-trigger .trigger-icon { width: 32px; height: 32px; } #tiktok-trigger .trigger-icon svg { width: 16px; height: 16px; } #tiktok-trigger .trigger-title { font-size: 12px; } #tiktok-trigger .trigger-subtitle { font-size: 9px; } }
            `;
            if (!document.getElementById('xreels-pulse-style')) {
                style.id = 'xreels-pulse-style';
                document.head.appendChild(style);
            }

            trigger.addEventListener('click', () => this.startFeed());
            document.body.appendChild(trigger);
        }

        startFeed() {
            if (this.isActive) return;

            this.lastScrollTime = Date.now();

            // フィルタリング条件を満たさないポストを全部非表示にする
            this.hideNonFilteredPosts();

            const firstPostWithMedia = this.findCentralMediaArticle();
            if (!firstPostWithMedia) {
                console.error('❌ No media found on screen. Scroll down and try again.');
                return;
            }
            if (this.savedOverflowY === null) {
                this.savedOverflowY = document.documentElement.style.overflowY || '';
            }
            document.documentElement.style.overflowY = 'scroll';
            this.isActive = true;
            this.createContainer();
            this.navigateToPost(firstPostWithMedia);
            const trigger = document.getElementById('tiktok-trigger');
            if (trigger) trigger.style.display = 'none';
        }

        exit() {
            console.log('👋 Exiting feed...');
            this.isActive = false;
            this.stopAutoScrollTimer();
            if (this.pointerIdleTimeoutId) {
                clearTimeout(this.pointerIdleTimeoutId);
                this.pointerIdleTimeoutId = null;
            }
            if (this.pointerMoveRafId) {
                cancelAnimationFrame(this.pointerMoveRafId);
                this.pointerMoveRafId = null;
            }
            this.deferredAutoAdvance = null;
            this.pointerMoving = false;
            clearTimeout(this.hideControlsTimeoutId);
            clearTimeout(this.hideInfoTimeoutId);

            // 非表示にしたポストを再表示
            this.showHiddenPosts();

            if (this.activeDisplayedMediaElement && this.activeDisplayedMediaElement.tagName === 'VIDEO') {
                this.activeDisplayedMediaElement.controls = false;
            }

            this.hideLoadingIndicator();
            this.restoreOriginalMediaPosition();

            if (this.activePost && this.activePost.mediaItems) {
                this.activePost.mediaItems.forEach(item => {
                    if (item.type === 'video' && item.originalElement && item.placeholder && item.placeholder.parentElement) {
                        item.originalElement.style.cssText = '';
                        if (item.originalElement.parentNode !== item.placeholder.parentElement) {
                            item.placeholder.parentElement.replaceChild(item.originalElement, item.placeholder);
                        }
                        if (!item.originalElement.paused) {
                            item.originalElement.pause();
                        }
                    }
                });
            }

            if (this.container) this.container.remove();
            this.container = null;
            document.body.style.scrollBehavior = 'auto';
            const trigger = document.getElementById('tiktok-trigger');
            if (trigger) trigger.style.display = 'inline-flex';
            this.activePost = { id: null, element: null, mediaItems: [], currentMediaIndex: 0 };
            this.activeDisplayedMediaElement = null;
            this.activePlaceholder = null;
            this.likeButton = null;
            this.followButton = null;
            this.exitButton = null;
            this.scrollUpButton = null;
            this.scrollDownButton = null;
            this.dateButton = null;
            this.datePanel = null;
            this.hideUIButton = null;
            this.savedScrollPosition = 0;
            this.disconnectObserver();
            this.isReturningFromFollow = false;
            document.documentElement.style.overflowY = this.savedOverflowY || '';

            // X Reels自体を再初期化(iframe内でも確実に動作するように)
            setTimeout(() => {
                const trigger = document.getElementById('tiktok-trigger');
                if (!trigger) {
                    this.addManualTrigger();
                }
            }, 300);
        }

        createContainer() {
            if (this.container) this.container.remove();
            this.container = document.createElement('div');
            this.container.id = 'tiktok-feed-container';
            this.container.style.cssText = `
                position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
                background: rgba(0, 0, 0, 0.05); z-index: 2147483647;
                pointer-events: none; overflow: hidden;
            `;

            // Insert styles for Fancy Buttons
            if(!document.getElementById('xreels-fancy-buttons')) {
                const fancyButtonsStyle = document.createElement('style');
                fancyButtonsStyle.id = 'xreels-fancy-buttons';
                fancyButtonsStyle.textContent = `
                    @keyframes pulse-ring { 0% { transform: scale(0.95); opacity: 1; } 50% { transform: scale(1.05); opacity: 0.7; } 100% { transform: scale(1.15); opacity: 0; } }
                    @keyframes pulse-rect { 0% { transform: scale(0.98); opacity: 1; } 50% { transform: scale(1.02); opacity: 0.7; } 100% { transform: scale(1.06); opacity: 0; } }
                    @keyframes heart-beat { 0%, 100% { transform: scale(1); } 25% { transform: scale(1.2); } 50% { transform: scale(1.1); } 75% { transform: scale(1.25); } }
                    @keyframes float-up { 0% { transform: translateY(0) scale(1); opacity: 1; } 100% { transform: translateY(-8px) scale(1.1); opacity: 0; } }
                    @keyframes float-down { 0% { transform: translateY(0) scale(1); opacity: 1; } 100% { transform: translateY(8px) scale(1.1); opacity: 0; } }
                    @keyframes exit-shake { 0%, 100% { transform: translateX(0) rotate(0deg); } 25% { transform: translateX(-3px) rotate(-5deg); } 75% { transform: translateX(3px) rotate(5deg); } }
                    .fancy-exit-btn { position: relative; }
                    .fancy-exit-btn::before { content: ''; position: absolute; inset: 0; border-radius: 16px; background: radial-gradient(circle at center, rgba(255,255,255,0.3) 0%, transparent 70%); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; z-index: 1; }
                    .fancy-exit-btn:hover::before { opacity: 1; }
                    .fancy-exit-btn:hover { transform: translateY(-3px) scale(1.05); background: linear-gradient(135deg, rgba(239, 68, 68, 0.5) 0%, rgba(220, 38, 38, 0.5) 100%) !important; border-color: rgba(239, 68, 68, 0.9) !important; box-shadow: 0 12px 40px rgba(239, 68, 68, 0.5), 0 0 30px rgba(239, 68, 68, 0.3) inset !important; }
                    .fancy-exit-btn:hover svg { animation: exit-shake 0.5s ease; }
                    .fancy-exit-btn:hover .exit-pulse { animation: pulse-rect 1s cubic-bezier(0.4, 0, 0.6, 1) infinite !important; }
                    .fancy-exit-btn:active { transform: translateY(-1px) scale(1.02); transition: all 0.1s ease; }
                    .fancy-action-btn { position: relative; }
                    .fancy-action-btn::before { content: ''; position: absolute; inset: 0; border-radius: 50%; background: radial-gradient(circle at center, rgba(255,255,255,0.3) 0%, transparent 70%); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; z-index: 1; }
                    .fancy-action-btn:hover::before { opacity: 1; }
                    .fancy-action-btn:hover { transform: translateY(-4px) scale(1.08); box-shadow: 0 12px 40px rgba(255, 255, 255, 0.2), 0 0 30px currentColor inset; }
                    .fancy-action-btn:hover .like-icon, .fancy-action-btn:hover .follow-icon, .fancy-action-btn:hover .arrow-icon { transform: scale(1.15); }
                    .fancy-action-btn:active { transform: translateY(-2px) scale(1.02); transition: all 0.1s ease; }
                    #tiktok-like-btn:hover .pulse-ring { animation: pulse-ring 1s cubic-bezier(0.4, 0, 0.6, 1) infinite !important; }
                    #tiktok-scroll-up-btn:hover .arrow-icon { animation: float-up 0.6s ease infinite; }
                    #tiktok-scroll-down-btn:hover .arrow-icon { animation: float-down 0.6s ease infinite; }
                    #tiktok-like-btn.liked .heart-path { fill: #ff1447; stroke: #ff1447; animation: heart-beat 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); transform-origin: center; }
                    #tiktok-like-btn.liked { background: linear-gradient(135deg, rgba(255, 20, 71, 0.5) 0%, rgba(220, 0, 50, 0.5) 100%); border-color: rgba(255, 20, 71, 0.8); box-shadow: 0 12px 40px rgba(255, 20, 71, 0.5), 0 0 30px rgba(255, 20, 71, 0.3) inset; }
                    #tiktok-follow-btn.following .follow-plus-v, #tiktok-follow-btn.following .follow-plus-h { opacity: 0; transform: scale(0); }
                    #tiktok-follow-btn.following { background: linear-gradient(135deg, rgba(29, 161, 242, 0.5) 0%, rgba(26, 140, 216, 0.5) 100%); border-color: rgba(29, 161, 242, 0.8); box-shadow: 0 12px 40px rgba(29, 161, 242, 0.5), 0 0 30px rgba(29, 161, 242, 0.3) inset; }
                    #tiktok-follow-btn.following::after { content: '✓'; position: absolute; top: 50%; right: -4px; transform: translate(0, -50%); font-size: 14px; color: white; font-weight: bold; z-index: 3; text-shadow: 0 2px 8px rgba(0,0,0,0.4); }
                `;
                document.head.appendChild(fancyButtonsStyle);
            }

            this.container.innerHTML = `
                <div class="media-wrapper" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; pointer-events: auto; overflow: hidden;"></div>
                <div class="ui-container" style="position: relative; z-index: 1000002; width: 100%; height: 100%; pointer-events: none;">

                    <div class="loading-indicator" style="display: none; position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); color: white; padding: 8px 12px; border-radius: 15px; z-index: 1000003; font-size: 12px; font-weight: 600; backdrop-filter: blur(10px); pointer-events: none;">Loading...</div>

                    <div style="position: absolute; top: 20px; left: 20px; display: flex; flex-direction: column; align-items: flex-start; gap: 5px; pointer-events: auto;">
                        <div class="info-controls-wrapper" style="color: rgba(255,255,255,0.9); background: rgba(0,0,0,0.6); padding: 6px 10px; border-radius: 15px; font-size: 12px; backdrop-filter: blur(10px); pointer-events: auto; transition: all 0.3s ease;">
                            <div class="info-header" style="color: white; cursor: pointer; text-align: left; display: flex; align-items: center; justify-content: flex-start; gap: 5px;">Keyboard Shortcuts <span style="display: inline-block; width: 12px; height: 12px; background-image: url('data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20d%3D%22M7.41%208.59L12%2013.17L16.59%208.59L18%2010L12%2016L6%2010L7.41%208.59Z%22%20fill%3D%22%23FF6F61%22%2F%3E%0A%3C%2Fsvg%3E'); background-repeat: no-repeat; background-position: center; background-size: contain; transform: rotate(0deg); transition: transform 0.3s ease-out;"></span></div>
                            <div class="controls-expanded" style="max-height: 0; overflow: hidden; opacity: 0; transition: max-height 0.3s ease-out, opacity 0.3s ease-out; padding-top: 0; font-weight: 400;">
                                • X: <span style="color:#FF6F61;">Enter/Exit Feed</span> <br>
                                • Scroll: <span style="color:#FF6F61;">⬆️/⬇️</span> <br>
                                • Cycle Media: <span style="color:#FF6F61;">⬅️/➡️</span> <br>
                                • Space: <span style="color:#FF6F61;">Play/Pause</span> <br>
                                • &lt; / &gt; : <span style="color:#FF6F61;">Scrub</span> <br>
                                • L: <span style="color:#FF6F61;">Like</span> <br>
                                • F: <span style="color:#FF6F61;">Follow</span> <br>
                                • Q: <span style="color:#FF6F61;">Exit</span>
                            </div>
                        </div>
                    </div>

                    <div style="position: absolute; top: 20px; right: 20px; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; pointer-events: auto;">
                        <button id="tiktok-exit-btn" class="fancy-exit-btn" style="background: linear-gradient(135deg, rgba(239, 68, 68, 0.3) 0%, rgba(220, 38, 38, 0.3) 100%); border: 1px solid rgba(239, 68, 68, 0.6); border-radius: 16px; padding: 10px 16px; color: white; font-size: 13px; font-weight: 700; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(239, 68, 68, 0.35), 0 0 20px rgba(239, 68, 68, 0.15) inset; transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); pointer-events: auto; display: flex; align-items: center; gap: 8px; position: relative; overflow: hidden;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="transition: all 0.3s ease;">
                                <path d="M18 6L6 18M6 6l12 12" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));"/>
                            </svg>
                            <span style="position: relative; z-index: 2;">Exit</span>
                            <div class="exit-pulse" style="position: absolute; inset: -2px; border-radius: 16px; border: 2px solid rgba(239, 68, 68, 0.6); opacity: 0; animation: pulse-rect 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;"></div>
                        </button>

                        <button id="tiktok-blur-toggle" style="background: linear-gradient(135deg, rgba(120, 120, 120, 0.25) 0%, rgba(80, 80, 80, 0.25) 100%); border: 1px solid rgba(180, 180, 180, 0.5); border-radius: 12px; padding: 8px 12px; color: white; font-size: 12px; font-weight: 700; cursor: pointer; backdrop-filter: blur(14px) saturate(160%); box-shadow: 0 6px 22px rgba(0, 0, 0, 0.35); transition: all 0.3s ease; pointer-events: auto; display: flex; align-items: center; gap: 6px;">
                            <span class="blur-label">Blur: Off</span>
                        </button>

                        <button id="xreels-date-toggle" style="background: linear-gradient(135deg, rgba(34, 197, 94, 0.25) 0%, rgba(21, 128, 61, 0.25) 100%); border: 1px solid rgba(34, 197, 94, 0.6); border-radius: 12px; padding: 8px 12px; color: white; font-size: 12px; font-weight: 700; cursor: pointer; backdrop-filter: blur(14px) saturate(160%); box-shadow: 0 6px 22px rgba(0, 0, 0, 0.35); transition: all 0.3s ease; pointer-events: auto; display: flex; align-items: center; gap: 6px;">
                            <span class="date-label">Date</span>
                        </button>

                        <button id="tiktok-hideui-btn" style="background: linear-gradient(135deg, rgba(255, 165, 0, 0.25) 0%, rgba(255, 140, 0, 0.25) 100%); border: 1px solid rgba(255, 165, 0, 0.6); border-radius: 12px; padding: 8px 12px; color: white; font-size: 12px; font-weight: 700; cursor: pointer; backdrop-filter: blur(14px) saturate(160%); box-shadow: 0 6px 22px rgba(0, 0, 0, 0.35); transition: all 0.3s ease; pointer-events: auto; display: flex; align-items: center; gap: 6px;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
                                <circle cx="12" cy="12" r="3" stroke="white" stroke-width="2"/>
                                <line x1="3" y1="3" x2="21" y2="21" stroke="white" stroke-width="2" stroke-linecap="round"/>
                            </svg>
                            <span class="hideui-label">Hide UI</span>
                        </button>

                        <div style="color: white; background: rgba(0,0,0,0.6); padding: 8px 12px; border-radius: 15px; font-size: 12px; font-weight: 600; backdrop-filter: blur(10px); display: flex; align-items: center; gap: 8px; pointer-events: auto; cursor: pointer;" id="xreels-autoscroll-container">
                            <span>Auto: <span id="xreels-autoscroll-display" style="color: #FF6F61;">Off</span></span>
                            <span style="color: #FF6F61;">▼</span>
                        </div>
                        <div id="xreels-autoscroll-menu" style="display: none; position: absolute; top: 90px; right: 20px; background: rgba(0,0,0,0.9); border-radius: 10px; padding: 8px; backdrop-filter: blur(10px); pointer-events: auto; z-index: 10000000;">
                            <div class="autoscroll-option" data-value="0" style="padding: 8px 16px; cursor: pointer; color: #FF6F61; border-radius: 5px; transition: background 0.2s;">Off</div>
                            <div class="autoscroll-option" data-value="-1" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">Auto (Smart)</div>
                            <div class="autoscroll-option" data-value="10" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">0.01s</div>
                            <div class="autoscroll-option" data-value="100" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">0.1s</div>
                            <div class="autoscroll-option" data-value="300" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">0.3s</div>
                            <div class="autoscroll-option" data-value="600" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">0.6s</div>
                            <div class="autoscroll-option" data-value="1000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">1s</div>
                            <div class="autoscroll-option" data-value="2000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">2s</div>
                            <div class="autoscroll-option" data-value="3000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">3s</div>
                            <div class="autoscroll-option" data-value="5000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">5s</div>
                            <div class="autoscroll-option" data-value="8000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">8s</div>
                            <div class="autoscroll-option" data-value="30000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">30s</div>
                            <div class="autoscroll-option" data-value="60000" style="padding: 8px 16px; cursor: pointer; color: white; border-radius: 5px; transition: background 0.2s;">60s</div>
                        </div>

                        <div id="xreels-date-panel" style="display: none; position: absolute; top: 180px; right: 20px; width: 260px; background: rgba(0,0,0,0.85); border-radius: 12px; padding: 12px; backdrop-filter: blur(12px); pointer-events: auto; z-index: 10000001; border: 1px solid rgba(34, 197, 94, 0.4); box-shadow: 0 10px 30px rgba(0,0,0,0.5);">
                            <div class="date-panel-body" style="color: white; font-size: 12px; display: flex; flex-direction: column; gap: 8px;"></div>
                        </div>
                    </div>

                    <div class="gallery-indicator" style="position: absolute; top: 20px; left: 50%; transform: translateX(-50%); color: white; background: rgba(0,0,0,0.8); padding: 6px 12px; border-radius: 15px; font-size: 14px; font-weight: 700; backdrop-filter: blur(10px); display: none; pointer-events: none; opacity: 1; transition: opacity 0.5s ease;"></div>

                    <div class="info" style="position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); width: calc(100% - 200px); max-width: 800px; text-align: center; color: white; background: linear-gradient(to top, rgba(200, 100, 200, 0.5) 0%, rgba(150, 80, 200, 0.3) 40%, transparent 100%); padding: 20px 30px 25px; border-radius: 20px; max-height: 200px; overflow-y: auto; backdrop-filter: blur(15px) saturate(120%); pointer-events: auto; box-shadow: 0 -4px 20px rgba(150, 80, 200, 0.2), inset 0 1px 0 rgba(255,255,255,0.05);"></div>

                    <div class="action-buttons" style="position: absolute; right: 20px; bottom: 100px; display: flex; flex-direction: column; gap: 14px; z-index: 1000002; pointer-events: auto;">
                        <button id="tiktok-like-btn" class="fancy-action-btn" style="background: linear-gradient(135deg, rgba(255, 20, 80, 0.25) 0%, rgba(255, 64, 129, 0.25) 100%); border: 1px solid rgba(255, 20, 80, 0.5); border-radius: 50%; width: 54px; height: 54px; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(255, 20, 80, 0.35), 0 0 20px rgba(255, 20, 80, 0.2) inset; transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); display: flex; align-items: center; justify-content: center; position: relative; overflow: visible;">
                            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="like-icon" style="position: relative; z-index: 2; transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); overflow: visible;">
                                <path class="heart-path" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" fill="none" stroke="white" stroke-width="2" stroke-linejoin="round" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4)); transition: all 0.4s ease;"/>
                            </svg>
                            <div class="pulse-ring" style="position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(255, 20, 80, 0.6); opacity: 0; animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;"></div>
                        </button>

                        <button id="tiktok-follow-btn" class="fancy-action-btn" style="background: linear-gradient(135deg, rgba(29, 161, 242, 0.25) 0%, rgba(26, 140, 216, 0.25) 100%); border: 1px solid rgba(29, 161, 242, 0.5); border-radius: 50%; width: 54px; height: 54px; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(29, 161, 242, 0.35), 0 0 20px rgba(29, 161, 242, 0.2) inset; transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); display: flex; align-items: center; justify-content: center; position: relative; overflow: visible;">
                            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="follow-icon" style="position: relative; z-index: 2; transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);">
                                <circle cx="12" cy="8" r="4" stroke="white" stroke-width="2" fill="none" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4));"/>
                                <path d="M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2" stroke="white" stroke-width="2" stroke-linecap="round" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4));"/>
                                <line x1="19" y1="8" x2="19" y2="14" stroke="white" stroke-width="2" stroke-linecap="round" class="follow-plus-v" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4)); transition: all 0.3s ease;"/>
                                <line x1="16" y1="11" x2="22" y2="11" stroke="white" stroke-width="2" stroke-linecap="round" class="follow-plus-h" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4)); transition: all 0.3s ease;"/>
                            </svg>
                            <div class="pulse-ring" style="position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(29, 161, 242, 0.6); opacity: 0; animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite 0.5s;"></div>
                        </button>

                        <button id="tiktok-scroll-up-btn" class="fancy-action-btn" style="background: linear-gradient(135deg, rgba(100, 150, 255, 0.25) 0%, rgba(70, 120, 255, 0.25) 100%); border: 1px solid rgba(100, 150, 255, 0.5); border-radius: 50%; width: 54px; height: 54px; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(100, 150, 255, 0.35), 0 0 20px rgba(100, 150, 255, 0.2) inset; transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); display: flex; align-items: center; justify-content: center; position: relative; overflow: visible;">
                            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="arrow-icon" style="position: relative; z-index: 2; transition: all 0.3s ease;">
                                <path d="M12 19V5M5 12l7-7 7 7" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4));"/>
                            </svg>
                            <div class="pulse-ring" style="position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(100, 150, 255, 0.6); opacity: 0; animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite 1s;"></div>
                        </button>

                        <button id="tiktok-scroll-down-btn" class="fancy-action-btn" style="background: linear-gradient(135deg, rgba(100, 150, 255, 0.25) 0%, rgba(70, 120, 255, 0.25) 100%); border: 1px solid rgba(100, 150, 255, 0.5); border-radius: 50%; width: 54px; height: 54px; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(100, 150, 255, 0.35), 0 0 20px rgba(100, 150, 255, 0.2) inset; transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); display: flex; align-items: center; justify-content: center; position: relative; overflow: visible;">
                            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="arrow-icon" style="position: relative; z-index: 2; transition: all 0.3s ease;">
                                <path d="M12 5v14M5 12l7 7 7-7" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4));"/>
                            </svg>
                            <div class="pulse-ring" style="position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(100, 150, 255, 0.6); opacity: 0; animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite 1.5s;"></div>
                        </button>

                        <button id="tiktok-replies-btn" class="fancy-action-btn" style="background: linear-gradient(135deg, rgba(150, 100, 255, 0.25) 0%, rgba(120, 70, 255, 0.25) 100%); border: 1px solid rgba(150, 100, 255, 0.5); border-radius: 50%; width: 54px; height: 54px; cursor: pointer; backdrop-filter: blur(20px) saturate(180%); box-shadow: 0 8px 32px rgba(150, 100, 255, 0.35), 0 0 20px rgba(150, 100, 255, 0.2) inset; transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); display: flex; align-items: center; justify-content: center; position: relative; overflow: visible;">
                            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="replies-icon" style="position: relative; z-index: 2; transition: all 0.3s ease;">
                                <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.4));"/>
                            </svg>
                            <div class="pulse-ring" style="position: absolute; inset: -2px; border-radius: 50%; border: 2px solid rgba(150, 100, 255, 0.6); opacity: 0; animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite 2s;"></div>
                        </button>
                    </div>

                    <div id="prev-posts-panel" style="display: none; position: absolute; left: 20px; top: 20px; width: 350px; height: 400px; background: rgba(0,0,0,0.2); border-radius: 16px; padding: 0; backdrop-filter: blur(20px); z-index: 1000003; pointer-events: auto; border: 1px solid rgba(255, 127, 97, 0.5); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); resize: both; min-width: 250px; max-width: 800px; min-height: 200px; max-height: 90vh; overflow: hidden;">
                        <div id="prev-posts-header" style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; border-bottom: 1px solid rgba(255,255,255,0.1); cursor: move; background: linear-gradient(135deg, rgba(255, 127, 97, 0.3) 0%, rgba(255, 97, 127, 0.3) 100%); border-radius: 16px 16px 0 0;">
                            <div style="color: #FF6F61; font-size: 10px; font-weight: 700; text-transform: uppercase; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">前のポスト</div>
                            <div style="display: flex; align-items: center; gap: 6px;">
                                <button id="prev-settings-btn" title="サイズ設定" style="background: rgba(0,0,0,0.25); border: 1px solid rgba(255,255,255,0.2); color: white; font-size: 11px; padding: 4px 6px; border-radius: 8px; cursor: pointer;">⚙</button>
                                <button id="close-prev-btn" style="background: none; border: none; color: rgba(255,255,255,0.6); cursor: pointer; font-size: 14px; padding: 0 4px; transition: color 0.2s;" onmouseover="this.style.color='white'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">×</button>
                            </div>
                        </div>
                        <div id="prev-settings-panel" style="display: none; position: absolute; top: 36px; right: 8px; background: rgba(0,0,0,0.7); border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; padding: 8px; z-index: 1000004; width: 200px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); backdrop-filter: blur(10px);">
                            <div style="display: flex; align-items: center; gap: 6px; margin-bottom: 6px;">
                                <label style="font-size: 10px; color: rgba(255,255,255,0.7); min-width: 38px;">Text</label>
                                <input type="range" id="prev-text-size-slider" min="8" max="18" step="1" style="flex: 1;" />
                                <span id="prev-text-size-value" style="font-size: 10px; color: rgba(255,255,255,0.6); min-width: 32px; text-align: right;"></span>
                            </div>
                            <div style="display: flex; align-items: center; gap: 6px;">
                                <label style="font-size: 10px; color: rgba(255,255,255,0.7); min-width: 38px;">Image</label>
                                <input type="range" id="prev-image-size-slider" min="20" max="100" step="5" style="flex: 1;" />
                                <span id="prev-image-size-value" style="font-size: 10px; color: rgba(255,255,255,0.6); min-width: 32px; text-align: right;"></span>
                            </div>
                        </div>
                        <div id="prev-posts-content" style="color: white; font-size: 12px; line-height: 1.4; padding: 12px; overflow-y: auto; height: calc(100% - 32px);"></div>
                    </div>

                    <div id="next-posts-panel" style="display: none; position: absolute; left: 20px; bottom: 20px; width: 350px; height: 400px; background: rgba(0,0,0,0.2); border-radius: 16px; padding: 0; backdrop-filter: blur(20px); z-index: 1000003; pointer-events: auto; border: 1px solid rgba(100, 150, 255, 0.5); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); resize: both; min-width: 250px; max-width: 800px; min-height: 200px; max-height: 90vh; overflow: hidden;">
                        <div id="next-posts-header" style="display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; border-bottom: 1px solid rgba(255,255,255,0.1); cursor: move; background: linear-gradient(135deg, rgba(100, 150, 255, 0.3) 0%, rgba(100, 200, 255, 0.3) 100%); border-radius: 16px 16px 0 0;">
                            <div style="color: #6496FF; font-size: 10px; font-weight: 700; text-transform: uppercase; text-shadow: 0 2px 4px rgba(0,0,0,0.5);">次のポスト</div>
                            <div style="display: flex; align-items: center; gap: 6px;">
                                <button id="next-settings-btn" title="サイズ設定" style="background: rgba(0,0,0,0.25); border: 1px solid rgba(255,255,255,0.2); color: white; font-size: 11px; padding: 4px 6px; border-radius: 8px; cursor: pointer;">⚙</button>
                            <button id="close-next-btn" style="background: none; border: none; color: rgba(255,255,255,0.6); cursor: pointer; font-size: 14px; padding: 0 4px; transition: color 0.2s;" onmouseover="this.style.color='white'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">×</button>
                            </div>
                        </div>
                        <div id="next-settings-panel" style="display: none; position: absolute; top: 36px; right: 8px; background: rgba(0,0,0,0.7); border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; padding: 8px; z-index: 1000004; width: 200px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); backdrop-filter: blur(10px);">
                            <div style="display: flex; align-items: center; gap: 6px; margin-bottom: 6px;">
                                <label style="font-size: 10px; color: rgba(255,255,255,0.7); min-width: 38px;">Text</label>
                                <input type="range" id="next-text-size-slider" min="8" max="18" step="1" style="flex: 1;" />
                                <span id="next-text-size-value" style="font-size: 10px; color: rgba(255,255,255,0.6); min-width: 32px; text-align: right;"></span>
                            </div>
                            <div style="display: flex; align-items: center; gap: 6px;">
                                <label style="font-size: 10px; color: rgba(255,255,255,0.7); min-width: 38px;">Image</label>
                                <input type="range" id="next-image-size-slider" min="20" max="100" step="5" style="flex: 1;" />
                                <span id="next-image-size-value" style="font-size: 10px; color: rgba(255,255,255,0.6); min-width: 32px; text-align: right;"></span>
                            </div>
                        </div>
                        <div id="next-posts-content" style="color: white; font-size: 12px; line-height: 1.4; padding: 12px; overflow-y: auto; height: calc(100% - 32px);"></div>
                    </div>
                </div>
            `;
            document.body.appendChild(this.container);
            this.likeButton = this.container.querySelector('#tiktok-like-btn');
            this.followButton = this.container.querySelector('#tiktok-follow-btn');
            this.exitButton = this.container.querySelector('#tiktok-exit-btn');
            this.scrollUpButton = this.container.querySelector('#tiktok-scroll-up-btn');
            this.scrollDownButton = this.container.querySelector('#tiktok-scroll-down-btn');
            this.repliesButton = this.container.querySelector('#tiktok-replies-btn');
            this.prevPostsPanel = this.container.querySelector('#prev-posts-panel');
            this.nextPostsPanel = this.container.querySelector('#next-posts-panel');
            this.blurToggleButton = this.container.querySelector('#tiktok-blur-toggle');
            this.dateButton = this.container.querySelector('#xreels-date-toggle');
            this.datePanel = this.container.querySelector('#xreels-date-panel');
            this.hideUIButton = this.container.querySelector('#tiktok-hideui-btn');

            this.likeButton.addEventListener('click', () => this.toggleLike());
            this.followButton.addEventListener('click', () => this.toggleFollow());
            this.exitButton.addEventListener('click', () => this.exit());
            this.repliesButton.addEventListener('click', () => this.toggleReplies());
            this.container.querySelector('#close-prev-btn').addEventListener('click', () => this.hidePrevPosts());
            this.container.querySelector('#close-next-btn').addEventListener('click', () => this.hideNextPosts());

            if (this.blurToggleButton) {
                const label = this.blurToggleButton.querySelector('.blur-label');
                if (label) label.textContent = `Blur: ${this.blurEnabled ? 'On' : 'Off'}`;
                this.blurToggleButton.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.toggleBlur();
                });
            }

            if (this.dateButton && this.datePanel) {
                this.dateButton.addEventListener('click', (e) => {
                    e.stopPropagation();
                    const isOpen = this.datePanel.style.display !== 'none';
                    this.datePanel.style.display = isOpen ? 'none' : 'block';
                    if (!isOpen) {
                        this.updateDatePanel();
                    }
                });
                document.addEventListener('click', (e) => {
                    if (!this.isActive) return;
                    if (!this.datePanel || this.datePanel.style.display === 'none') return;
                    if (!this.datePanel.contains(e.target) && e.target !== this.dateButton) {
                        this.datePanel.style.display = 'none';
                    }
                });
            }

            if (this.hideUIButton) {
                this.hideUIButton.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.toggleUIHidden();
                });
            }

            // ドラッグ移動機能
            this.setupDraggable(this.prevPostsPanel, this.container.querySelector('#prev-posts-header'), 'prev');
            this.setupDraggable(this.nextPostsPanel, this.container.querySelector('#next-posts-header'), 'next');

            // リサイズ監視
            this.setupResizeObserver(this.prevPostsPanel, 'prev');
            this.setupResizeObserver(this.nextPostsPanel, 'next');

            // 保存された位置とサイズを復元
            this.restorePanelState('prev');
            this.restorePanelState('next');

            // スライダーのイベントリスナー設定
            this.setupPanelSliders();

            const infoControlsWrapper = this.container.querySelector('.info-controls-wrapper');
            const controlsExpanded = this.container.querySelector('.controls-expanded');
            const infoHeader = this.container.querySelector('.info-header');
            const infoArrow = infoHeader.querySelector('span');

            let isHoveringInfo = false;
            let isInfoExpanded = false;

            infoControlsWrapper.addEventListener('mouseenter', () => {
                isHoveringInfo = true;
                clearTimeout(this.hideInfoTimeoutId);
                if (!isInfoExpanded) {
                    controlsExpanded.style.maxHeight = '200px';
                    controlsExpanded.style.opacity = '1';
                    controlsExpanded.style.paddingTop = '8px';
                    infoArrow.style.transform = 'rotate(180deg)';
                    isInfoExpanded = true;
                }
            });

            infoControlsWrapper.addEventListener('mouseleave', () => {
                isHoveringInfo = false;
                this.hideInfoTimeoutId = setTimeout(() => {
                    if (!isHoveringInfo) {
                        controlsExpanded.style.maxHeight = '0';
                        controlsExpanded.style.opacity = '0';
                        controlsExpanded.style.paddingTop = '0';
                        infoArrow.style.transform = 'rotate(0deg)';
                        isInfoExpanded = false;
                    }
                }, 500);
            });

            this.scrollUpButton.addEventListener('click', () => {
                const mediaNavigated = this.handleMediaNavigation('prev');
                if (!mediaNavigated) {
                    this.handlePostNavigation('prev');
                }
            });
            this.scrollDownButton.addEventListener('click', () => {
                const mediaNavigated = this.handleMediaNavigation('next');
                if (!mediaNavigated) {
                    this.handlePostNavigation('next');
                }
            });

            this.setupAutoScrollControls();
            this.setupPointerHoldForAutoScroll();
        }

        setupAutoScrollControls() {
            const container = document.getElementById('xreels-autoscroll-container');
            const menu = document.getElementById('xreels-autoscroll-menu');
            const display = document.getElementById('xreels-autoscroll-display');

            const timeLabels = { 0: 'Off', '-1': 'Auto (Smart)', 10: '0.01s', 100: '0.1s', 300: '0.3s', 600: '0.6s', 1000: '1s', 2000: '2s', 3000: '3s', 5000: '5s', 8000: '8s', 30000: '30s', 60000: '60s' };
            display.textContent = timeLabels[this.autoScrollDelay] || 'Off';

            container.addEventListener('click', (e) => {
                e.stopPropagation();
                menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
            });

            // イベントデリゲーションでスタイル変更と処理を統一
            menu.addEventListener('mouseenter', (e) => {
                if (e.target.classList.contains('autoscroll-option')) {
                    e.target.style.background = 'rgba(255,255,255,0.1)';
                }
            }, true);
            menu.addEventListener('mouseleave', (e) => {
                if (e.target.classList.contains('autoscroll-option')) {
                    e.target.style.background = 'transparent';
                }
            }, true);
            menu.addEventListener('click', (e) => {
                const option = e.target.closest('.autoscroll-option');
                if (option) {
                    e.stopPropagation();
                    const value = parseInt(option.dataset.value, 10);
                    this.autoScrollDelay = value;
                    localStorage.setItem('xreels_autoScrollDelay', value.toString());
                    display.textContent = timeLabels[value];
                    menu.style.display = 'none';

                    if (this.isActive && (value > 0 || value === -1)) {
                        this.startAutoScrollTimer();
                    } else if (value === 0) {
                        this.stopAutoScrollTimer();
                    }
                }
            });

            document.addEventListener('click', () => {
                if (menu) menu.style.display = 'none';
            });
        }

        setupPointerHoldForAutoScroll() {
            if (!this.container) return;
            const area = this.container;
            const handleMove = () => {
                if (this.pointerMoveRafId) return;
                this.pointerMoveRafId = requestAnimationFrame(() => {
                    this.pointerMoveRafId = null;
                    this.pointerMoving = true;
                    if (this.pointerIdleTimeoutId) clearTimeout(this.pointerIdleTimeoutId);
                    this.pointerIdleTimeoutId = setTimeout(() => {
                        this.pointerMoving = false;
                        if (!this.isActive) {
                            this.deferredAutoAdvance = null;
                            return;
                        }
                        if (this.deferredAutoAdvance) {
                            const action = this.deferredAutoAdvance;
                            this.deferredAutoAdvance = null;
                            action();
                        }
                    }, 600);
                });
            };
            area.addEventListener('pointermove', handleMove, { passive: true });
        }

        maybeDeferAutoAdvance(action) {
            if (!this.isActive) return;
            if (this.pointerMoving) {
                this.deferredAutoAdvance = action;
                return;
            }
            action();
        }

        startAutoScrollTimer() {
            this.stopAutoScrollTimer();
            if (!this.isActive) return;
            if (this.autoScrollDelay === 0) return;

            // SMART MODE (-1)
            if (this.autoScrollDelay === -1) {
                if (this.activeDisplayedMediaElement && this.activeDisplayedMediaElement.tagName === 'VIDEO') {
                    // Video: Wait for end
                    const video = this.activeDisplayedMediaElement;
                    this.videoEndedListener = () => {
                        if (!this.isActive) return;
                        // リスナーを即座に削除(重複実行防止)
                        if (this.videoEndedListener) {
                            video.removeEventListener('ended', this.videoEndedListener);
                            this.videoEndedListener = null;
                        }
                        this.maybeDeferAutoAdvance(() => {
                            const mediaNavigated = this.handleMediaNavigation('next');
                            if (!mediaNavigated) {
                                this.handlePostNavigation('next');
                            }
                        });
                    };
                    video.addEventListener('ended', this.videoEndedListener);
                } else {
                    // Image: Wait 3 seconds
                    this.autoScrollTimeoutId = setTimeout(() => {
                        this.maybeDeferAutoAdvance(() => {
                            this.autoScrollTimeoutId = null;
                            const mediaNavigated = this.handleMediaNavigation('next');
                            if (!mediaNavigated) {
                                this.handlePostNavigation('next');
                            }
                        });
                    }, 3000);
                }
            }
            // STANDARD TIMER MODE (> 0)
            else if (this.autoScrollDelay > 0) {
                this.autoScrollTimeoutId = setTimeout(() => {
                    this.maybeDeferAutoAdvance(() => {
                        this.autoScrollTimeoutId = null;
                        const mediaNavigated = this.handleMediaNavigation('next');
                        if (!mediaNavigated) {
                            this.handlePostNavigation('next');
                        }
                    });
                }, this.autoScrollDelay);
            }

            // Watchdog: Check if auto-scroll needs to be restarted
            this.setupAutoScrollWatchdog();
        }

        setupAutoScrollWatchdog() {
            if (this.autoScrollWatchdogId) {
                clearInterval(this.autoScrollWatchdogId);
                this.autoScrollWatchdogId = null;
            }

            // ウォッチドッグは正の遅延でのみ稼働(Smartはイベントに任せる)
            if (!this.isActive || this.autoScrollDelay <= 0) return;

            this.autoScrollWatchdogId = setInterval(() => {
                if (!this.isActive || this.autoScrollDelay <= 0) {
                    clearInterval(this.autoScrollWatchdogId);
                    this.autoScrollWatchdogId = null;
                    return;
                }

                // タイマーが失敗しているかチェック
                if (!this.autoScrollTimeoutId) {
                    console.log('Auto-scroll timer stopped, restarting...');
                    this.startAutoScrollTimer();
                }
            }, 5000); // 5秒ごとにチェック
        }

        stopAutoScrollTimer() {
            if (this.autoScrollTimeoutId) {
                clearTimeout(this.autoScrollTimeoutId);
                this.autoScrollTimeoutId = null;
            }
            if (this.autoScrollWatchdogId) {
                clearInterval(this.autoScrollWatchdogId);
                this.autoScrollWatchdogId = null;
            }
            // Cleanup Video Ended Listener
            if (this.videoEndedListener && this.activeDisplayedMediaElement) {
                this.activeDisplayedMediaElement.removeEventListener('ended', this.videoEndedListener);
                this.videoEndedListener = null;
            }
        }

        async toggleLike() {
            if (!this.activePost.element) return;

            this.likeButton.style.transform = 'scale(1.3)';
            this.likeButton.style.filter = 'brightness(1.5)';
            this.likeButton.style.transition = 'transform 0.1s ease-out, filter 0.1s ease-out';

            const likeButton = this.activePost.element.querySelector('[data-testid="like"], [data-testid="unlike"]');
            if (likeButton) {
                likeButton.click();
                await new Promise(resolve => setTimeout(resolve, 250));
                this.updateLikeButtonState();
            }

            setTimeout(() => {
                this.likeButton.style.transform = 'scale(1)';
                this.likeButton.style.filter = 'brightness(1)';
                this.likeButton.style.transition = 'all 0.3s ease';
            }, 200);
        }

        async toggleFollow() {
            if (!this.activePost.element) return;

            const currentMedia = this.activePost.mediaItems[this.activePost.currentMediaIndex];
            if (!currentMedia) return;

            const contextElement = currentMedia.contextElement || this.activePost.element;
            const originalTweetId = this.activePost.id;

            this.savedScrollPosition = window.scrollY;
            this.isReturningFromFollow = true;
            console.log(`Saved scroll position: ${this.savedScrollPosition} for tweet ${originalTweetId}`);

            let userLinkToClick = contextElement.querySelector('[data-testid="User-Name"] a');
            let clickedQuoteCard = false;
            const isMainContext = contextElement === this.activePost.element;

            if (!userLinkToClick && !isMainContext) {
                 console.log("No user link found in context (likely Quote Tweet). Clicking context to navigate to tweet.");
                 userLinkToClick = contextElement.closest('[role="link"]') || contextElement;
                 clickedQuoteCard = true;
            }

            if (!userLinkToClick) {
                 if (isMainContext) {
                     console.warn("Could not find user link in main context.");
                     userLinkToClick = this.activePost.element.querySelector('[data-testid="User-Name"] a');
                 } else {
                     console.warn("Could not find actionable link in Quote Context.");
                 }
            }

            if (!userLinkToClick) {
                console.warn("Could not find user link for follow action.");
                this.isReturningFromFollow = false;
                return;
            }

            console.log(`Navigating... (QuoteCard: ${clickedQuoteCard})`);
            userLinkToClick.click();
            await new Promise(resolve => setTimeout(resolve, 1500));

            let extraDepth = 0;
            if (clickedQuoteCard) {
                 let tweetUserLink = null;
                 let loadAttempts = 0;
                 while (!tweetUserLink && loadAttempts < 20) {
                     tweetUserLink = document.querySelector('article[data-testid="tweet"] [data-testid="User-Name"] a');
                     if (!tweetUserLink) await new Promise(r => setTimeout(r, 200));
                     loadAttempts++;
                 }

                 if (tweetUserLink) {
                     console.log("On Tweet page, clicking profile link...");
                     tweetUserLink.click();
                     await new Promise(resolve => setTimeout(resolve, 1500));
                     extraDepth = 1;
                 } else {
                     console.warn("Could not find profile link on Tweet page.");
                 }
            }

            let followBtn = null;
            let attempts = 0;
            const maxAttempts = 15;
            while (!followBtn && attempts < maxAttempts) {
                followBtn = document.querySelector('[data-testid*="follow"], [aria-label*="Follow"], [aria-label*="Unfollow"]');
                if (!followBtn) {
                    await new Promise(resolve => setTimeout(resolve, 200));
                }
                attempts++;
            }

            if (followBtn) {
                followBtn.click();
                await new Promise(resolve => setTimeout(resolve, 500));
            } else {
                console.warn("Follow/Unfollow button not found on profile page.");
            }

            if (clickedQuoteCard && extraDepth > 0) {
                window.history.go(-2);
            } else {
                window.history.back();
            }

            await new Promise(resolve => setTimeout(resolve, 1500));

            this.showLoadingIndicator('Restoring feed position...');
            let targetPostElement = null;
            attempts = 0;
            const checkInterval = 200;
            const maxChecks = 25;

            while (!targetPostElement && attempts < maxChecks) {
                targetPostElement = document.querySelector(`article[role="article"] a[href*="/status/${originalTweetId}"]`)?.closest('article[role="article"]');
                if (!targetPostElement) {
                    await new Promise(resolve => setTimeout(resolve, checkInterval));
                }
                attempts++;
            }

            if (targetPostElement) {
                console.log(`Found original tweet element (ID: ${originalTweetId}), re-navigating...`);
                this.navigateToPost(targetPostElement);
            } else {
                console.warn(`Original tweet element (ID: ${originalTweetId}) not found after returning. Falling back to central post.`);
                this.navigateToPost(this.findCentralMediaArticle());
            }

            window.scrollTo(0, this.savedScrollPosition);
            this.hideLoadingIndicator();
            this.isReturningFromFollow = false;
        }

        updateLikeButtonState() {
            if (!this.likeButton || !this.activePost.element) return;
            const likedButton = this.activePost.element.querySelector('[data-testid="unlike"]');
            if (likedButton) {
                this.likeButton.classList.add('liked');
                const heartPath = this.likeButton.querySelector('.heart-path');
                if (heartPath) {
                    heartPath.setAttribute('fill', '#ff1447');
                    heartPath.setAttribute('stroke', '#ff1447');
                }
            } else {
                this.likeButton.classList.remove('liked');
                const heartPath = this.likeButton.querySelector('.heart-path');
                if (heartPath) {
                    heartPath.setAttribute('fill', 'none');
                    heartPath.setAttribute('stroke', 'white');
                }
            }
        }

        updateFollowButtonState() {
            if (!this.followButton || !this.activePost.element) return;

            const currentMedia = this.activePost.mediaItems[this.activePost.currentMediaIndex];
            if (!currentMedia) return;
            const contextElement = currentMedia.contextElement || this.activePost.element;

            let isFollowingTargetAuthor = false;

            const mainAuthorUnfollowBtn = contextElement.querySelector('[data-testid$="-unfollow"]');
            if (mainAuthorUnfollowBtn) {
                isFollowingTargetAuthor = true;
            } else {
                const mainAuthorFollowingText = Array.from(contextElement.querySelectorAll('[data-testid="User-Name"] ~ div button')).some(btn => {
                    const text = btn.textContent.trim();
                    return text === 'Following' || text === 'Unfollow';
                });
                if (mainAuthorFollowingText) {
                    isFollowingTargetAuthor = true;
                }
            }

            if (isFollowingTargetAuthor) {
                this.followButton.classList.add('following');
            } else {
                this.followButton.classList.remove('following');
            }
        }

        getIconFromArticle(article) {
            const iconEl = article ? article.querySelector('[data-testid="User-Name"] img[src*="profile_images"]') : null;
            return iconEl ? iconEl.src : '';
        }

        getTimestampText(currentMedia) {
            if (currentMedia && currentMedia.timestampText) return currentMedia.timestampText;
            const timeEl = this.activePost && this.activePost.element ? this.activePost.element.querySelector('time') : null;
            if (timeEl) {
                const iso = timeEl.getAttribute('datetime');
                if (iso) {
                    const parsed = new Date(iso);
                    if (!isNaN(parsed.getTime())) {
                        return parsed.toLocaleString();
                    }
                }
                return timeEl.textContent ? timeEl.textContent.trim() : '';
            }
            return '';
        }

        applyMainTextStyles() {
            if (!this.container) return;
            const textSize = Math.max(8, this.panelSettings ? this.panelSettings.textSize : 11);
            const authorSize = textSize + 5;
            const bodySize = textSize + 3;
            const metaSize = Math.max(9, textSize - 1);

            const infoEl = this.container.querySelector('.info');
            if (!infoEl) return;

            const authorEl = infoEl.querySelector('.author-name');
            const metaEl = infoEl.querySelector('.main-post-meta');
            const bodyEl = infoEl.querySelector('.main-post-text');

            if (authorEl) authorEl.style.fontSize = `${authorSize}px`;
            if (metaEl) metaEl.style.fontSize = `${metaSize}px`;
            if (bodyEl) bodyEl.style.fontSize = `${bodySize}px`;
        }

        updateUI() {
            if (!this.container || !this.activePost || !this.activePost.element) return;

            const currentMedia = this.activePost.mediaItems[this.activePost.currentMediaIndex];
            if (!currentMedia) return;

            const authorName = currentMedia.author || 'Unknown User';
            const authorHandle = currentMedia.handle;
            const tweetText = currentMedia.text || '';

            const iconUrl = currentMedia.icon || this.getIconFromArticle(this.activePost.element);
            const timestampText = this.getTimestampText(currentMedia);
            const metaParts = [];
            if (authorHandle) metaParts.push(`<span class="meta-handle">@${authorHandle}</span>`);
            if (timestampText) metaParts.push(`<span class="meta-time">${timestampText}</span>`);
            if (currentMedia.repostTimestampText) metaParts.push(`<span class="meta-repost" style="color:#22c55e; text-shadow: 0 0 2px #000, 0 0 4px #000;">Repost: ${currentMedia.repostTimestampText}</span>`);
            const metaLine = metaParts.join(' · ');

            const infoEl = this.container.querySelector('.info');
            const iconHtml = iconUrl ? `<img class="main-post-icon" src="${iconUrl}" style="width: 38px; height: 38px; border-radius: 50%; object-fit: cover; box-shadow: 0 4px 10px rgba(0,0,0,0.35);" />` : '';
            const metaStyle = metaLine ? '' : 'display: none;';

            infoEl.innerHTML = `
                <div class="main-post-header" style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 8px;">
                    ${iconHtml}
                    <div style="display: flex; flex-direction: column; align-items: flex-start; text-align: left; gap: 2px;">
                        <div class="author-name" style="font-weight: 700; margin-bottom: 0; ${authorHandle ? 'cursor: pointer;' : ''}">${authorName}</div>
                        <div class="main-post-meta" style="color: rgba(255,255,255,0.9); font-weight: 600; ${metaStyle}">${metaLine}</div>
                    </div>
                </div>
                <div class="main-post-text" style="line-height: 1.5; opacity: 0.95; color: #22c55e; text-shadow: 0 0 2px #000, 0 0 4px #000;">${tweetText}</div>
            `;

            this.updateDatePanel();

            const authorElement = this.container.querySelector('.author-name');
            if (authorElement && authorHandle) {
                authorElement.addEventListener('click', (e) => {
                    e.stopPropagation();
                    window.open(`https://x.com/${authorHandle}`, '_blank');
                });
            }

            const galleryIndicator = this.container.querySelector('.gallery-indicator');
            if (this.activePost.mediaItems.length > 1) {
                galleryIndicator.textContent = `${this.activePost.currentMediaIndex + 1} / ${this.activePost.mediaItems.length}`;
                galleryIndicator.style.display = 'block';
                galleryIndicator.style.opacity = '1';

                if (this.galleryIndicatorTimeout) {
                    clearTimeout(this.galleryIndicatorTimeout);
                }

                this.galleryIndicatorTimeout = setTimeout(() => {
                    galleryIndicator.style.opacity = '0';
                }, 1500);
            } else {
                galleryIndicator.style.display = 'none';
            }
            this.updateLikeButtonState();
            this.updateFollowButtonState();
            this.applyMainTextStyles();
        }

        updateDatePanel() {
            if (!this.datePanel) return;
            const body = this.datePanel.querySelector('.date-panel-body');
            if (!body) return;

            const currentMedia = (this.activePost && this.activePost.mediaItems) ? this.activePost.mediaItems[this.activePost.currentMediaIndex] : null;
            if (!currentMedia) {
                body.innerHTML = '<div style="color: rgba(255,255,255,0.75);">No active post</div>';
                return;
            }

            const baseDate = this.getTimestampText(currentMedia) || 'N/A';
            const repostDate = currentMedia.repostTimestampText || '';
            const isRepost = Boolean(repostDate);

            const primaryLabel = isRepost ? 'Repost Date' : 'Post Date';
            const primaryValue = isRepost ? repostDate : baseDate;

            const primaryIso = (isRepost ? currentMedia.repostTimestampIso : currentMedia.timestampIso) || '';
            const dateForCalendar = primaryIso ? new Date(primaryIso) : new Date(primaryValue);
            const safeDate = isNaN(dateForCalendar.getTime()) ? new Date() : dateForCalendar;
            const calendarHtml = this.renderCalendar(safeDate);

            const originalLine = isRepost && baseDate
                ? `<div style="font-size: 11px; color: rgba(255,255,255,0.75);">Original: ${baseDate}</div>`
                : '';

            body.innerHTML = `
                <div style="display:flex; justify-content: space-between; align-items: center; color: rgba(255,255,255,0.85); font-weight: 700;">${primaryLabel}<span style="font-size: 11px; color: rgba(255,255,255,0.6);">${this.activePost.id ? '#' + this.activePost.id : ''}</span></div>
                <div style="margin-top: 6px; padding: 10px; border-radius: 10px; background: rgba(34,197,94,0.12); border: 1px solid rgba(34,197,94,0.4); color: #22c55e; text-shadow: 0 0 2px #000, 0 0 4px #000; font-weight: 700;">${primaryValue}</div>
                ${originalLine}
                <div style="margin-top: 8px;">${calendarHtml}</div>
            `;
        }

        renderCalendar(dateObj) {
            const year = dateObj.getFullYear();
            const month = dateObj.getMonth();
            const day = dateObj.getDate();

            const firstDay = new Date(year, month, 1);
            const startWeekday = firstDay.getDay(); // 0 Sun
            const daysInMonth = new Date(year, month + 1, 0).getDate();

            const weeks = [];
            let week = new Array(startWeekday).fill('');

            for (let d = 1; d <= daysInMonth; d++) {
                week.push(d);
                if (week.length === 7) {
                    weeks.push(week);
                    week = [];
                }
            }
            if (week.length > 0) {
                while (week.length < 7) week.push('');
                weeks.push(week);
            }

            const monthLabel = dateObj.toLocaleString(undefined, { month: 'long', year: 'numeric' });

            const rows = weeks.map(w => {
                const cells = w.map(value => {
                    if (!value) return '<div style="width: 32px; height: 32px; opacity: 0.25;"></div>';
                    const isToday = value === day;
                    const baseStyle = 'width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:8px;font-size:12px;';
                    const highlight = isToday ? 'background: rgba(34,197,94,0.25); border:1px solid rgba(34,197,94,0.7); color:#22c55e; text-shadow:0 0 2px #000,0 0 4px #000;' : 'color: rgba(255,255,255,0.8);';
                    return `<div style="${baseStyle}${highlight}">${value}</div>`;
                }).join('');
                return `<div style="display:flex; gap:4px; margin-bottom:4px;">${cells}</div>`;
            }).join('');

            const weekdayRow = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].map(d => `<div style="width:32px;height:20px;display:flex;align-items:center;justify-content:center;font-size:11px;color:rgba(255,255,255,0.6);">${d}</div>`).join('');

            return `
                <div style="color: rgba(255,255,255,0.9); font-weight: 700; margin-bottom: 4px;">${monthLabel}</div>
                <div style="display:flex; gap:4px; margin-bottom:4px;">${weekdayRow}</div>
                ${rows}
            `;
        }

        showLoadingIndicator(message = 'Loading...') {
            if (this.container) {
                const indicator = this.container.querySelector('.loading-indicator');
                if(indicator) { indicator.textContent = message; indicator.style.display = 'block'; }
            }
        }

        hideLoadingIndicator() {
            if (this.container) {
                const indicator = this.container.querySelector('.loading-indicator');
                if(indicator) indicator.style.display = 'none';
            }
        }

        toggleBlur() {
            this.blurEnabled = !this.blurEnabled;
            localStorage.setItem('xreels_blur', this.blurEnabled ? 'true' : 'false');
            const label = this.blurToggleButton ? this.blurToggleButton.querySelector('.blur-label') : null;
            if (label) label.textContent = `Blur: ${this.blurEnabled ? 'On' : 'Off'}`;
            this.applyBlurStyles();
        }

        toggleUIHidden() {
            this.uiHiddenMode = !this.uiHiddenMode;
            
            const displayValue = this.uiHiddenMode ? 'none' : '';
            
            // UI要素の表示/非表示を切り替え(querySelector結果のキャッシング)
            if (this.likeButton) this.likeButton.style.display = displayValue;
            if (this.followButton) this.followButton.style.display = displayValue;
            if (this.exitButton) this.exitButton.style.display = displayValue;
            if (this.scrollUpButton) this.scrollUpButton.style.display = displayValue;
            if (this.scrollDownButton) this.scrollDownButton.style.display = displayValue;
            if (this.repliesButton) this.repliesButton.style.display = displayValue;
            if (this.blurToggleButton) this.blurToggleButton.style.display = displayValue;
            if (this.dateButton) this.dateButton.style.display = displayValue;
            if (this.prevPostsPanel) this.prevPostsPanel.style.display = displayValue;
            if (this.nextPostsPanel) this.nextPostsPanel.style.display = displayValue;
            
            const autoscrollContainer = this.container?.querySelector('#xreels-autoscroll-container');
            if (autoscrollContainer) autoscrollContainer.style.display = displayValue;
            
            const infoEl = this.container?.querySelector('.info');
            if (infoEl) infoEl.style.display = displayValue;
            
            const mediaInfo = this.container?.querySelector('.media-info');
            if (mediaInfo) mediaInfo.style.display = displayValue;

            // ボタンのラベルを更新
            const label = this.hideUIButton?.querySelector('.hideui-label');
            if (label) {
                label.textContent = this.uiHiddenMode ? 'Show UI' : 'Hide UI';
            }
            
            // hideUIButtonは常に表示
            if (this.hideUIButton) {
                this.hideUIButton.style.display = 'flex';
            }
        }

        applyBlurStyles() {
            if (!this.container) return;
            const blurFilter = this.blurEnabled ? 'blur(16px)' : 'none';
            const blurOpacity = this.blurEnabled ? '0.85' : '';

            // メイン表示(動画/画像)
            const mediaWrapper = this.container.querySelector('.media-wrapper');
            if (mediaWrapper) {
                mediaWrapper.querySelectorAll('video, img').forEach(el => {
                    el.style.filter = blurFilter;
                    el.style.opacity = blurOpacity;
                });
            }

            // パネル内の画像
            const prevContent = this.container.querySelector('#prev-posts-content');
            const nextContent = this.container.querySelector('#next-posts-content');
            
            // 両方のパネルのimg要素を一度に取得して処理(querySelectorAll繰り返しを削減)
            if (prevContent) {
                prevContent.querySelectorAll('img.post-image').forEach(img => {
                    img.style.filter = blurFilter;
                    img.style.opacity = blurOpacity;
                });
            }
            if (nextContent) {
                nextContent.querySelectorAll('img.post-image').forEach(img => {
                    img.style.filter = blurFilter;
                    img.style.opacity = blurOpacity;
                });
            }
        }

        navigateToPost(targetArticleElement) {
            if (!targetArticleElement || this.isNavigating) return;
            this.isNavigating = true;
            this.stopAutoScrollTimer();
            this.restoreOriginalMediaPosition();
            this.showLoadingIndicator('Loading...');

            // ポスト移動のたびにフィルタリング非表示を実行
            this.hideNonFilteredPosts();

            if (!this.hasTargetContentWarning(targetArticleElement)) {
                this.isNavigating = false;
                this.hideLoadingIndicator();
                const fallback = this.findNextMediaArticle(targetArticleElement, false) || this.findNextMediaArticle(targetArticleElement, true);
                if (fallback) {
                    this.navigateToPost(fallback);
                }
                return;
            }

            const postTweetId = this.generateTweetId(targetArticleElement);
            const foundMediaItems = [];
            const addedElements = new Set();

            const extractMetadata = (contextEl, isMain) => {
                let author = 'Unknown User';
                let handle = '';
                let text = '';
                let icon = '';
                let timestampText = '';
                let timestampIso = '';
                let repostTimestampText = '';
                let repostTimestampIso = '';

                const authorEl = contextEl.querySelector('[data-testid="User-Name"] [dir="ltr"]');
                if (authorEl) {
                    author = authorEl.textContent;
                } else if (isMain) {
                    const mainAuthor = targetArticleElement.querySelector('[data-testid="User-Name"] [dir="ltr"]');
                    if (mainAuthor) author = mainAuthor.textContent;
                }

                const linkEl = contextEl.querySelector('[data-testid="User-Name"] a');
                if (linkEl) {
                    handle = linkEl.getAttribute('href');
                } else if (isMain) {
                    const mainLink = targetArticleElement.querySelector('[data-testid="User-Name"] a');
                    if (mainLink) handle = mainLink.getAttribute('href');
                } else if (!isMain && contextEl.getAttribute('role') === 'link') {
                     const quoteHref = contextEl.getAttribute('href');
                     if (quoteHref) {
                         const match = quoteHref.match(/^\/([^/]+)\/status\//);
                         if (match && match[1]) {
                             handle = '/' + match[1];
                         }
                     }
                }

                if (!handle) {
                     const userInfo = contextEl.querySelector('[data-testid="User-Name"]');
                     if (userInfo) {
                         const content = userInfo.textContent;
                         const match = content.match(/@([a-zA-Z0-9_]+)/);
                         if (match) {
                             handle = '/' + match[1];
                         }
                     }
                }

                if (handle && handle.startsWith('/')) handle = handle.substring(1);

                const textEl = contextEl.querySelector('[data-testid="tweetText"]');
                if (textEl) {
                    if (isMain) {
                        const quoteWrapper = textEl.closest('div[role="link"]');
                        if (quoteWrapper && quoteWrapper !== contextEl && targetArticleElement.contains(quoteWrapper)) {
                        }
                    }
                    text = textEl.textContent;
                }

                const iconEl = contextEl.querySelector('[data-testid="User-Name"] img[src*="profile_images"]') || targetArticleElement.querySelector('[data-testid="User-Name"] img[src*="profile_images"]');
                if (iconEl) icon = iconEl.src;

                const timeEl = contextEl.querySelector('time') || targetArticleElement.querySelector('time');
                if (timeEl) {
                    const iso = timeEl.getAttribute('datetime');
                    if (iso) {
                        const parsed = new Date(iso);
                        if (!isNaN(parsed.getTime())) {
                            timestampText = parsed.toLocaleString();
                            timestampIso = iso;
                        }
                    }
                    if (!timestampText) {
                        timestampText = timeEl.textContent ? timeEl.textContent.trim() : '';
                    }
                }

                const socialContextEl = targetArticleElement.querySelector('[data-testid="socialContext"]');
                const socialText = socialContextEl ? socialContextEl.textContent.trim() : '';
                const isRepost = /Reposted|Repost|リポスト/.test(socialText);
                if (isRepost) {
                    const repostTimeEl = socialContextEl ? socialContextEl.querySelector('time') : null;
                    const candidateTime = repostTimeEl || timeEl;
                    if (candidateTime) {
                        const iso = candidateTime.getAttribute('datetime');
                        if (iso) {
                            const parsed = new Date(iso);
                            if (!isNaN(parsed.getTime())) {
                                repostTimestampText = parsed.toLocaleString();
                                repostTimestampIso = iso;
                            }
                        }
                        if (!repostTimestampText) {
                            repostTimestampText = candidateTime.textContent ? candidateTime.textContent.trim() : '';
                        }
                    }
                }

                return { author, handle, text, icon, timestampText, timestampIso, repostTimestampText, repostTimestampIso };
            };

            const resolveContext = (mediaEl) => {
                let current = mediaEl.parentElement;
                while (current && current !== targetArticleElement) {
                    if (current.getAttribute('role') === 'link' && current.querySelector('[data-testid="User-Name"]')) {
                        return current;
                    }
                    current = current.parentElement;
                }
                return targetArticleElement;
            };

            targetArticleElement.querySelectorAll('div[data-testid="videoPlayer"] video, div[data-testid="tweetPhoto"] img').forEach(mediaEl => {
                if (addedElements.has(mediaEl)) return;

                const contextElement = resolveContext(mediaEl);
                const isMain = contextElement === targetArticleElement;
                const { author, handle, text, icon, timestampText, timestampIso, repostTimestampText, repostTimestampIso } = extractMetadata(contextElement, isMain);

                if (mediaEl.tagName === 'VIDEO' && this.isValidMedia(mediaEl)) {
                    foundMediaItems.push({
                        type: 'video',
                        originalElement: mediaEl,
                        src: mediaEl.src,
                        placeholder: null,
                        contextElement: contextElement,
                        author: author,
                        handle: handle,
                        text: text,
                        icon: icon,
                        timestampText: timestampText,
                        timestampIso: timestampIso,
                        repostTimestampText: repostTimestampText,
                        repostTimestampIso: repostTimestampIso
                    });
                    addedElements.add(mediaEl);
                } else if (mediaEl.tagName === 'IMG' && !mediaEl.src.includes('video_thumb') && this.isValidMedia(mediaEl)) {
                    foundMediaItems.push({
                        type: 'image',
                        originalElement: mediaEl,
                        src: this.getFullQualityImageUrl(mediaEl),
                        thumbSrc: mediaEl.src,
                        placeholder: null,
                        contextElement: contextElement,
                        author: author,
                        handle: handle,
                        text: text,
                        icon: icon,
                        timestampText: timestampText,
                        timestampIso: timestampIso,
                        repostTimestampText: repostTimestampText,
                        repostTimestampIso: repostTimestampIso
                    });
                    addedElements.add(mediaEl);
                }
            });

            if (foundMediaItems.length === 0) {
                console.warn('No valid media found in the target article, skipping to next.');
                this.isNavigating = false;
                this.hideLoadingIndicator();
                this.handlePostNavigation('next');
                return;
            }

            this.activePost = { id: postTweetId, element: targetArticleElement, mediaItems: foundMediaItems, currentMediaIndex: 0 };
            const nextStackTarget = this.findNextMediaArticle(targetArticleElement, false);
            this.stackTimelineForNextPost(nextStackTarget);
            document.body.style.scrollBehavior = 'auto';
            this.attemptScrollToPost(targetArticleElement);

            this.displayCurrentMediaItem();
            this.hideLoadingIndicator();
            this.isNavigating = false;
        }

        async attemptScrollToPost(element, attempts = 0, maxAttempts = 10, interval = 100) {
            if (!element || attempts >= maxAttempts) {
                console.warn('Failed to scroll post into view after multiple attempts.');
                return;
            }
            element.scrollIntoView({ behavior: 'auto', block: 'center' });
            await new Promise(resolve => setTimeout(resolve, interval));
            if (!this.isElementInViewport(element)) {
                this.attemptScrollToPost(element, attempts + 1, maxAttempts, interval);
            }
        }

        isElementInViewport(el) {
            if (!el) return false;
            const rect = el.getBoundingClientRect();
            const margin = 50;
            return (
                rect.top >= -margin &&
                rect.left >= -margin &&
                rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + margin &&
                rect.right <= (window.innerWidth || document.documentElement.clientWidth) + margin
            );
        }

        displayCurrentMediaItem() {
            if (!this.activePost || !this.activePost.mediaItems || this.activePost.mediaItems.length === 0) return;
            this.restoreOriginalMediaPosition();

            const mediaItemToDisplay = this.activePost.mediaItems[this.activePost.currentMediaIndex];
            if (!mediaItemToDisplay) return;

            this.updateUI();

            const mediaWrapper = this.container.querySelector('.media-wrapper');
            mediaWrapper.innerHTML = '';

            if (mediaItemToDisplay.type === 'video') {
                this.displayVideo(mediaItemToDisplay);
            } else {
                this.displayImage(mediaItemToDisplay);
            }

            // パネルが開いていたら自動更新
            if (this.prevPostsPanel && this.prevPostsPanel.style.display !== 'none') {
                this.showPrevPosts();
            }
            if (this.nextPostsPanel && this.nextPostsPanel.style.display !== 'none') {
                this.showNextPosts();
            }

            // ぼかし反映
            this.applyBlurStyles();

            // X Reels Auto Downloader に メディア変更を通知
            window.postMessage({
                type: 'xreels-media-changed',
                mediaType: mediaItemToDisplay.type,
                timestamp: Date.now()
            }, '*');

            this.startAutoScrollTimer();
        }

        displayVideo(mediaItem) {
            const mediaWrapper = this.container.querySelector('.media-wrapper');
            const videoElement = mediaItem.originalElement;

            // 可能なら最高画質ソースを選択
            this.selectHighestQualityVideo(videoElement);

            if (!mediaItem.placeholder) {
                const placeholder = document.createElement('div');
                const rect = videoElement.getBoundingClientRect();
                placeholder.style.width = `${rect.width}px`;
                placeholder.style.height = `${rect.height}px`;
                mediaItem.placeholder = placeholder;

                if (videoElement.parentElement) {
                    videoElement.parentElement.replaceChild(placeholder, videoElement);
                }
            }

            this.activeDisplayedMediaElement = videoElement;

            const videoContainer = document.createElement('div');
            videoContainer.className = 'custom-video-container';
            videoContainer.style.cssText = `
                position: relative;
                width: 100%;
                height: 100%;
                background: transparent;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
            `;

            videoElement.style.cssText = `
                width: 100%;
                height: 100%;
                object-fit: contain;
                border-radius: 0;
                display: block;
                background: transparent;
            `;

            // Smart mode での無限ループ防止: loop=false に設定
            // Smart mode ではビデオの ended イベントで次へ進むため、ループさせてはいけない
            videoElement.loop = (this.autoScrollDelay !== -1); // Smart mode の場合は false
            videoElement.muted = false; // 音声を有効に
            videoElement.controls = false;
            videoElement.volume = 0.05; // 5%に固定
            videoElement.playbackRate = this.savedPlaybackRate;

            // 現在のぼかし設定を反映
            if (this.blurEnabled) {
                videoElement.style.filter = 'blur(16px)';
                videoElement.style.opacity = '0.85';
            } else {
                videoElement.style.filter = 'none';
                videoElement.style.opacity = '';
            }

            const controlsOverlay = document.createElement('div');
            controlsOverlay.className = 'video-controls-overlay';
            controlsOverlay.style.cssText = `
                position: absolute;
                bottom: 0;
                left: 0;
                right: 0;
                background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.4) 50%, transparent 100%);
                padding: 12px 30px 12px 12px;
                opacity: 0;
                transition: opacity 0.3s ease;
                pointer-events: all;
                z-index: 1000010;
            `;

            const progressContainer = document.createElement('div');
            progressContainer.style.cssText = `
                width: 100%;
                height: 14px;
                background: transparent;
                border-radius: 10px;
                margin-bottom: 10px;
                cursor: pointer;
                pointer-events: all;
                position: relative;
                display: flex;
                align-items: center;
                padding: 5px 0;
            `;

            const progressBackground = document.createElement('div');
            progressBackground.style.cssText = `
                width: 100%;
                height: 4px;
                background: rgba(255,255,255,0.3);
                border-radius: 2px;
                position: relative;
            `;

            const progressBar = document.createElement('div');
            progressBar.className = 'progress-fill';
            progressBar.style.cssText = `
                height: 100%;
                background: linear-gradient(90deg, #FF6F61, #E63946);
                border-radius: 3px;
                width: 0%;
                transition: width 0.1s ease;
                position: relative;
            `;

            const progressThumb = document.createElement('div');
            progressThumb.style.cssText = `
                position: absolute;
                right: -5px;
                top: 50%;
                transform: translateY(-50%);
                width: 10px;
                height: 10px;
                background: #FF6F61;
                border-radius: 50%;
                opacity: 0;
                transition: opacity 0.3s ease;
                box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            `;

            progressBar.appendChild(progressThumb);
            progressBackground.appendChild(progressBar);
            progressContainer.appendChild(progressBackground);

            const controlsContainer = document.createElement('div');
            controlsContainer.style.cssText = `
                display: flex;
                align-items: center;
                gap: 15px;
                pointer-events: all;
            `;

            const playButton = document.createElement('button');
            playButton.innerHTML = `
                <svg class="play-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M8 5v14l11-7z" fill="currentColor"/>
                </svg>
                <svg class="pause-icon" style="display: none;" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M6 4h4v16H6V4zm8 0h4v16h-4V4z" fill="currentColor"/>
                </svg>
            `;
            playButton.style.cssText = `
                background: rgba(255,255,255,0.15);
                border: 1px solid rgba(255,255,255,0.2);
                color: white;
                font-size: 16px;
                cursor: pointer;
                padding: 0;
                border-radius: 50%;
                transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
                display: flex;
                align-items: center;
                justify-content: center;
                width: 36px;
                height: 36px;
                position: relative;
                overflow: hidden;
                backdrop-filter: blur(10px);
                box-shadow: 0 4px 12px rgba(0,0,0,0.3);
                flex-shrink: 0;
            `;

            const playButtonStyle = document.createElement('style');
            playButtonStyle.textContent = `
                .custom-video-container button:hover { background: rgba(255,255,255,0.25) !important; border-color: rgba(255,255,255,0.4) !important; transform: scale(1.1); box-shadow: 0 6px 20px rgba(0,0,0,0.4) !important; }
                .custom-video-container button:active { transform: scale(0.95); }
                .custom-video-container button::before { content: ''; position: absolute; inset: 0; border-radius: 50%; background: radial-gradient(circle, rgba(255,255,255,0.3) 0%, transparent 70%); opacity: 0; transition: opacity 0.3s ease; pointer-events: none; }
                .custom-video-container button:hover::before { opacity: 1; }
                .play-icon, .pause-icon { transition: all 0.3s ease; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3)); }
                .video-controls-overlay:hover .progress-fill div { opacity: 1; }
                .custom-video-container:hover .video-controls-overlay { opacity: 1; }
            `;
            document.head.appendChild(playButtonStyle);

            const timeDisplay = document.createElement('div');
            timeDisplay.style.cssText = `
                color: white;
                font-size: 11px;
                font-weight: 600;
                min-width: 70px;
                text-shadow: 0 1px 2px rgba(0,0,0,0.8);
            `;
            timeDisplay.textContent = '0:00 / 0:00';

            const speedButton = document.createElement('button');
            speedButton.innerHTML = '1.0x';
            speedButton.style.cssText = `
                background: rgba(255,255,255,0.15);
                border: 1px solid rgba(255,255,255,0.2);
                color: white;
                font-size: 11px;
                font-weight: 600;
                cursor: pointer;
                padding: 4px 8px;
                border-radius: 12px;
                transition: all 0.3s ease;
                min-width: 42px;
                text-align: center;
                flex-shrink: 0;
                backdrop-filter: blur(10px);
            `;
            speedButton.title = 'Playback Speed';

            const volumeContainer = document.createElement('div');
            volumeContainer.style.cssText = `
                display: flex;
                align-items: center;
                gap: 8px;
                margin-left: auto;
                margin-right: 25px;
                flex-shrink: 0;
            `;

            const muteButton = document.createElement('button');
            muteButton.innerHTML = '🔇';
            muteButton.style.cssText = `
                background: none;
                border: none;
                color: white;
                font-size: 14px;
                cursor: pointer;
                padding: 4px;
                border-radius: 4px;
                transition: background 0.3s ease;
                flex-shrink: 0;
            `;

            const volumeSlider = document.createElement('input');
            volumeSlider.type = 'range';
            volumeSlider.min = '0';
            volumeSlider.max = '100';
            volumeSlider.step = '1';
            volumeSlider.value = (this.savedVolume * 100).toString();
            volumeSlider.style.cssText = `
                width: 70px;
                height: 4px;
                background: rgba(255,255,255,0.3);
                border-radius: 2px;
                outline: none;
                cursor: pointer;
                -webkit-appearance: none;
                appearance: none;
                opacity: 0.5;
                flex-shrink: 0;
            `;

            const volumeSliderStyle = document.createElement('style');
            volumeSliderStyle.textContent = `
                .custom-video-container input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; border-radius: 50%; background: #FF6F61; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.3); }
                .custom-video-container input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: #FF6F61; cursor: pointer; border: none; box-shadow: 0 2px 4px rgba(0,0,0,0.3); }
            `;
            document.head.appendChild(volumeSliderStyle);

            volumeContainer.appendChild(muteButton);
            volumeContainer.appendChild(volumeSlider);

            controlsContainer.appendChild(playButton);
            controlsContainer.appendChild(timeDisplay);
            controlsContainer.appendChild(speedButton);
            controlsContainer.appendChild(volumeContainer);

            controlsOverlay.appendChild(progressContainer);
            controlsOverlay.appendChild(controlsContainer);

            videoContainer.appendChild(videoElement);
            videoContainer.appendChild(controlsOverlay);

            mediaWrapper.appendChild(videoContainer);

            let isDragging = false;

            const updatePlayPauseIcon = () => {
                const playIcon = playButton.querySelector('.play-icon');
                const pauseIcon = playButton.querySelector('.pause-icon');
                if (playIcon && pauseIcon) {
                    if (videoElement.paused) {
                        playIcon.style.display = 'block';
                        pauseIcon.style.display = 'none';
                    } else {
                        playIcon.style.display = 'none';
                        pauseIcon.style.display = 'block';
                    }
                }
            };

            const updateControlsUI = () => {
                muteButton.innerHTML = videoElement.muted ? '🔇' : '🔊';
                volumeSlider.style.opacity = videoElement.muted ? '0.5' : '1';
                volumeSlider.value = (videoElement.volume * 100).toString();
                updatePlayPauseIcon();
            };

            const togglePlayPause = () => {
                if (videoElement.paused) {
                    videoElement.play().catch(err => {
                        console.warn('Play failed:', err);
                        this.attemptVideoPlay(videoElement);
                    });
                } else {
                    // ユーザーが明示的に一時停止したことを記録
                    videoElement.pause();
                    videoElement.dataset.userPaused = 'true';
                }
            };

            playButton.addEventListener('click', (e) => {
                e.stopPropagation();
                if (videoElement.paused) {
                    delete videoElement.dataset.userPaused;
                }
                togglePlayPause();
            });

            videoElement.addEventListener('click', togglePlayPause);

            const toggleMute = () => {
                videoElement.muted = !videoElement.muted;
                this.savedMuted = videoElement.muted;
                localStorage.setItem('xreels_muted', this.savedMuted.toString());
            };

            videoElement.addEventListener('dblclick', (e) => {
                e.stopPropagation();
                toggleMute();
            });

            muteButton.addEventListener('click', (e) => {
                e.stopPropagation();
                toggleMute();
            });

            volumeSlider.addEventListener('input', (e) => {
                e.stopPropagation();
                const newVolume = parseFloat(e.target.value) / 100;
                videoElement.volume = newVolume;
                videoElement.muted = false;

                this.savedVolume = newVolume;
                localStorage.setItem('xreels_volume', this.savedVolume.toString());

                this.savedMuted = false;
                localStorage.setItem('xreels_muted', 'false');

                if (newVolume === 0 && !videoElement.muted) {
                    videoElement.muted = true;
                    this.savedMuted = true;
                    localStorage.setItem('xreels_muted', 'true');
                }
            });

            // UPDATED: Extended speed options
            const speedOptions = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 3.5, 4];
            speedButton.addEventListener('click', (e) => {
                e.stopPropagation();
                const currentIndex = speedOptions.indexOf(this.savedPlaybackRate);
                const nextIndex = (currentIndex + 1) % speedOptions.length;
                this.savedPlaybackRate = speedOptions[nextIndex];
                videoElement.playbackRate = this.savedPlaybackRate;
                speedButton.innerHTML = this.savedPlaybackRate.toFixed(2) + 'x';
                localStorage.setItem('xreels_playbackRate', this.savedPlaybackRate.toString());
            });

            const updateProgress = () => {
                if (!isDragging && videoElement.duration) {
                    const progress = (videoElement.currentTime / videoElement.duration) * 100;
                    progressBar.style.width = progress + '%';
                }
            };

            const updateTime = () => {
                const formatTime = (seconds) => {
                    const mins = Math.floor(seconds / 60);
                    const secs = Math.floor(seconds % 60);
                    return `${mins}:${secs.toString().padStart(2, '0')}`;
                };

                const current = formatTime(videoElement.currentTime || 0);
                const total = formatTime(videoElement.duration || 0);
                timeDisplay.textContent = `${current} / ${total}`;
            };

            videoElement.addEventListener('timeupdate', () => {
                updateProgress();
                updateTime();
            });

            videoElement.addEventListener('loadedmetadata', () => {
                updateTime();
                videoElement.playbackRate = this.savedPlaybackRate;
            });

            videoElement.addEventListener('loadeddata', () => {
                updateControlsUI();
                videoElement.playbackRate = this.savedPlaybackRate;
            });

            // UPDATED: Aggressive Speed Enforcement
            // Fixes issue where speed resets to 1.0x when videos loop or reload
            const enforceSpeed = () => {
                if (Math.abs(videoElement.playbackRate - this.savedPlaybackRate) > 0.01) {
                    videoElement.playbackRate = this.savedPlaybackRate;
                }
            };
            videoElement.addEventListener('ratechange', enforceSpeed);
            videoElement.addEventListener('playing', enforceSpeed);
            videoElement.addEventListener('play', enforceSpeed);

            const handleProgressClick = (e) => {
                const rect = progressBackground.getBoundingClientRect();
                const pos = (e.clientX - rect.left) / rect.width;
                if (videoElement.duration) {
                    videoElement.currentTime = pos * videoElement.duration;
                }
            };

            progressContainer.addEventListener('mousedown', (e) => {
                e.stopPropagation();
                isDragging = true;
                handleProgressClick(e);

                const handleMouseMove = (e) => {
                    if (isDragging) {
                        handleProgressClick(e);
                    }
                };

                const handleMouseUp = () => {
                    isDragging = false;
                    document.removeEventListener('mousemove', handleMouseMove);
                    document.removeEventListener('mouseup', handleMouseUp);
                };

                document.addEventListener('mousemove', handleMouseMove);
                document.addEventListener('mouseup', handleMouseUp);
            });

            let isHoveringControls = false;
            const controlsHoverZone = 150;
            videoContainer.addEventListener('mousemove', (e) => {
                const rect = videoContainer.getBoundingClientRect();
                const distanceFromBottom = rect.bottom - e.clientY;

                if (distanceFromBottom <= controlsHoverZone) {
                    if (!isHoveringControls) {
                        isHoveringControls = true;
                        controlsOverlay.style.opacity = '1';
                    }
                } else {
                    if (isHoveringControls) {
                        isHoveringControls = false;
                        if (!isDragging) {
                            controlsOverlay.style.opacity = '0';
                        }
                    }
                }
            });
            videoContainer.addEventListener('mouseleave', () => {
                isHoveringControls = false;
                if (!isDragging) {
                    controlsOverlay.style.opacity = '0';
                }
            });

            updateControlsUI();
            videoElement.addEventListener('volumechange', updateControlsUI);
            videoElement.addEventListener('play', updatePlayPauseIcon);
            videoElement.addEventListener('pause', updatePlayPauseIcon);

            // 動画が勝手に止まらないように監視
            const playbackMonitor = setInterval(() => {
                if (!this.isActive || this.activeDisplayedMediaElement !== videoElement) {
                    clearInterval(playbackMonitor);
                    return;
                }
                // ユーザーが明示的に一時停止した場合は再生しない
                if (videoElement.dataset.userPaused === 'true') {
                    return;
                }
                // ユーザー操作以外で止まった場合、再生を再開
                if (videoElement.paused && !videoElement.ended) {
                    console.log('Video paused unexpectedly, resuming playback...');
                    videoElement.play().catch(err => console.warn('Resume play failed:', err));
                }
            }, 1000); // 1秒ごとにチェック

            videoElement.playbackRate = this.savedPlaybackRate;
            speedButton.innerHTML = this.savedPlaybackRate.toFixed(2) + 'x';

            this.attemptVideoPlay(videoElement);
        }

        async simulateTwitterPlayClick(vidEl) {
            const videoPlayerContainer = vidEl.closest('div[data-testid="videoPlayer"]');
            if (videoPlayerContainer) {
                let playButton = videoPlayerContainer.querySelector(
                    'svg[role="img"][aria-label="Play"], ' +
                    'div[role="button"][tabindex="0"][aria-label*="Play"], ' +
                    'div[data-testid="playButton"], ' +
                    'div[data-testid="SensitiveMediaDisclosure"] button, ' +
                    'div[data-testid="videoPlayer"] > div > div > div[tabindex="0"], ' +
                    'div[data-testid="tweetPhoto"] div[role="button"], ' +
                    '[data-testid="ShyPinButton"], ' +
                    'div[data-testid="play-pause-button"]'
                );

                if (!playButton) {
                    playButton = videoPlayerContainer;
                    console.log('No specific play button found, falling back to clicking video container.');
                }

                if (playButton) {
                    console.log('Attempting to simulate click on Twitter\'s video player button/container:', playButton);
                    playButton.click();
                    await new Promise(resolve => setTimeout(resolve, 100));
                    return true;
                }
            }
            return false;
        }

        async attemptVideoPlay(video) {
            let playAttempts = 0;
            const maxPlayAttempts = 15;
            const attemptInterval = 500;

            while (playAttempts < maxPlayAttempts) {
                if (!this.isActive || this.activeDisplayedMediaElement !== video) {
                    console.log('Video no longer active or feed exited, stopping autoplay attempts.');
                    video.pause();
                    return;
                }

                if (!video.paused && video.muted === false) {
                    console.log('Video is playing at 5% volume. Autoplay successful.');
                    return;
                }

                console.log(`Autoplay attempt ${playAttempts + 1}/${maxPlayAttempts} for video. State: paused=${video.paused}, muted=${video.muted}`);

                if (video.paused) {
                    try {
                        if (video.currentTime >= video.duration - 0.5) {
                            video.currentTime = 0;
                        }
                        video.muted = false;
                        video.volume = 0.05; // 5%に固定
                        await video.play();
                        console.log('Direct play attempt successful at 5% volume.');
                        video.playbackRate = this.savedPlaybackRate;
                        video.dispatchEvent(new Event('volumechange'));
                    } catch (error) {
                        console.warn(`Direct play failed (attempt ${playAttempts + 1}):`, error.name, error.message);
                        await this.simulateTwitterPlayClick(video);
                    }
                } else {
                    video.muted = false;
                    video.volume = 0.05;
                    video.playbackRate = this.savedPlaybackRate;
                    video.dispatchEvent(new Event('volumechange'));
                }

                playAttempts++;
                await new Promise(resolve => setTimeout(resolve, attemptInterval));
            }

            console.warn('All aggressive autoplay attempts exhausted. Video might require manual interaction.');
            video.controls = true;
            video.muted = false;
            video.volume = 0.05;
            video.playbackRate = this.savedPlaybackRate;
            video.dispatchEvent(new Event('volumechange'));
        }

        displayImage(mediaItem) {
            const mediaWrapper = this.container.querySelector('.media-wrapper');
            const img = document.createElement('img');
            const previewSrc = mediaItem.thumbSrc || mediaItem.src;
            img.src = previewSrc;
            img.style.cssText = `
                width: 100%;
                height: 100%;
                object-fit: contain;
                border-radius: 0;
                background: transparent;
            `;
            if (this.blurEnabled) {
                img.style.filter = 'blur(16px)';
                img.style.opacity = '0.85';
            }
            mediaWrapper.appendChild(img);
            this.activeDisplayedMediaElement = img;

            if (mediaItem.src && mediaItem.src !== previewSrc) {
                const hiResImage = new Image();
                hiResImage.onload = () => {
                    img.src = mediaItem.src;
                    if (this.blurEnabled) {
                        img.style.filter = 'blur(16px)';
                        img.style.opacity = '0.85';
                    }
                };
                hiResImage.src = mediaItem.src;
            }
        }

        restoreOriginalMediaPosition() {
            if (this.activeDisplayedMediaElement) {
                if (this.activeDisplayedMediaElement.tagName === 'VIDEO' || this.activeDisplayedMediaElement.tagName === 'IMG') {
                    this.activeDisplayedMediaElement.style.cssText = '';

                    const currentMediaItemObject = this.activePost.mediaItems.find(item => item.originalElement === this.activeDisplayedMediaElement);
                    if (currentMediaItemObject && currentMediaItemObject.placeholder && currentMediaItemObject.placeholder.parentNode) {
                        currentMediaItemObject.placeholder.parentElement.replaceChild(this.activeDisplayedMediaElement, currentMediaItemObject.placeholder);
                    } else if (this.activeDisplayedMediaElement.parentNode) {
                        this.activeDisplayedMediaElement.remove();
                    }
                }
            }
            this.activeDisplayedMediaElement = null;
        }

        stackTimelineForNextPost(nextArticleElement) {
            if (!this.activePost || !this.activePost.element) return;

            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            const allowed = new Set([this.activePost.element]);
            if (nextArticleElement && this.hasTargetContentWarning(nextArticleElement)) {
                allowed.add(nextArticleElement);
            }

            allArticles.forEach(article => {
                // フィルタリングに通過しないポストは常に非表示
                if (!this.hasTargetContentWarning(article)) {
                    if (!this.hiddenPosts.includes(article)) {
                        this.hiddenPosts.push(article);
                    }
                    article.style.display = 'none';
                    return;
                }

                // フィルタリング通過ポストは表示。特別扱いが必要なら allowed に追加する
                article.style.display = '';
            });
        }

        async waitForNextPostBelowCurrent(maxWaitMs = 4000, pollInterval = 250) {
            if (!this.activePost || !this.activePost.element) return null;

            const deadline = Date.now() + maxWaitMs;
            while (Date.now() < deadline) {
                this.hideNonFilteredPosts();
                this.stackTimelineForNextPost(null);

                const candidate = this.findNextMediaArticle(this.activePost.element, false);
                if (candidate) {
                    return candidate;
                }

                await new Promise(resolve => setTimeout(resolve, pollInterval));
            }

            return null;
        }

        async handlePostNavigation(direction) {
            if (this.isNavigating) return;
            if (!this.activePost || !this.activePost.element) return;

            this.isNavigating = true;

            if (direction === 'prev') {
                const prevPostElement = this.findNextMediaArticle(this.activePost.element, true);
                this.isNavigating = false;
                if (prevPostElement) {
                    this.navigateToPost(prevPostElement);
                }
                return;
            }

            this.showLoadingIndicator('Waiting for next filtered post...');
            const nextPostElement = await this.waitForNextPostBelowCurrent();
            this.hideLoadingIndicator();
            this.isNavigating = false;

            if (nextPostElement) {
                this.stackTimelineForNextPost(nextPostElement);
                this.navigateToPost(nextPostElement);
            } else {
                console.log('No filtered next post available yet.');
            }
        }

        handleMediaNavigation(direction) {
            if (this.isNavigating || !this.activePost || !this.activePost.mediaItems || this.activePost.mediaItems.length <= 1) {
                return false;
            }
            let newMediaIndex = this.activePost.currentMediaIndex;
            let mediaNavigated = false;

            if (direction === 'next') {
                if (newMediaIndex < this.activePost.mediaItems.length - 1) {
                    newMediaIndex++;
                    this.activePost.currentMediaIndex = newMediaIndex;
                    this.displayCurrentMediaItem();
                    mediaNavigated = true;
                }
            } else if (direction === 'prev') {
                if (newMediaIndex > 0) {
                    newMediaIndex--;
                    this.activePost.currentMediaIndex = newMediaIndex;
                    this.displayCurrentMediaItem();
                    mediaNavigated = true;
                }
            }
            return mediaNavigated;
        }

        findNextMediaArticle(currentArticle, findPrevious = false) {
            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            const currentIndex = currentArticle ? allArticles.indexOf(currentArticle) : -1;
            let startIndex = findPrevious ? currentIndex - 1 : currentIndex + 1;
            let endIndex = findPrevious ? -1 : allArticles.length;
            let step = findPrevious ? -1 : 1;

            for (let i = startIndex; findPrevious ? i >= endIndex : i < endIndex; i += step) {
                const article = allArticles[i];
                if (!this.hasTargetContentWarning(article)) continue;
                if (article.querySelector('div[data-testid="videoPlayer"] video, div[data-testid="tweetPhoto"] img')) {
                    const mediaEls = article.querySelectorAll('div[data-testid="videoPlayer"] video, div[data-testid="tweetPhoto"] img');
                    for (const el of mediaEls) {
                        if (this.isValidMedia(el)) {
                            return article;
                        }
                    }
                }
            }
            return null;
        }

        findCentralMediaArticle() {
            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
            const viewportCenter = viewportHeight / 2;
            let closestArticle = null;
            let minDistance = Infinity;

            for (const article of allArticles) {
                if (!this.hasTargetContentWarning(article)) continue;
                const mediaElements = article.querySelectorAll('div[data-testid="videoPlayer"] video, div[data-testid="tweetPhoto"] img');
                let hasValidMedia = false;
                for (const el of mediaElements) {
                    if (this.isValidMedia(el)) {
                        hasValidMedia = true;
                        break;
                    }
                }

                if (hasValidMedia) {
                    const rect = article.getBoundingClientRect();
                    if (rect.bottom > 0 && rect.top < viewportHeight) {
                        const articleCenter = rect.top + rect.height / 2;
                        const distance = Math.abs(viewportCenter - articleCenter);
                        if (distance < minDistance) {
                            minDistance = distance;
                            closestArticle = article;
                        }
                    }
                }
            }
            return closestArticle;
        }

        getFullQualityImageUrl(imgElement) {
            if (!imgElement || !imgElement.src) return null;
            let src = imgElement.src;
            if (src.includes('pbs.twimg.com/media/')) {
                const url = new URL(src);
                url.searchParams.set('name', 'orig');
                src = url.href;
            }
            return src;
        }

        selectHighestQualityVideo(videoEl) {
            if (!videoEl) return;

            const candidates = [];

            const addCandidate = (src) => {
                if (!src) return;
                let score = 0;
                const resMatch = src.match(/(\d{3,4})x(\d{3,4})/);
                if (resMatch) {
                    const w = parseInt(resMatch[1], 10);
                    const h = parseInt(resMatch[2], 10);
                    score = w * h;
                }
                const pMatch = src.match(/(\d{3,4})p/);
                if (pMatch) {
                    score = Math.max(score, parseInt(pMatch[1], 10) * 1000);
                }
                const brMatch = src.match(/(?:bitrate|br|abr)[=\/](\d{3,6})/i);
                if (brMatch) {
                    score = Math.max(score, parseInt(brMatch[1], 10));
                }
                candidates.push({ src, score });
            };

            videoEl.querySelectorAll('source').forEach(source => addCandidate(source.src));
            addCandidate(videoEl.currentSrc);
            addCandidate(videoEl.src);

            if (candidates.length === 0) return;

            candidates.sort((a, b) => b.score - a.score);
            const best = candidates[0];
            if (best && best.src && videoEl.src !== best.src && videoEl.currentSrc !== best.src) {
                videoEl.src = best.src;
                videoEl.load();
            }
        }

        generateTweetId(tweet) {
            if (!tweet) return null;
            const link = tweet.querySelector('a[href*="/status/"][dir="ltr"], a[href*="/status/"]');
            if (link) {
                const match = link.href.match(/\/status\/(\d+)/);
                if (match && match[1]) return match[1];
            }
            return null;
        }

        isValidMedia(element) {
            if (!element) return false;

            if (element.tagName === 'VIDEO') {
                const container = element.closest('div[data-testid="videoPlayer"]');
                return container && element.readyState >= 0;
            } else if (element.tagName === 'IMG') {
                return element.src && element.src.includes('pbs.twimg.com/media/') && !element.src.includes('video_thumb');
            }
            return false;
        }

        hasTargetContentWarning(article) {
            if (!article) return false;
            const warningTexts = new Set([
                '内容の警告: 成人向けコンテンツ',
                '内容の警告: ヌード',
                '内容の警告: ヌードとセンシティブな内容'
            ]);

            return Array.from(article.querySelectorAll('div, span')).some(el => {
                const text = (el.textContent || '').trim();
                return warningTexts.has(text);
            });
        }

        hideNonFilteredPosts() {
            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            allArticles.forEach(article => {
                if (!this.hasTargetContentWarning(article)) {
                    // まだ非表示リストに入っていない場合のみ追加
                    if (!this.hiddenPosts.includes(article)) {
                        this.hiddenPosts.push(article);
                    }
                    article.style.display = 'none';
                }
            });
        }

        showHiddenPosts() {
            // 非表示にしたポストを全て再表示
            this.hiddenPosts.forEach(article => {
                article.style.display = '';
            });
            // 配列をクリア
            this.hiddenPosts = [];
        }

        setupEventListeners() {
            if (this.boundHandleWheel) {
                document.removeEventListener('wheel', this.boundHandleWheel, { passive: false, capture: true });
                document.removeEventListener('keydown', this.boundHandleKeydown, { capture: true });
                window.removeEventListener('popstate', this.boundHandlePopState);
            }

            this.boundHandleWheel = this.handleWheel.bind(this);
            this.boundHandleKeydown = this.handleKeydown.bind(this);
            this.boundHandlePopState = this.handlePopState.bind(this);

            document.addEventListener('wheel', this.boundHandleWheel, { passive: false, capture: true });
            document.addEventListener('keydown', this.boundHandleKeydown, { capture: true });
            window.addEventListener('popstate', this.boundHandlePopState);
        }

        handleWheel(e) {
            if (!this.isActive) return;
            e.preventDefault();
            e.stopPropagation();
            this.lastScrollTime = Date.now();

            const direction = e.deltaY > 0 ? 'next' : 'prev';
            const mediaNavigated = this.handleMediaNavigation(direction);

            if (!mediaNavigated) {
                this.handlePostNavigation(direction);
            }
        }

        handleKeydown(e) {
            if (e.key.toLowerCase() === 'x') {
                e.preventDefault();
                e.stopImmediatePropagation();
                if (this.isActive) {
                    this.exit();
                } else {
                    this.startFeed();
                }
                return;
            }

            if (!this.isActive) {
                return;
            }

            if (document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) {
                if (e.key.toLowerCase() === 'q') {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    this.exit();
                }
                return;
            }

            if (e.key === ' ') {
                e.preventDefault();
                e.stopPropagation();

                if (this.activeDisplayedMediaElement && this.activeDisplayedMediaElement.tagName === 'VIDEO') {
                    if (this.activeDisplayedMediaElement.paused) {
                        this.activeDisplayedMediaElement.play();
                    } else {
                        this.activeDisplayedMediaElement.pause();
                    }
                }
                return;
            }

            if (this.activeDisplayedMediaElement && this.activeDisplayedMediaElement.tagName === 'VIDEO') {
                const video = this.activeDisplayedMediaElement;
                const scrubAmount = 5;

                if (e.key === ',' || e.key === '<') {
                    e.preventDefault();
                    e.stopPropagation();
                    video.currentTime = Math.max(0, video.currentTime - scrubAmount);
                    return;
                } else if (e.key === '.' || e.key === '>') {
                    e.preventDefault();
                    e.stopPropagation();
                    video.currentTime = Math.min(video.duration, video.currentTime + scrubAmount);
                    return;
                }
            }

            const keyMap = {
                'q': () => this.exit(),
                'Q': () => this.exit(),
                'ArrowUp': () => { e.preventDefault(); e.stopPropagation(); this.handlePostNavigation('prev'); },
                'ArrowDown': () => { e.preventDefault(); e.stopPropagation(); this.handlePostNavigation('next'); },
                'ArrowLeft': () => { e.preventDefault(); e.stopPropagation(); this.handleMediaNavigation('prev'); },
                'ArrowRight': () => { e.preventDefault(); e.stopPropagation(); this.handleMediaNavigation('next'); },
                'l': () => { e.preventDefault(); e.stopPropagation(); this.toggleLike(); },
                'L': () => { e.preventDefault(); e.stopPropagation(); this.toggleLike(); },
                'f': () => { e.preventDefault(); e.stopPropagation(); this.toggleFollow(); },
                'F': () => { e.preventDefault(); e.stopPropagation(); this.toggleFollow(); }
            };

            if (keyMap[e.key]) {
                e.preventDefault();
                e.stopImmediatePropagation();
                keyMap[e.key]();
            }
        }

        handlePopState() {
            if (this.isActive && this.isReturningFromFollow) {
                console.log('Popstate detected, returning from follow action.');
                this.hideLoadingIndicator();
                this.isReturningFromFollow = false;
            }
        }

        disconnectObserver() {
            if (this.mutationObserver) {
                this.mutationObserver.disconnect();
                this.mutationObserver = null;
                console.log('MutationObserver disconnected.');
            }
        }

        toggleReplies() {
            if (this.prevPostsPanel.style.display === 'none' && this.nextPostsPanel.style.display === 'none') {
                this.showPrevPosts();
                this.showNextPosts();
            } else {
                this.hidePrevPosts();
                this.hideNextPosts();
            }
        }

        hidePrevPosts() {
            this.prevPostsPanel.style.display = 'none';
        }

        hideNextPosts() {
            this.nextPostsPanel.style.display = 'none';
        }

        async showPrevPosts() {
            if (!this.activePost.element) return;
            
            this.prevPostsPanel.style.display = 'block';
            const content = this.container.querySelector('#prev-posts-content');
            content.innerHTML = '<div style="text-align: center; padding: 10px; color: rgba(255,255,255,0.6); font-size: 11px;">読み込み中...</div>';

            const prevPosts = await this.fetchPrevPosts();
            
            if (prevPosts.length === 0) {
                content.innerHTML = '<div style="text-align: center; padding: 10px; color: rgba(255,255,255,0.6); font-size: 11px;">前のポストがありません</div>';
                return;
            }

            content.innerHTML = this.renderPosts(prevPosts, '#FF6F61');
            this.updatePrevPostsStyles();
            this.applyBlurStyles();
        }

        async showNextPosts() {
            if (!this.activePost.element) return;
            
            this.nextPostsPanel.style.display = 'block';
            const content = this.container.querySelector('#next-posts-content');
            content.innerHTML = '<div style="text-align: center; padding: 10px; color: rgba(255,255,255,0.6); font-size: 11px;">読み込み中...</div>';

            const nextPosts = await this.fetchNextPosts();
            
            if (nextPosts.length === 0) {
                content.innerHTML = '<div style="text-align: center; padding: 10px; color: rgba(255,255,255,0.6); font-size: 11px;">次のポストがありません</div>';
                return;
            }

            content.innerHTML = this.renderPosts(nextPosts, '#6496FF');
            this.updateNextPostsStyles();
            this.applyBlurStyles();
        }

        renderPosts(posts, borderColor) {
            let html = '';
            posts.forEach(post => {
                const isFiltered = post.hasWarning ? 'rgba(255, 215, 0, 0.5)' : borderColor;
                let mediaHtml = '';
                if (post.media && post.media.length > 0) {
                    mediaHtml = '<div class="panel-media-row" style="display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; align-items: flex-start;">';
                    post.media.forEach(mediaUrl => {
                        mediaHtml += `<img class="post-image" src="${mediaUrl}" style="width: auto; height: auto; max-width: 100%; border-radius: 6px; object-fit: cover; box-shadow: 0 2px 6px rgba(0,0,0,0.25);" />`;
                    });
                    mediaHtml += '</div>';
                }

                const iconHtml = post.icon ? `<img src="${post.icon}" style="width: 24px; height: 24px; border-radius: 50%; margin-right: 6px; object-fit: cover;" />` : '';
                
                html += `
                    <div class="panel-post" style="padding: 8px; margin-bottom: 6px; background: rgba(255,255,255,0.05); border-radius: 10px; border-left: 3px solid ${isFiltered}; transition: all 0.2s;" onmouseover="this.style.background='rgba(255,255,255,0.1)'" onmouseout="this.style.background='rgba(255,255,255,0.05)'">
                        <div style="display: flex; align-items: center; margin-bottom: 4px;">
                            ${iconHtml}
                            <div>
                                <div style="font-weight: 700; font-size: 11px; background: linear-gradient(135deg, #ffffff 0%, #e0e0e0 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">${post.author}</div>
                                <div style="color: rgba(255,255,255,0.5); font-size: 10px;">@${post.handle}</div>
                            </div>
                        </div>
                        <div class="post-text" style="font-size: 11px; line-height: 1.4; color: rgba(255,255,255,0.95); margin-bottom: 4px; text-shadow: 0 1px 2px rgba(0,0,0,0.3);">${post.text}</div>
                        ${mediaHtml}
                        <div style="display: flex; gap: 12px; margin-top: 6px; font-size: 10px; color: rgba(255,255,255,0.6);">
                            <span style="transition: color 0.2s;" onmouseover="this.style.color='rgba(100,200,255,0.9)'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">💬 ${post.replies || 0}</span>
                            <span style="transition: color 0.2s;" onmouseover="this.style.color='rgba(100,255,150,0.9)'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">🔁 ${post.retweets || 0}</span>
                            <span style="transition: color 0.2s;" onmouseover="this.style.color='rgba(255,100,150,0.9)'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">❤️ ${post.likes || 0}</span>
                            <span style="transition: color 0.2s;" onmouseover="this.style.color='rgba(255,200,100,0.9)'" onmouseout="this.style.color='rgba(255,255,255,0.6)'">🔖 ${post.bookmarks || 0}</span>
                        </div>
                    </div>
                `;
            });
            return html;
        }

        async fetchPrevPosts() {
            if (!this.activePost.element) return [];

            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            const currentIndex = allArticles.indexOf(this.activePost.element);
            
            const prevPosts = [];

            for (let i = currentIndex - 1; i >= Math.max(currentIndex - 6, 0); i--) {
                const article = allArticles[i];
                const post = this.extractPostInfo(article);
                if (post) {
                    prevPosts.unshift(post);
                    if (prevPosts.length >= 3) break;
                }
            }

            return prevPosts;
        }

        async fetchNextPosts() {
            if (!this.activePost.element) return [];

            const allArticles = Array.from(document.querySelectorAll('article[role="article"]'));
            const currentIndex = allArticles.indexOf(this.activePost.element);
            
            const nextPosts = [];

            for (let i = currentIndex + 1; i < Math.min(currentIndex + 7, allArticles.length); i++) {
                const article = allArticles[i];
                const post = this.extractPostInfo(article);
                if (post) {
                    nextPosts.push(post);
                    if (nextPosts.length >= 3) break;
                }
            }

            return nextPosts;
        }

        extractPostInfo(article) {
            if (!article) return null;

            const authorEl = article.querySelector('[data-testid="User-Name"] [dir="ltr"]');
            const handleEl = article.querySelector('[data-testid="User-Name"] a');
            const textEl = article.querySelector('[data-testid="tweetText"]');

            if (!authorEl || !textEl) return null;

            let handle = '';
            if (handleEl) {
                const href = handleEl.getAttribute('href');
                if (href) {
                    handle = href.replace('/', '');
                }
            }

            const media = [];
            const mediaElements = article.querySelectorAll('div[data-testid="tweetPhoto"] img');
            mediaElements.forEach(img => {
                if (img.src && !img.src.includes('profile_images')) {
                    media.push(img.src);
                }
            });

            const iconEl = article.querySelector('[data-testid="User-Name"] img[src*="profile_images"]');
            const icon = iconEl ? iconEl.src : '';

            const repliesEl = article.querySelector('[data-testid="reply"]');
            const replies = repliesEl ? this.extractCount(repliesEl) : 0;

            const retweetsEl = article.querySelector('[data-testid="retweet"]');
            const retweets = retweetsEl ? this.extractCount(retweetsEl) : 0;

            const likesEl = article.querySelector('[data-testid="like"], [data-testid="unlike"]');
            const likes = likesEl ? this.extractCount(likesEl) : 0;

            const bookmarksEl = article.querySelector('[data-testid="bookmark"], [data-testid="removeBookmark"]');
            const bookmarks = bookmarksEl ? this.extractCount(bookmarksEl) : 0;

            const hasWarning = this.hasTargetContentWarning(article);

            return {
                author: authorEl.textContent || 'Unknown',
                handle: handle || 'unknown',
                text: textEl.textContent || '',
                media: media,
                icon: icon,
                replies: replies,
                retweets: retweets,
                likes: likes,
                bookmarks: bookmarks,
                hasWarning: hasWarning
            };
        }

        extractCount(element) {
            if (!element) return 0;
            const countEl = element.querySelector('[data-testid$="-count"]');
            if (countEl) {
                const text = countEl.textContent.trim();
                if (text.includes('K')) {
                    return parseFloat(text.replace('K', '')) * 1000;
                } else if (text.includes('M')) {
                    return parseFloat(text.replace('M', '')) * 1000000;
                }
                return parseInt(text, 10) || 0;
            }
            return 0;
        }

        setupDraggable(panel, header, panelType) {
            let isDragging = false;
            let currentX, currentY, initialX, initialY;

            header.addEventListener('mousedown', (e) => {
                isDragging = true;
                initialX = e.clientX - panel.offsetLeft;
                initialY = e.clientY - panel.offsetTop;
            });

            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    e.preventDefault();
                    currentX = e.clientX - initialX;
                    currentY = e.clientY - initialY;
                    panel.style.left = currentX + 'px';
                    panel.style.top = currentY + 'px';
                    panel.style.bottom = 'auto';
                }
            });

            document.addEventListener('mouseup', () => {
                if (isDragging) {
                    isDragging = false;
                    this.savePanelState(panelType);
                }
            });
        }

        persistPanelSettings() {
            if (!this.panelSettings) return;
            localStorage.setItem('xreels_textSize', this.panelSettings.textSize.toString());
            localStorage.setItem('xreels_imagePercent', this.panelSettings.imagePercent.toString());
        }

        setupPanelSliders() {
            const prevTextSlider = this.container.querySelector('#prev-text-size-slider');
            const prevTextValue = this.container.querySelector('#prev-text-size-value');
            const prevImageSlider = this.container.querySelector('#prev-image-size-slider');
            const prevImageValue = this.container.querySelector('#prev-image-size-value');
            const prevSettingsBtn = this.container.querySelector('#prev-settings-btn');
            const prevSettingsPanel = this.container.querySelector('#prev-settings-panel');

            const nextTextSlider = this.container.querySelector('#next-text-size-slider');
            const nextTextValue = this.container.querySelector('#next-text-size-value');
            const nextImageSlider = this.container.querySelector('#next-image-size-slider');
            const nextImageValue = this.container.querySelector('#next-image-size-value');
            const nextSettingsBtn = this.container.querySelector('#next-settings-btn');
            const nextSettingsPanel = this.container.querySelector('#next-settings-panel');

            const clampText = (val) => Math.max(8, Math.min(18, parseInt(val, 10) || 11));
            const clampImagePercent = (val) => Math.max(20, Math.min(100, parseInt(val, 10) || 60));

            const syncTextDisplay = (size) => {
                if (prevTextSlider) prevTextSlider.value = size;
                if (nextTextSlider) nextTextSlider.value = size;
                if (prevTextValue) prevTextValue.textContent = size + 'px';
                if (nextTextValue) nextTextValue.textContent = size + 'px';
            };

            const syncImageDisplay = (percent) => {
                if (prevImageSlider) prevImageSlider.value = percent;
                if (nextImageSlider) nextImageSlider.value = percent;
                if (prevImageValue) prevImageValue.textContent = percent + '%';
                if (nextImageValue) nextImageValue.textContent = percent + '%';
            };

            const initialText = clampText(this.panelSettings.textSize);
            const initialImage = clampImagePercent(this.panelSettings.imagePercent);
            this.panelSettings.textSize = initialText;
            this.panelSettings.imagePercent = initialImage;

            syncTextDisplay(initialText);
            syncImageDisplay(initialImage);
            this.persistPanelSettings();

            const handleTextChange = (value) => {
                const size = clampText(value);
                this.panelSettings.textSize = size;
                this.persistPanelSettings();
                syncTextDisplay(size);
                this.updateAllPanelStyles();
            };

            const handleImageChange = (value) => {
                const percent = clampImagePercent(value);
                this.panelSettings.imagePercent = percent;
                this.persistPanelSettings();
                syncImageDisplay(percent);
                this.updateAllPanelStyles();
            };

            if (prevTextSlider) prevTextSlider.addEventListener('input', (e) => handleTextChange(e.target.value));
            if (nextTextSlider) nextTextSlider.addEventListener('input', (e) => handleTextChange(e.target.value));
            if (prevImageSlider) prevImageSlider.addEventListener('input', (e) => handleImageChange(e.target.value));
            if (nextImageSlider) nextImageSlider.addEventListener('input', (e) => handleImageChange(e.target.value));

            const toggleSettingsPanel = (panelToOpen, otherPanel) => {
                if (!panelToOpen) return;
                const shouldShow = panelToOpen.style.display === 'none' || panelToOpen.style.display === '';
                panelToOpen.style.display = shouldShow ? 'block' : 'none';
                if (otherPanel) otherPanel.style.display = 'none';
            };

            if (prevSettingsBtn && prevSettingsPanel) {
                prevSettingsBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    toggleSettingsPanel(prevSettingsPanel, nextSettingsPanel);
                });
            }

            if (nextSettingsBtn && nextSettingsPanel) {
                nextSettingsBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    toggleSettingsPanel(nextSettingsPanel, prevSettingsPanel);
                });
            }

            document.addEventListener('click', (e) => {
                if (prevSettingsPanel && !prevSettingsPanel.contains(e.target) && prevSettingsBtn && !prevSettingsBtn.contains(e.target)) {
                    prevSettingsPanel.style.display = 'none';
                }
                if (nextSettingsPanel && !nextSettingsPanel.contains(e.target) && nextSettingsBtn && !nextSettingsBtn.contains(e.target)) {
                    nextSettingsPanel.style.display = 'none';
                }
            });

            this.updateAllPanelStyles();
        }

        updatePanelStyles(panelType) {
            const clampedText = Math.max(8, this.panelSettings ? this.panelSettings.textSize : 11);
            const clampedPercent = Math.max(20, Math.min(100, this.panelSettings ? this.panelSettings.imagePercent : 60));
            const content = this.container ? this.container.querySelector(`#${panelType}-posts-content`) : null;
            if (!content) return;

            content.querySelectorAll('.post-text').forEach(el => {
                el.style.fontSize = `${clampedText}px`;
            });

            const fallbackWidth = content.clientWidth || content.offsetWidth || 0;
            content.querySelectorAll('.panel-post').forEach(postEl => {
                const baseWidth = postEl.clientWidth || fallbackWidth;
                const targetPx = baseWidth ? Math.round(baseWidth * (clampedPercent / 100)) : 0;
                postEl.querySelectorAll('.post-image').forEach(img => {
                    if (targetPx > 0) {
                        img.style.width = `${targetPx}px`;
                        img.style.maxWidth = `${targetPx}px`;
                        img.style.maxHeight = `${targetPx}px`;
                    } else {
                        img.style.width = `${clampedPercent}%`;
                        img.style.maxWidth = `${clampedPercent}%`;
                        img.style.maxHeight = `${clampedPercent}%`;
                    }
                    img.style.height = 'auto';
                });
            });
        }

        updatePrevPostsStyles() {
            this.updatePanelStyles('prev');
        }

        updateNextPostsStyles() {
            this.updatePanelStyles('next');
        }

        updateAllPanelStyles() {
            this.updatePrevPostsStyles();
            this.updateNextPostsStyles();
            this.applyMainTextStyles();
        }

        setupResizeObserver(panel, panelType) {
            if (!panel) return;
            const resizeObserver = new ResizeObserver(() => {
                this.savePanelState(panelType);
                if (panelType === 'prev') {
                    this.updatePrevPostsStyles();
                } else {
                    this.updateNextPostsStyles();
                }
            });
            resizeObserver.observe(panel);
        }

        savePanelState(panelType) {
            const panel = panelType === 'prev' ? this.prevPostsPanel : this.nextPostsPanel;
            const state = {
                left: panel.style.left,
                top: panel.style.top,
                width: panel.style.width || panel.offsetWidth + 'px',
                height: panel.style.height || panel.offsetHeight + 'px'
            };
            localStorage.setItem(`xreels_${panelType}PanelState`, JSON.stringify(state));
        }

        restorePanelState(panelType) {
            const panel = panelType === 'prev' ? this.prevPostsPanel : this.nextPostsPanel;
            const savedState = localStorage.getItem(`xreels_${panelType}PanelState`);
            if (savedState) {
                try {
                    const state = JSON.parse(savedState);
                    if (state.left) panel.style.left = state.left;
                    if (state.top) panel.style.top = state.top;
                    if (state.width) panel.style.width = state.width;
                    if (state.height) panel.style.height = state.height;
                    if (panelType === 'next' && state.top) {
                        panel.style.bottom = 'auto';
                    }
                } catch (e) {
                    console.error('Failed to restore panel state:', e);
                }
            }
        }
    }

    function onReady() {
        if (window.tikTokFeedInstance) {
            console.log('Existing TikTok Feed instance found, cleaning up...');
            if (window.tikTokFeedInstance.boundHandleWheel) {
                document.removeEventListener('wheel', window.tikTokFeedInstance.boundHandleWheel, { passive: false, capture: true });
                document.removeEventListener('keydown', window.tikTokFeedInstance.boundHandleKeydown, { capture: true });
                window.removeEventListener('popstate', window.tikTokFeedInstance.boundHandlePopState);
            }
            if (window.tikTokFeedInstance.isActive) {
                window.tikTokFeedInstance.exit();
            }
            if (window.tikTokFeedInstance.activeDisplayedMediaElement && window.tikTokFeedInstance.activeDisplayedMediaElement.tagName === 'VIDEO') {
                window.tikTokFeedInstance.activeDisplayedMediaElement.pause();
                window.tikTokFeedInstance.activeDisplayedMediaElement.muted = true;
            }

            window.tikTokFeedInstance.disconnectObserver();
            window.tikTokFeedInstance = null;
            console.log('Previous TikTok Feed instance cleaned up.');
        }

        window.tikTokFeedInstance = new TikTokFeed();
        window.tikTokFeedInstance.init();
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        onReady();
    } else {
        document.addEventListener('DOMContentLoaded', onReady);
    }
})();