OldRedditPlus

Old Reddit customization with overlay sidebar and dark mode(no data collection, all settings are stored locally)

当前为 2025-12-24 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         OldRedditPlus
// @name:en      OldRedditPlus
// @namespace    https://codeberg.org/Merlinsencho/oldredditplus
// @version      0.9.0
// @description  Old Reddit customization with overlay sidebar and dark mode(no data collection, all settings are stored locally)
// @description:en Old Reddit customization with overlay sidebar on the left of the screen and dark mode(no data collection, all settings are stored locally)
// @author       Merlinsencho
// @match        https://www.reddit.com/*
// @grant        GM_addStyle
// @run-at       document-end
// @noframes
// @copyright    2025, Merlinsencho
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    // CSS注入
    GM_addStyle(`
/* ===================================
   New Old Reddit Style - Overlay MVP
   =================================== */

:root {
    --bg-primary: #1a1a1b;
    --bg-secondary: #272729;
    --text-primary: #d7dadc;
    --text-secondary: #818384;
    --border-color: #343536;
    --link-color: #4fbcff;
    --hover-bg: #272729;
}

/* bodyへの干渉を最小化 - ダークモード時のみ背景色/文字色を適用 */
body.dark-mode {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
}

/* #headerへの干渉を削除 - サブレディット独自レイアウトを尊重 */

/* LEFTサイドバー - オーバーレイ */
#sr-header-area {
    position: fixed !important;
    left: -260px;
    top: 60px;  /* フォールバック値:JavaScriptで動的に更新 */
    width: 250px;
    height: calc(100vh - 60px);  /* フォールバック値:JavaScriptで動的に更新 */
    overflow-y: scroll !important;
    background: var(--bg-primary, #1a1a1b);
    border-right: 1px solid var(--border-color, #343536);
    z-index: 999;
    padding-top: 0;
    display: flex !important;
    flex-direction: column !important;
    transition: left 0.3s ease-in-out;
    box-shadow: 2px 0 8px rgba(0, 0, 0, 0.3);
}

body.show-left #sr-header-area {
    left: 0;
}

body.show-left::before {
    content: '';
    position: fixed;
    top: 0;
    left: 250px;  /* SUBSの幅分だけ右にずらして、SUBSエリアには背景を適用しない */
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 998;
    animation: fadeIn 0.3s ease-in-out;
    pointer-events: none;  /* 背景オーバーレイはクリック不可に(視覚効果のみ) */
}

/* 設定パネル表示時は半透明背景を無効化 */
#orp-settings-overlay.show ~ body.show-left::before,
body.show-left.settings-open::before {
    display: none;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* #header-bottom-left/right削除 - サブレディット依存要素への干渉回避 */

#sr-header-area .width-clip {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}

#sr-header-area .sr-list {
    background: var(--bg-primary, #1a1a1b);
    padding: 0;
    margin: 0;
    height: 100%;
    display: flex;
    flex-direction: column;
    flex: 1;
    overflow-y: auto;
}

#sr-header-area .sr-bar {
    display: flex !important;
    flex-direction: column !important;
    padding: 0;
    list-style: none;
    margin: 0;
    order: 1;
    flex: 1;
}

#sr-header-area .sr-bar li {
    display: block;
    margin: 0;
    padding: 0;
}

#sr-header-area .sr-bar a {
    display: block !important;
    padding: 10px 15px !important;
    color: var(--text-primary, #d7dadc) !important;
    text-decoration: none !important;
    transition: background 0.2s;
    border-bottom: 1px solid var(--border-color, #343536);
    font-size: 14px;
}

#sr-header-area .sr-bar a:hover {
    background: var(--hover-bg, #272729) !important;
}

#sr-header-area .separator,
#sr-header-area .dropdown,
#sr-header-area .srdrop {
    display: none !important;
}

#sr-header-area #sr-more-link {
    display: block;
    padding: 10px 15px;
    color: var(--link-color, #4fbcff);
    text-decoration: none;
    font-size: 12px;
    text-align: center;
    border-top: 1px solid var(--border-color, #343536);
    order: 3;
    margin-top: auto;
}

/* .contentへの干渉を削除 - サブレディット独自レイアウトを尊重 */

/* .thingをダークモード限定に - サブレディット独自デザインを尊重 */
body.dark-mode .thing {
    background: var(--bg-primary, #1a1a1b);
    border: 1px solid var(--border-color, #343536);
    border-radius: 4px;
    margin-bottom: 10px;
}

body.dark-mode .thing .title a {
    color: var(--text-primary, #d7dadc) !important;
}

body.dark-mode .thing .title a:visited {
    color: var(--text-secondary, #818384) !important;
}

/* ===================================
   コメントページ(アーティクル)用ダークモード
   =================================== */

/* コメントエリア全般 */
body.dark-mode .commentarea {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
}

/* コメント本体 */
body.dark-mode .comment {
    background: transparent !important;
    border-left: 1px solid var(--border-color) !important;
}

body.dark-mode .comment .entry {
    color: var(--text-primary) !important;
}

/* タグライン(投稿者情報) */
body.dark-mode .tagline,
body.dark-mode .comment .tagline {
    color: var(--text-secondary) !important;
}

body.dark-mode .tagline .author,
body.dark-mode .comment .tagline .author {
    color: var(--link-color) !important;
}

/* コメント本文のテキスト */
body.dark-mode .usertext-body,
body.dark-mode .usertext-body .md,
body.dark-mode .comment .usertext-body,
body.dark-mode .comment .md {
    background: transparent !important;
    color: var(--text-primary) !important;
}

body.dark-mode .usertext-body .md p,
body.dark-mode .comment .md p {
    color: var(--text-primary) !important;
}

/* コメント内のリンク */
body.dark-mode .usertext-body a,
body.dark-mode .comment .md a {
    color: var(--link-color) !important;
}

body.dark-mode .usertext-body a:visited,
body.dark-mode .comment .md a:visited {
    color: var(--text-secondary) !important;
}

body.dark-mode .usertext-body a:hover,
body.dark-mode .comment .md a:hover {
    color: var(--text-primary) !important;
}

/* コメント投稿フォーム */
body.dark-mode .usertext-edit {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode .usertext-edit textarea {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode .usertext-edit textarea:focus {
    background: var(--bg-secondary) !important;
    border-color: var(--link-color) !important;
}

/* フォーム下部エリア */
body.dark-mode .usertext-edit .bottom-area {
    background: var(--bg-secondary) !important;
    border-top-color: var(--border-color) !important;
}

body.dark-mode .usertext-edit .bottom-area a,
body.dark-mode .help-toggle a {
    color: var(--link-color) !important;
}

/* ボタン */
body.dark-mode .usertext-buttons button {
    background: var(--link-color) !important;
    color: #ffffff !important;
    border: none !important;
}

body.dark-mode .usertext-buttons button:hover {
    background: #3a9dd9 !important;
}

body.dark-mode .usertext-buttons .cancel {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
    border: 1px solid var(--border-color) !important;
}

body.dark-mode .usertext-buttons .cancel:hover {
    background: var(--hover-bg) !important;
}

/* フラットリストのボタン */
body.dark-mode .flat-list.buttons li a {
    color: var(--text-secondary) !important;
}

body.dark-mode .flat-list.buttons li a:hover {
    color: var(--link-color) !important;
}

/* エクスパンド領域 */
body.dark-mode .expando {
    background: transparent !important;
}

/* メニューエリア */
body.dark-mode .menuarea {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
}

/* ドロップダウン */
body.dark-mode .dropdown {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode .drop-choices {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode .drop-choices a {
    color: var(--text-primary) !important;
    background: var(--bg-secondary) !important;
}

body.dark-mode .drop-choices a:hover {
    background: var(--hover-bg) !important;
}

/* マークダウンヘルプ */
body.dark-mode .markhelp {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode .markhelp td {
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

/* パネルスタックタイトル */
body.dark-mode .panestack-title {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
}

body.dark-mode .panestack-title .title {
    color: var(--text-primary) !important;
}

/* siteTable */
body.dark-mode .sitetable {
    background: transparent !important;
}

/* エントリー */
body.dark-mode .entry {
    color: var(--text-primary) !important;
}

body.dark-mode .entry .title {
    color: var(--text-primary) !important;
}

/* ドメイン */
body.dark-mode .domain a {
    color: var(--text-secondary) !important;
}

/* スコア */
body.dark-mode .score {
    color: var(--text-primary) !important;
}

/* エラーメッセージ */
body.dark-mode .error {
    color: #ff6b6b !important;
}

/* ステータスメッセージ */
body.dark-mode .status {
    color: var(--text-primary) !important;
}

/* ===================================
   RIGHT側サイドバー用ダークモード(オプション)
   最小限の適用でサブレディット共通で使える安全策
   =================================== */

/* RIGHTサイドバー - 背景と基本テキスト色のみ(最小限) */
body.dark-mode.dark-mode-right .side {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
}

/* サイドバーの基本リンク色 */
body.dark-mode.dark-mode-right .side a {
    color: var(--link-color) !important;
}

/* 検索ボックス - 最小限の調整 */
body.dark-mode.dark-mode-right .side .search input[type="text"] {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right .side .search input[type="text"]:focus {
    border-color: var(--link-color) !important;
}

/* ===================================
   RIGHT側フルダークモード(警告:問題が起こりやすい)
   =================================== */

/* サイドボックス(各ウィジェット) */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .spacer {
    background: var(--bg-secondary) !important;
    border: 1px solid var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .spacer .titlebox {
    background: var(--bg-secondary) !important;
}

/* サイドバーのタイトル */
body.dark-mode.dark-mode-right.dark-mode-right-full .side h1,
body.dark-mode.dark-mode-right.dark-mode-right-full .side h2,
body.dark-mode.dark-mode-right.dark-mode-right-full .side h3,
body.dark-mode.dark-mode-right.dark-mode-right-full .side h4,
body.dark-mode.dark-mode-right.dark-mode-right-full .side h5,
body.dark-mode.dark-mode-right.dark-mode-right-full .side h6 {
    color: var(--text-primary) !important;
}

/* サイドバーのテキスト */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .usertext-body,
body.dark-mode.dark-mode-right.dark-mode-right-full .side .md {
    color: var(--text-primary) !important;
}

/* リンクホバー */
body.dark-mode.dark-mode-right.dark-mode-right-full .side a:hover {
    color: var(--text-primary) !important;
}

/* スポンサードリンク */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox .title h1 {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox .content {
    background: var(--bg-primary) !important;
}

/* 検索ボックス(フル版) */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .search input[type="text"]:focus {
    background: var(--bg-secondary) !important;
}

/* ログインフォーム */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .login-form-side {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .login-form-side input[type="text"],
body.dark-mode.dark-mode-right.dark-mode-right-full .side .login-form-side input[type="password"] {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

/* モデレーターリスト */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox .morelink {
    background: var(--link-color) !important;
    border-color: var(--link-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox .morelink:hover {
    background: #3a9dd9 !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidecontentbox .morelink a {
    color: #ffffff !important;
}

/* 購読ボタン */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .fancy-toggle-button {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .fancy-toggle-button .active {
    background: var(--link-color) !important;
}

/* サイドバーの投稿作成ボタン */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .sidebox.submit {
    background: var(--bg-secondary) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .morelink {
    background: var(--link-color) !important;
    border: none !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .morelink:hover {
    background: #3a9dd9 !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .morelink a {
    color: #ffffff !important;
}

/* フレアセレクター */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .flairselector {
    background: var(--bg-primary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .flairselector li {
    background: var(--bg-secondary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .flairselector li:hover {
    background: var(--hover-bg) !important;
}

/* モデレーター情報 */
body.dark-mode.dark-mode-right.dark-mode-right-full .side .icon-menu a {
    background: var(--bg-secondary) !important;
    color: var(--text-primary) !important;
}

body.dark-mode.dark-mode-right.dark-mode-right-full .side .icon-menu a:hover {
    background: var(--hover-bg) !important;
}

/* ===================================
   投稿フォーム用ダークモードスタイル
   =================================== */

/* 投稿ページ全体 */
body.dark-mode.submit-page {
    background: var(--bg-primary) !important;
}

/* 投稿フォームのコンテナ */
body.dark-mode .content[role="main"] {
    background: var(--bg-primary);
}

/* roundfield(入力フィールドのコンテナ) */
body.dark-mode .roundfield {
    background: var(--bg-secondary);
    border-color: var(--border-color);
}

body.dark-mode .roundfield-content {
    background: var(--bg-secondary);
}

/* spacer要素 */
body.dark-mode .spacer {
    background: transparent;
}

/* タイトルラベル */
body.dark-mode .roundfield .title {
    color: var(--text-primary);
}

/* 必須フィールドマーク */
body.dark-mode .required-roundfield {
    color: var(--text-primary);
}

/* 小さい説明文 */
body.dark-mode .little,
body.dark-mode .roundfield-description {
    color: var(--text-secondary);
}

/* テキストエリア・インプット */
body.dark-mode textarea,
body.dark-mode input[type="text"],
body.dark-mode input[type="url"] {
    background: var(--bg-primary) !important;
    color: var(--text-primary) !important;
    border-color: var(--border-color) !important;
}

body.dark-mode textarea:focus,
body.dark-mode input[type="text"]:focus,
body.dark-mode input[type="url"]:focus {
    background: var(--bg-secondary) !important;
    border-color: var(--link-color) !important;
}

/* ユーザーテキスト編集エリア */
body.dark-mode .usertext-edit {
    background: var(--bg-secondary);
    border-color: var(--border-color);
}

body.dark-mode .usertext-edit .md {
    background: var(--bg-primary);
}

/* マークダウンヘルプエリア */
body.dark-mode .usertext-edit .bottom-area {
    background: var(--bg-secondary);
    border-top-color: var(--border-color);
}

/* ヘルプトグル */
body.dark-mode .help-toggle a,
body.dark-mode .bottom-area a {
    color: var(--link-color);
}

body.dark-mode .help-toggle a:hover,
body.dark-mode .bottom-area a:hover {
    color: var(--text-primary);
}

/* マークダウンヘルプテーブル */
body.dark-mode .markhelp {
    background: var(--bg-secondary);
    border-color: var(--border-color);
}

body.dark-mode .markhelp td {
    color: var(--text-primary);
    border-color: var(--border-color);
}

/* 情報バー */
body.dark-mode .infobar {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-color: var(--border-color);
}

/* フォームタブ */
body.dark-mode .tabmenu.formtab {
    background: var(--bg-secondary);
}

body.dark-mode .tabmenu.formtab li a {
    background: var(--bg-secondary);
    color: var(--text-secondary);
    border-color: var(--border-color);
}

body.dark-mode .tabmenu.formtab li.selected a,
body.dark-mode .tabmenu.formtab li a:hover {
    background: var(--bg-primary);
    color: var(--text-primary);
}

/* フォームタブコンテンツ */
body.dark-mode .formtabs-content {
    background: var(--bg-primary);
}

/* ボタン類 */
body.dark-mode button,
body.dark-mode .c-btn {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-color: var(--border-color);
}

body.dark-mode button:hover,
body.dark-mode .c-btn:hover {
    background: var(--hover-bg);
}

body.dark-mode .c-btn-primary {
    background: var(--link-color);
    color: #ffffff;
}

body.dark-mode .c-btn-primary:hover {
    background: #3a9dd9;
}

/* サブレディット提案リスト */
body.dark-mode .sr-suggestion {
    color: var(--link-color);
}

body.dark-mode .sr-suggestion:hover {
    background: var(--hover-bg);
    color: var(--text-primary);
}

/* フレアプレビュー */
body.dark-mode .flair-preview {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-color: var(--border-color);
}

/* コレクションプロモ */
body.dark-mode .roundfield--with-padding {
    background: var(--bg-secondary);
}

body.dark-mode .roundfield--with-padding .usertext {
    color: var(--text-primary);
}

/* 送信テキスト */
body.dark-mode .submit_text h1 {
    color: var(--text-primary);
}

body.dark-mode .submit_text .content {
    color: var(--text-primary);
}

/* 左サイドバートグル - 左側の縦バー */
#orp-left-sidebar-toggle {
    position: fixed !important;
    left: 0 !important;
    top: 50% !important;
    transform: translateY(-50%) !important;
    z-index: 1001 !important;
    background: var(--bg-secondary, #272729) !important;
    border: 1px solid var(--border-color, #343536) !important;
    border-left: none !important;
    border-radius: 0 8px 8px 0 !important;
    padding: 15px 6px !important;
    cursor: pointer !important;
    font-size: 20px !important;
    color: var(--text-primary, #d7dadc) !important;
    transition: all 0.2s !important;
    writing-mode: vertical-rl !important;
    text-orientation: mixed !important;
    box-shadow: 2px 0 8px rgba(0, 0, 0, 0.3) !important;
    width: auto !important;
    max-width: 50px !important;
    height: auto !important;
    min-width: unset !important;
    min-height: unset !important;
    box-sizing: border-box !important;
}

#orp-left-sidebar-toggle:hover {
    background: var(--hover-bg, #343536) !important;
    padding-left: 10px !important;
}

/* 設定ボタン - 左サイドバー下部 */
#orp-settings-button {
    display: block;
    padding: 15px 15px;
    background: var(--link-color, #4fbcff);
    color: #ffffff;
    text-decoration: none;
    font-size: 14px;
    font-weight: bold;
    text-align: center;
    border-top: 2px solid var(--border-color, #343536);
    cursor: pointer;
    transition: background 0.2s;
    order: 4;
    margin-top: 0;
}

#orp-settings-button:hover {
    background: #3a9dd9;
}

/* 設定画面オーバーレイ */
#orp-settings-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: transparent;
    z-index: 10000;
    overflow-y: auto;
}

#orp-settings-overlay.show {
    display: flex;
    align-items: center;
    justify-content: center;
}

#orp-settings-panel {
    background: var(--bg-primary, #1a1a1b);
    border: 2px solid var(--border-color, #343536);
    border-radius: 8px;
    max-width: 600px;
    width: 90%;
    max-height: 80vh;
    overflow-y: auto;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}

#orp-settings-header {
    background: var(--bg-secondary, #272729);
    padding: 20px;
    border-bottom: 2px solid var(--border-color, #343536);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

#orp-settings-header h2 {
    margin: 0;
    color: var(--text-primary, #d7dadc);
    font-size: 20px;
}

#orp-settings-close {
    background: transparent;
    border: none;
    color: var(--text-primary, #d7dadc);
    font-size: 24px;
    cursor: pointer;
    padding: 0;
    width: 30px;
    height: 30px;
    line-height: 30px;
    text-align: center;
}

#orp-settings-close:hover {
    color: var(--link-color, #4fbcff);
}

#orp-settings-content {
    padding: 20px;
}

/* タブUI */
.orp-tabs {
    display: flex;
    border-bottom: 2px solid var(--border-color, #343536);
    background: var(--bg-secondary, #272729);
    margin: 0;
    padding: 0;
}

.orp-tab {
    flex: 1;
    padding: 15px 20px;
    background: transparent;
    border: none;
    color: var(--text-secondary, #818384);
    font-size: 14px;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.2s;
    border-bottom: 3px solid transparent;
}

.orp-tab:hover {
    background: var(--hover-bg, #343536);
    color: var(--text-primary, #d7dadc);
}

.orp-tab.active {
    color: var(--link-color, #4fbcff);
    border-bottom-color: var(--link-color, #4fbcff);
}

.orp-tab-content {
    display: none;
    padding: 20px;
}

.orp-tab-content.active {
    display: block;
}

/* メニュータブのレイアウト */
#tab-menu {
    display: none;
    padding: 0;
}

#tab-menu.active {
    display: flex;
    gap: 20px;
}

.orp-menu-settings {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
}

.orp-menu-preview {
    width: 250px;
    background: var(--bg-primary, #1a1a1b);
    border-left: 2px solid var(--border-color, #343536);
    padding: 20px 0;
    position: sticky;
    top: 0;
    align-self: flex-start;
    max-height: calc(80vh - 120px);
    overflow-y: auto;
}

.orp-preview-title {
    color: var(--text-secondary, #818384);
    font-size: 12px;
    font-weight: bold;
    padding: 0 15px 10px;
    border-bottom: 1px solid var(--border-color, #343536);
}

.orp-preview-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.orp-preview-item {
    display: block;
    padding: 10px 15px;
    color: var(--text-primary, #d7dadc);
    border-bottom: 1px solid var(--border-color, #343536);
    font-size: 14px;
}

.orp-preview-item:hover {
    background: var(--hover-bg, #272729);
}

.orp-setting-item {
    margin-bottom: 25px;
    padding-bottom: 25px;
    border-bottom: 1px solid var(--border-color, #343536);
}

.orp-setting-item:last-child {
    border-bottom: none;
}

.orp-setting-label {
    display: block;
    color: var(--text-primary, #d7dadc);
    font-weight: bold;
    margin-bottom: 10px;
    font-size: 16px;
}

.orp-setting-description {
    color: var(--text-secondary, #818384);
    font-size: 13px;
    margin-bottom: 10px;
}

.orp-toggle-switch {
    display: inline-block;
    width: 50px;
    height: 26px;
    background: var(--text-secondary, #818384);
    border-radius: 13px;
    position: relative;
    cursor: pointer;
    transition: background 0.3s;
}

.orp-toggle-switch.active {
    background: var(--link-color, #4fbcff);
}

.orp-toggle-switch::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    background: white;
    border-radius: 50%;
    transition: left 0.3s;
}

.orp-toggle-switch.active::after {
    left: 27px;
}

.orp-rgb-control {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}

.orp-rgb-label {
    width: 30px;
    font-weight: bold;
    color: var(--text-primary, #d7dadc);
}

.orp-rgb-spinner {
    display: flex;
    align-items: center;
    gap: 5px;
}

.orp-spinner-btn {
    background: var(--bg-secondary, #272729);
    border: 1px solid var(--border-color, #343536);
    color: var(--text-primary, #d7dadc);
    width: 30px;
    height: 30px;
    cursor: pointer;
    font-size: 16px;
    border-radius: 4px;
    transition: background 0.2s;
}

.orp-spinner-btn:hover {
    background: var(--hover-bg, #343536);
}

.orp-spinner-value {
    width: 50px;
    text-align: center;
    padding: 5px;
    background: var(--bg-secondary, #272729);
    border: 1px solid var(--border-color, #343536);
    border-radius: 4px;
    color: var(--text-primary, #d7dadc);
}

.orp-rgb-preview {
    width: 100px;
    height: 40px;
    border: 2px solid var(--border-color, #343536);
    border-radius: 4px;
    margin-top: 10px;
}

body.dark-mode {
    --bg-primary: #1a1a1b;
    --bg-secondary: #272729;
    --text-primary: #d7dadc;
    --text-secondary: #818384;
    --border-color: #343536;
}

body:not(.dark-mode) {
    --bg-primary: #ffffff;
    --bg-secondary: #f6f7f8;
    --text-primary: #1c1c1c;
    --text-secondary: #7c7c7c;
    --border-color: #ccc;
    --hover-bg: #e8e8e8;
}

#sr-header-area::-webkit-scrollbar {
    width: 12px;
}

#sr-header-area::-webkit-scrollbar-track {
    background: var(--bg-secondary, #272729);
}

#sr-header-area::-webkit-scrollbar-thumb {
    background: var(--text-secondary, #818384);
    border-radius: 6px;
    border: 2px solid var(--bg-primary, #1a1a1b);
}

#sr-header-area::-webkit-scrollbar-thumb:hover {
    background: var(--text-primary, #d7dadc);
}

#sr-header-area {
    scrollbar-width: thin;
    scrollbar-color: var(--text-secondary, #818384) var(--bg-secondary, #272729);
}
`);
    
    // JavaScript機能
    
    // ===================================
    // セキュリティユーティリティ関数
    // ===================================
    
    /**
     * HTMLエスケープ: XSS攻撃対策
     * @param {string} str - エスケープする文字列
     * @returns {string} エスケープ済み文字列
     */
    function sanitizeHTML(str) {
        if (typeof str !== 'string') return '';
        const escapeMap = {
            '<': '&lt;',
            '>': '&gt;',
            '&': '&amp;',
            '"': '&quot;',
            "'": '&#x27;'
        };
        return str.replace(/[<>&"']/g, char => escapeMap[char]);
    }
    
    /**
     * スクリプトタグ除去: XSS攻撃対策
     * @param {string} str - 処理する文字列
     * @returns {string} スクリプトタグを除去した文字列
     */
    function removeScriptTags(str) {
        if (typeof str !== 'string') return '';
        return str
            .replace(/<script[^>]*>.*?<\/script>/gi, '')
            .replace(/<iframe[^>]*>.*?<\/iframe>/gi, '')
            .replace(/on\w+\s*=\s*["'][^"']*["']/gi, '')
            .replace(/on\w+\s*=\s*[^\s>]*/gi, '')
            .replace(/javascript:/gi, '')
            .replace(/data:text\/html/gi, '');
    }
    
    /**
     * CSSサニタイズ: CSSインジェクション対策
     * @param {string} str - 処理する文字列
     * @returns {string} 危険なCSS構文を除去した文字列
     */
    function sanitizeCSS(str) {
        if (typeof str !== 'string') return '';
        return str
            .replace(/url\s*\(\s*["']?javascript:/gi, 'url(about:blank')
            .replace(/url\s*\(\s*["']?data:/gi, 'url(about:blank')
            .replace(/expression\s*\(/gi, 'invalid(')
            .replace(/@import/gi, '@invalid')
            .replace(/behavior\s*:/gi, 'invalid:')
            .replace(/binding\s*:/gi, 'invalid:')
            .replace(/-moz-binding/gi, 'invalid')
            .replace(/vbscript:/gi, 'invalid:');
    }
    
    /**
     * 数値検証: 範囲チェックとNaN対策
     * @param {any} value - 検証する値
     * @param {number} min - 最小値
     * @param {number} max - 最大値
     * @param {number} defaultValue - デフォルト値
     * @param {string} key - 設定キー名(警告用)
     * @returns {number} 検証済みの数値
     */
    function sanitizeNumericValue(value, min, max, defaultValue, key = '') {
        const parsed = parseInt(value, 10);
        
        if (isNaN(parsed)) {
            if (key && value !== null && value !== undefined && value !== '') {
                console.warn(`[NOR Security] NaN値を検出: ${key}="${value}" → デフォルト値${defaultValue}を使用`);
            }
            return defaultValue;
        }
        
        if (parsed < min || parsed > max) {
            if (key) {
                console.warn(`[NOR Security] 範囲外の値を検出: ${key}=${parsed} (範囲: ${min}-${max}) → ${Math.max(min, Math.min(max, parsed))}に修正`);
            }
            return Math.max(min, Math.min(max, parsed));
        }
        
        return parsed;
    }
    
    /**
     * 文字列サニタイズチェーン: HTML/Script/CSSの全サニタイズを適用
     * @param {string} str - 処理する文字列
     * @returns {string} サニタイズ済み文字列
     */
    function sanitizeString(str) {
        if (typeof str !== 'string') return '';
        let sanitized = sanitizeHTML(str);
        sanitized = removeScriptTags(sanitized);
        sanitized = sanitizeCSS(sanitized);
        return sanitized;
    }
    
    // ===================================
    // localStorage安全アクセス
    // ===================================
    
    const storage = {
        get(key, defaultValue = null) {
            try {
                const value = localStorage.getItem(key);
                if (value === null) return defaultValue;
                
                // Boolean値の場合
                if (defaultValue === true || defaultValue === false) {
                    return value === 'true';
                }
                
                // 文字列の場合はサニタイズ適用(二重防御)
                if (typeof defaultValue === 'string') {
                    const sanitized = sanitizeString(value);
                    if (sanitized !== value && sanitized.length < value.length) {
                        console.warn(`[NOR Security] 不正な値を検出してサニタイズ: ${key}`);
                    }
                    return sanitized;
                }
                
                // その他(数値検証は呼び出し側で実施)
                return value;
            } catch (error) {
                console.warn('[NOR] localStorage read failed:', error);
                return defaultValue;
            }
        },
        
        set(key, value) {
            try {
                let sanitizedValue = String(value);
                
                // サイズチェック
                if (sanitizedValue.length > 10000) {
                    console.warn(`[NOR Security] 値が大きすぎます(${sanitizedValue.length}文字): ${key} → 10000文字に切り捨て`);
                    sanitizedValue = sanitizedValue.substring(0, 10000);
                }
                
                // Boolean以外の文字列の場合はサニタイズ
                if (value !== true && value !== false && value !== 'true' && value !== 'false') {
                    const originalLength = sanitizedValue.length;
                    sanitizedValue = sanitizeString(sanitizedValue);
                    if (sanitizedValue.length < originalLength) {
                        console.warn(`[NOR Security] 不正な内容を除去して保存: ${key}`);
                    }
                    
                    // サニタイズで空になった場合
                    if (sanitizedValue.trim() === '' && originalLength > 0) {
                        console.warn(`[NOR Security] サニタイズ後に空文字列になったため保存をスキップ: ${key}`);
                        return;
                    }
                }
                
                localStorage.setItem(key, sanitizedValue);
            } catch (error) {
                console.warn('[NOR] localStorage write failed:', error);
            }
        }
    };
    
    function createToggleButtons() {
        // 左サイドバートグルボタン(画面左側の縦バー)
        const leftToggleBtn = document.createElement('button');
        leftToggleBtn.id = 'orp-left-sidebar-toggle';
        leftToggleBtn.textContent = '☰';
        leftToggleBtn.title = 'LEFTサイドバーの表示/非表示';
        leftToggleBtn.setAttribute('type', 'button');
        document.body.appendChild(leftToggleBtn);
        
        // 左サイドバーに設定ボタンを追加
        const sidebar = document.querySelector('#sr-header-area');
        if (sidebar) {
            const settingsBtn = document.createElement('button');
            settingsBtn.id = 'orp-settings-button';
            settingsBtn.textContent = '⚙️ 設定';
            settingsBtn.title = '設定画面を開く';
            settingsBtn.setAttribute('type', 'button');
            
            const widthClip = sidebar.querySelector('.width-clip');
            if (widthClip) {
                widthClip.appendChild(settingsBtn);
            }
        }
        
        return { leftToggleBtn };
    }
    
    function createSettingsPanel() {
        const overlay = document.createElement('div');
        overlay.id = 'orp-settings-overlay';
        overlay.innerHTML = `
            <div id="orp-settings-panel">
                <div id="orp-settings-header">
                    <h2>Oldredditplus設定画面</h2>
                    <button id="orp-settings-close" type="button">×</button>
                </div>
                <div class="orp-tabs">
                    <button class="orp-tab active" data-tab="menu-button">メニューボタン</button>
                    <button class="orp-tab" data-tab="menu">メニュー</button>
                </div>
                <div id="tab-menu-button" class="orp-tab-content active">
                    <div class="orp-setting-item">
                        <label class="orp-setting-label">ダークモード</label>
                        <div class="orp-setting-description">荒ぶり要素 ダークモードのオン/オフを切り替えます(RIGHT側はなるべく除外)</div>
                        <div id="orp-darkmode-toggle" class="orp-toggle-switch"></div>
                    </div>
                    <div class="orp-setting-item">
                        <label class="orp-setting-label">RIGHT側ダークモード</label>
                        <div class="orp-setting-description">RIGHT側サイドバーにも最小限のダークモードを適用します(サブレディット共通で安全な範囲のみ)</div>
                        <div id="orp-darkmode-right-toggle" class="orp-toggle-switch"></div>
                    </div>
                    <div class="orp-setting-item" id="orp-darkmode-right-full-container" style="display: none;">
                        <label class="orp-setting-label">⚠️ フルダークモードを適用</label>
                        <div class="orp-setting-description" style="color: #ff6b6b;">問題が起こりやすいです:サブレディット固有のデザインが崩れる可能性があります</div>
                        <div id="orp-darkmode-right-full-toggle" class="orp-toggle-switch"></div>
                    </div>
                    <div class="orp-setting-item">
                        <label class="orp-setting-label">メニューボタンの色設定</label>
                        <div class="orp-setting-description">左サイドバートグルボタンの背景色を設定します(5段階で調整)</div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label" style="color: #ff0000;">R:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-r-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-r-value">0</span>
                                <button class="orp-spinner-btn" id="orp-r-plus" type="button">+</button>
                            </div>
                        </div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label" style="color: #00ff00;">G:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-g-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-g-value">0</span>
                                <button class="orp-spinner-btn" id="orp-g-plus" type="button">+</button>
                            </div>
                        </div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label" style="color: #0088ff;">B:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-b-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-b-value">0</span>
                                <button class="orp-spinner-btn" id="orp-b-plus" type="button">+</button>
                            </div>
                        </div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label" style="color: #ffffff;">A:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-a-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-a-value">100</span>
                                <button class="orp-spinner-btn" id="orp-a-plus" type="button">+</button>
                            </div>
                            <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">%</span>
                        </div>
                        <div class="orp-setting-description">プレビュー:</div>
                        <div id="orp-rgb-preview" class="orp-rgb-preview"></div>
                    </div>
                    <div class="orp-setting-item">
                        <label class="orp-setting-label">メニューボタンのサイズ設定</label>
                        <div class="orp-setting-description">左サイドバートグルボタンの幅と高さを設定します</div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label">幅:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-width-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-width-value">50</span>
                                <button class="orp-spinner-btn" id="orp-width-plus" type="button">+</button>
                            </div>
                            <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                        </div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label">高さ:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-height-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-height-value">200</span>
                                <button class="orp-spinner-btn" id="orp-height-plus" type="button">+</button>
                            </div>
                            <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                        </div>
                        <button id="orp-size-reset" class="orp-spinner-btn" type="button" style="margin-top: 10px; width: auto; padding: 5px 15px;">規定値にリセット</button>
                    </div>
                    <div class="orp-setting-item">
                        <label class="orp-setting-label">上下オフセット設定</label>
                        <div class="orp-setting-description">メニューボタンの上下位置を調整します(マイナスで上、プラスで下)</div>
                        <div class="orp-rgb-control">
                            <span class="orp-rgb-label">位置:</span>
                            <div class="orp-rgb-spinner">
                                <button class="orp-spinner-btn" id="orp-offset-minus" type="button">−</button>
                                <span class="orp-spinner-value" id="orp-offset-value">0</span>
                                <button class="orp-spinner-btn" id="orp-offset-plus" type="button">+</button>
                            </div>
                            <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                        </div>
                        <button id="orp-offset-reset" class="orp-spinner-btn" type="button" style="margin-top: 10px; width: auto; padding: 5px 15px;">規定値にリセット</button>
                    </div>
                </div>
                <div id="tab-menu" class="orp-tab-content">
                    <div class="orp-menu-settings">
                        <div class="orp-setting-item">
                            <label class="orp-setting-label">SUBS文字サイズ</label>
                            <div class="orp-setting-description">サブレディット名の文字サイズを設定します</div>
                            <div class="orp-rgb-control">
                                <span class="orp-rgb-label">サイズ:</span>
                                <div class="orp-rgb-spinner">
                                    <button class="orp-spinner-btn" id="orp-subs-font-size-minus" type="button">−</button>
                                    <span class="orp-spinner-value" id="orp-subs-font-size-value">14</span>
                                    <button class="orp-spinner-btn" id="orp-subs-font-size-plus" type="button">+</button>
                                </div>
                                <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                                <button id="orp-subs-font-size-reset" class="orp-spinner-btn" type="button" style="width: auto; padding: 5px 15px; margin-left: 10px;">規定値</button>
                            </div>
                        </div>
                        <div class="orp-setting-item">
                            <label class="orp-setting-label">SUBS行の上下マージン</label>
                            <div class="orp-setting-description">サブレディット行の上下マージンを設定します</div>
                            <div class="orp-rgb-control">
                                <span class="orp-rgb-label">マージン:</span>
                                <div class="orp-rgb-spinner">
                                    <button class="orp-spinner-btn" id="orp-subs-margin-minus" type="button">−</button>
                                    <span class="orp-spinner-value" id="orp-subs-margin-value">10</span>
                                    <button class="orp-spinner-btn" id="orp-subs-margin-plus" type="button">+</button>
                                </div>
                                <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                                <button id="orp-subs-margin-reset" class="orp-spinner-btn" type="button" style="width: auto; padding: 5px 15px; margin-left: 10px;">規定値</button>
                            </div>
                        </div>
                        <div class="orp-setting-item">
                            <label class="orp-setting-label">SUBS上端オフセット</label>
                            <div class="orp-setting-description">サブレディットリストの上端位置を調整します</div>
                            <div class="orp-rgb-control">
                                <span class="orp-rgb-label">オフセット:</span>
                                <div class="orp-rgb-spinner">
                                    <button class="orp-spinner-btn" id="orp-subs-top-offset-minus" type="button">−</button>
                                    <span class="orp-spinner-value" id="orp-subs-top-offset-value">0</span>
                                    <button class="orp-spinner-btn" id="orp-subs-top-offset-plus" type="button">+</button>
                                </div>
                                <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                                <button id="orp-subs-top-offset-reset" class="orp-spinner-btn" type="button" style="width: auto; padding: 5px 15px; margin-left: 10px;">規定値</button>
                            </div>
                        </div>
                        <div class="orp-setting-item">
                            <label class="orp-setting-label">SUBS文字列の左右オフセット</label>
                            <div class="orp-setting-description">サブレディット名の左右位置を調整します</div>
                            <div class="orp-rgb-control">
                                <span class="orp-rgb-label">オフセット:</span>
                                <div class="orp-rgb-spinner">
                                    <button class="orp-spinner-btn" id="orp-subs-lr-offset-minus" type="button">−</button>
                                    <span class="orp-spinner-value" id="orp-subs-lr-offset-value">0</span>
                                    <button class="orp-spinner-btn" id="orp-subs-lr-offset-plus" type="button">+</button>
                                </div>
                                <span style="color: var(--text-secondary); font-size: 12px; margin-left: 5px;">px</span>
                                <button id="orp-subs-lr-offset-reset" class="orp-spinner-btn" type="button" style="width: auto; padding: 5px 15px; margin-left: 10px;">規定値</button>
                            </div>
                        </div>
                    </div>
                    <div class="orp-menu-preview">
                        <div class="orp-preview-title">プレビュー</div>
                        <ul class="orp-preview-list" id="orp-preview-list">
                            <li class="orp-preview-item">TEST1</li>
                            <li class="orp-preview-item">TEST2</li>
                            <li class="orp-preview-item">TEST3</li>
                            <li class="orp-preview-item">TEST4</li>
                            <li class="orp-preview-item">TEST5</li>
                        </ul>
                    </div>
                </div>
            </div>
        `;
        document.body.appendChild(overlay);
        return overlay;
    }
    
    function initSettingsPanel() {
        const overlay = createSettingsPanel();
        const closeBtn = document.getElementById('orp-settings-close');
        const settingsBtn = document.getElementById('orp-settings-button');
        const darkModeToggle = document.getElementById('orp-darkmode-toggle');
        
        // タブ切り替え機能
        const tabs = document.querySelectorAll('.orp-tab');
        const tabContents = document.querySelectorAll('.orp-tab-content');
        
        tabs.forEach(tab => {
            tab.addEventListener('click', function() {
                const targetTab = this.dataset.tab;
                
                // すべてのタブとコンテンツから active クラスを削除
                tabs.forEach(t => t.classList.remove('active'));
                tabContents.forEach(tc => tc.classList.remove('active'));
                
                // クリックされたタブとそのコンテンツに active クラスを追加
                this.classList.add('active');
                document.getElementById(`tab-${targetTab}`).classList.add('active');
            });
        });
        
        // ダークモードの初期状態を反映
        const isDarkMode = storage.get('darkMode') === 'true';
        if (isDarkMode) {
            darkModeToggle.classList.add('active');
        }
        
        // RIGHT側ダークモードの初期化
        const darkModeRightToggle = document.getElementById('orp-darkmode-right-toggle');
        const darkModeRightFullToggle = document.getElementById('orp-darkmode-right-full-toggle');
        const darkModeRightFullContainer = document.getElementById('orp-darkmode-right-full-container');
        
        const isDarkModeRight = storage.get('darkModeRight') === 'true';
        if (isDarkModeRight) {
            darkModeRightToggle.classList.add('active');
            document.body.classList.add('dark-mode-right');
            // RIGHT側ダークモードがONの場合、フルダークモード設定を表示
            darkModeRightFullContainer.style.display = '';
        }
        
        // RIGHT側フルダークモードの初期化
        const isDarkModeRightFull = storage.get('darkModeRightFull') === 'true';
        if (isDarkModeRightFull && isDarkModeRight) {
            darkModeRightFullToggle.classList.add('active');
            document.body.classList.add('dark-mode-right-full');
        }
        
        // 設定ボタンクリックで表示
        if (settingsBtn) {
            settingsBtn.addEventListener('click', function(e) {
                e.stopPropagation();
                overlay.classList.add('show');
                document.body.classList.add('settings-open');
                // 左サイドバーを閉じる
                document.body.classList.remove('show-left');
                storage.set('leftSidebarVisible', false);
                const leftToggle = document.getElementById('orp-left-sidebar-toggle');
                if (leftToggle) leftToggle.style.display = '';
            });
        }
        
        // 閉じるボタン
        closeBtn.addEventListener('click', function() {
            overlay.classList.remove('show');
            document.body.classList.remove('settings-open');
            // SUBSを閉じる
            document.body.classList.remove('show-left');
        });
        
        // オーバーレイの外側クリックで閉じる
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) {
                overlay.classList.remove('show');
                document.body.classList.remove('settings-open');
                // SUBSを閉じる
                document.body.classList.remove('show-left');
            }
        });
        
        // ダークモードトグル
        darkModeToggle.addEventListener('click', function() {
            darkModeToggle.classList.toggle('active');
            const isNowDark = darkModeToggle.classList.contains('active');
            document.body.classList.toggle('dark-mode', isNowDark);
            storage.set('darkMode', isNowDark);
        });
        
        // RIGHT側ダークモードトグル
        darkModeRightToggle.addEventListener('click', function() {
            darkModeRightToggle.classList.toggle('active');
            const isNowDarkRight = darkModeRightToggle.classList.contains('active');
            document.body.classList.toggle('dark-mode-right', isNowDarkRight);
            storage.set('darkModeRight', isNowDarkRight);
            
            // フルダークモード設定の表示/非表示を切り替え
            if (isNowDarkRight) {
                darkModeRightFullContainer.style.display = '';
            } else {
                darkModeRightFullContainer.style.display = 'none';
                // RIGHT側ダークモードをOFFにした場合、フルダークモードも自動的にOFF
                if (darkModeRightFullToggle.classList.contains('active')) {
                    darkModeRightFullToggle.classList.remove('active');
                    document.body.classList.remove('dark-mode-right-full');
                    storage.set('darkModeRightFull', false);
                }
            }
        });
        
        // RIGHT側フルダークモードトグル
        darkModeRightFullToggle.addEventListener('click', function() {
            darkModeRightFullToggle.classList.toggle('active');
            const isNowDarkRightFull = darkModeRightFullToggle.classList.contains('active');
            document.body.classList.toggle('dark-mode-right-full', isNowDarkRightFull);
            storage.set('darkModeRightFull', isNowDarkRightFull);
        });
        
        // RGB設定の初期化
        initRGBSettings();
        
        // メニュー設定の初期化
        initMenuSettings();
    }
    
    function initRGBSettings() {
        const leftToggleBtn = document.getElementById('orp-left-sidebar-toggle');
        
        // RGB値を0-255の範囲で、5刻みで管理(セキュリティ検証付き)
        let rValue = sanitizeNumericValue(storage.get('menuColorR', '39'), 0, 255, 39, 'menuColorR');
        let gValue = sanitizeNumericValue(storage.get('menuColorG', '39'), 0, 255, 39, 'menuColorG');
        let bValue = sanitizeNumericValue(storage.get('menuColorB', '41'), 0, 255, 41, 'menuColorB');
        // アルファ値は10-100の範囲で、5刻みで管理(%表記)
        let aValue = sanitizeNumericValue(storage.get('menuColorA', '100'), 10, 100, 100, 'menuColorA');
        
        // サイズとオフセット設定(セキュリティ検証付き)
        let widthValue = sanitizeNumericValue(storage.get('menuWidth', '50'), 5, 100, 50, 'menuWidth');
        let heightValue = sanitizeNumericValue(storage.get('menuHeight', '200'), 5, 5000, 200, 'menuHeight');
        let offsetValue = sanitizeNumericValue(storage.get('menuOffset', '0'), -10000, 10000, 0, 'menuOffset');
        
        const rValueEl = document.getElementById('orp-r-value');
        const gValueEl = document.getElementById('orp-g-value');
        const bValueEl = document.getElementById('orp-b-value');
        const aValueEl = document.getElementById('orp-a-value');
        const widthValueEl = document.getElementById('orp-width-value');
        const heightValueEl = document.getElementById('orp-height-value');
        const offsetValueEl = document.getElementById('orp-offset-value');
        const previewEl = document.getElementById('orp-rgb-preview');
        
        const updateRGBA = () => {
            // 値を表示
            rValueEl.textContent = rValue;
            gValueEl.textContent = gValue;
            bValueEl.textContent = bValue;
            aValueEl.textContent = aValue;
            
            // アルファ値を0-1の範囲に変換
            const alpha = aValue / 100;
            
            // プレビュー更新(強制的に再描画)
            const color = `rgba(${rValue}, ${gValue}, ${bValue}, ${alpha})`;
            previewEl.style.background = '';
            setTimeout(() => {
                previewEl.style.background = color;
            }, 0);
            
            // メニューボタンに適用
            if (leftToggleBtn) {
                leftToggleBtn.style.setProperty('background', color, 'important');
            }
            
            // 保存
            storage.set('menuColorR', String(rValue));
            storage.set('menuColorG', String(gValue));
            storage.set('menuColorB', String(bValue));
            storage.set('menuColorA', String(aValue));
            
            console.log('[NOR] Color updated:', color);
        };
        
        const updateSize = () => {
            // 値を表示
            widthValueEl.textContent = widthValue;
            heightValueEl.textContent = heightValue;
            
            // メニューボタンに適用
            if (leftToggleBtn) {
                leftToggleBtn.style.setProperty('max-width', `${widthValue}px`, 'important');
                leftToggleBtn.style.setProperty('width', `${widthValue}px`, 'important');
                leftToggleBtn.style.setProperty('height', `${heightValue}px`, 'important');
            }
            
            // 保存
            storage.set('menuWidth', String(widthValue));
            storage.set('menuHeight', String(heightValue));
            
            console.log('[NOR] Size updated:', widthValue, heightValue);
        };
        
        const updateOffset = () => {
            // 値を表示
            offsetValueEl.textContent = offsetValue;
            
            // メニューボタンに適用
            if (leftToggleBtn) {
                const transform = `translateY(calc(-50% + ${offsetValue}px))`;
                leftToggleBtn.style.setProperty('transform', transform, 'important');
            }
            
            // 保存
            storage.set('menuOffset', String(offsetValue));
            
            console.log('[NOR] Offset updated:', offsetValue);
        };
        
        // 初期表示
        updateRGBA();
        updateSize();
        updateOffset();
        
        // RGB値更新のヘルパー関数
        const createHandler = (getValue, setValue, updateFunc, min = 0, max = 255, step = 5) => {
            return {
                increment: () => {
                    setValue(Math.min(max, getValue() + step));
                    updateFunc();
                },
                decrement: () => {
                    setValue(Math.max(min, getValue() - step));
                    updateFunc();
                }
            };
        };
        
        // R, G, B, Aのハンドラーを設定
        const colorHandlers = {
            r: createHandler(() => rValue, (v) => rValue = v, updateRGBA, 0, 255, 5),
            g: createHandler(() => gValue, (v) => gValue = v, updateRGBA, 0, 255, 5),
            b: createHandler(() => bValue, (v) => bValue = v, updateRGBA, 0, 255, 5),
            a: createHandler(() => aValue, (v) => aValue = v, updateRGBA, 10, 100, 5)
        };
        
        // サイズハンドラー
        const sizeHandlers = {
            width: createHandler(() => widthValue, (v) => widthValue = v, updateSize, 5, 100, 5),
            height: createHandler(() => heightValue, (v) => heightValue = v, updateSize, 5, 5000, 10)
        };
        
        // オフセットハンドラー
        const offsetHandlers = {
            offset: createHandler(() => offsetValue, (v) => offsetValue = v, updateOffset, -10000, 10000, 10)
        };
        
        // イベントリスナーを登録
        ['r', 'g', 'b', 'a'].forEach(color => {
            document.getElementById(`orp-${color}-minus`).addEventListener('click', colorHandlers[color].decrement);
            document.getElementById(`orp-${color}-plus`).addEventListener('click', colorHandlers[color].increment);
        });
        
        // サイズのイベントリスナー
        document.getElementById('orp-width-minus').addEventListener('click', sizeHandlers.width.decrement);
        document.getElementById('orp-width-plus').addEventListener('click', sizeHandlers.width.increment);
        document.getElementById('orp-height-minus').addEventListener('click', sizeHandlers.height.decrement);
        document.getElementById('orp-height-plus').addEventListener('click', sizeHandlers.height.increment);
        
        // オフセットのイベントリスナー
        document.getElementById('orp-offset-minus').addEventListener('click', offsetHandlers.offset.decrement);
        document.getElementById('orp-offset-plus').addEventListener('click', offsetHandlers.offset.increment);
        
        // リセットボタン
        document.getElementById('orp-size-reset').addEventListener('click', () => {
            widthValue = 50;
            heightValue = 200;
            updateSize();
        });
        
        document.getElementById('orp-offset-reset').addEventListener('click', () => {
            offsetValue = 0;
            updateOffset();
        });
    }
    
    function initMenuSettings() {
        // SUBS設定の初期化(セキュリティ検証付き)
        const subsSettings = {
            fontSize: {
                value: sanitizeNumericValue(storage.get('subsFontSize', '14'), 5, 50, 14, 'subsFontSize'),
                default: 14,
                min: 5,
                max: 50,
                step: 1
            },
            margin: {
                value: sanitizeNumericValue(storage.get('subsMargin', '10'), 0, 100, 10, 'subsMargin'),
                default: 10,
                min: 0,
                max: 100,
                step: 1
            },
            topOffset: {
                value: sanitizeNumericValue(storage.get('subsTopOffset', '0'), 0, 1000, 0, 'subsTopOffset'),
                default: 0,
                min: 0,
                max: 1000,
                step: 1
            },
            lrOffset: {
                value: sanitizeNumericValue(storage.get('subsLROffset', '0'), 0, 1000, 0, 'subsLROffset'),
                default: 0,
                min: 0,
                max: 1000,
                step: 1
            }
        };
        
        const updateSubsStyles = () => {
            const styleId = 'orp-subs-custom-style';
            let styleEl = document.getElementById(styleId);
            if (!styleEl) {
                styleEl = document.createElement('style');
                styleEl.id = styleId;
                document.head.appendChild(styleEl);
            }
            
            // CSSインジェクション対策: 値の再検証(多層防御)
            const fontSize = sanitizeNumericValue(subsSettings.fontSize.value, 5, 50, 14);
            const margin = sanitizeNumericValue(subsSettings.margin.value, 0, 100, 10);
            const topOffset = sanitizeNumericValue(subsSettings.topOffset.value, 0, 1000, 0);
            const lrOffset = sanitizeNumericValue(subsSettings.lrOffset.value, 0, 1000, 0);
            
            // 型チェック(数値であることを保証)
            if (typeof fontSize !== 'number' || typeof margin !== 'number' || 
                typeof topOffset !== 'number' || typeof lrOffset !== 'number') {
                console.error('[NOR Security] CSS値の型が不正です。スタイル更新をスキップします。');
                return;
            }
            
            styleEl.textContent = `
                #sr-header-area .sr-bar a {
                    font-size: ${fontSize}px !important;
                    padding-top: ${margin}px !important;
                    padding-bottom: ${margin}px !important;
                    padding-left: ${15 + lrOffset}px !important;
                    padding-right: ${15 + lrOffset}px !important;
                }
                #sr-header-area .sr-bar {
                    padding-top: ${topOffset}px !important;
                }
                /* プレビュー用 */
                .orp-preview-item {
                    font-size: ${fontSize}px !important;
                    padding-top: ${margin}px !important;
                    padding-bottom: ${margin}px !important;
                    padding-left: ${15 + lrOffset}px !important;
                    padding-right: ${15 + lrOffset}px !important;
                }
                .orp-preview-list {
                    padding-top: ${topOffset}px !important;
                }
            `;
        };
        
        // 各設定の更新関数
        const updateSetting = (key) => {
            const setting = subsSettings[key];
            const valueEl = document.getElementById(`orp-subs-${key.replace(/([A-Z])/g, '-$1').toLowerCase()}-value`);
            valueEl.textContent = setting.value;
            
            const storageKey = 'subs' + key.charAt(0).toUpperCase() + key.slice(1);
            storage.set(storageKey, String(setting.value));
            
            updateSubsStyles();
        };
        
        // 初期表示
        Object.keys(subsSettings).forEach(key => {
            const setting = subsSettings[key];
            const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
            const valueEl = document.getElementById(`orp-subs-${kebabKey}-value`);
            valueEl.textContent = setting.value;
        });
        updateSubsStyles();
        
        // イベントリスナー
        Object.keys(subsSettings).forEach(key => {
            const setting = subsSettings[key];
            const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
            
            // +ボタン
            document.getElementById(`orp-subs-${kebabKey}-plus`).addEventListener('click', () => {
                setting.value = Math.min(setting.max, setting.value + setting.step);
                updateSetting(key);
            });
            
            // -ボタン
            document.getElementById(`orp-subs-${kebabKey}-minus`).addEventListener('click', () => {
                setting.value = Math.max(setting.min, setting.value - setting.step);
                updateSetting(key);
            });
            
            // リセットボタン
            document.getElementById(`orp-subs-${kebabKey}-reset`).addEventListener('click', () => {
                setting.value = setting.default;
                updateSetting(key);
            });
        });
    }
    
    function initDarkMode() {
        const isDarkMode = storage.get('darkMode') === 'true';
        if (isDarkMode) {
            document.body.classList.add('dark-mode');
        }
        
        const isDarkModeRight = storage.get('darkModeRight') === 'true';
        if (isDarkModeRight) {
            document.body.classList.add('dark-mode-right');
            
            // RIGHT側フルダークモード(RIGHT側ダークモードがONの場合のみ)
            const isDarkModeRightFull = storage.get('darkModeRightFull') === 'true';
            if (isDarkModeRightFull) {
                document.body.classList.add('dark-mode-right-full');
            }
        }
    }
    
    function initLeftSidebar(button) {
        // ページロード時は常にSUBSを閉じた状態で開始(ページ遷移時の暗転を防ぐ)
        document.body.classList.remove('show-left');
        storage.set('leftSidebarVisible', false);
        button.style.display = '';
        
        button.addEventListener('click', function(e) {
            e.stopPropagation();
            document.body.classList.toggle('show-left');
            const isNowVisible = document.body.classList.contains('show-left');
            storage.set('leftSidebarVisible', isNowVisible);
            
            // SUBS表示時はフロートボタンを非表示
            if (isNowVisible) {
                button.style.display = 'none';
            }
        });
        
        document.addEventListener('click', function(e) {
            const sidebar = document.getElementById('sr-header-area');
            const isOpen = document.body.classList.contains('show-left');
            
            if (isOpen && sidebar && !sidebar.contains(e.target) && e.target !== button) {
                document.body.classList.remove('show-left');
                storage.set('leftSidebarVisible', false);
                // SUBSがひっこんだらフロートボタンを表示
                button.style.display = '';
            }
        });
        
        const sidebar = document.getElementById('sr-header-area');
        if (sidebar) {
            sidebar.addEventListener('click', function(e) {
                e.stopPropagation();
                
                // SUBSメニュー内のリンククリック時に即座にメニューを閉じる
                if (e.target.tagName === 'A') {
                    document.body.classList.remove('show-left');
                    storage.set('leftSidebarVisible', false);
                    // フロートボタンを表示
                    button.style.display = '';
                }
            });
        }
    }
    
    function updateSubsPosition() {
        // ヘッダーの実際の高さを取得してSUBSの位置を動的に調整
        const header = document.getElementById('header');
        const subs = document.getElementById('sr-header-area');
        
        if (header && subs) {
            const headerHeight = header.offsetHeight || 0;
            subs.style.top = `${headerHeight}px`;
            subs.style.height = `calc(100vh - ${headerHeight}px)`;
            console.log('[NOR] SUBS position updated: headerHeight =', headerHeight);
        }
    }
    
    function init() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', init);
            return;
        }
        
        const buttons = createToggleButtons();
        if (buttons) {
            initDarkMode();
            initLeftSidebar(buttons.leftToggleBtn);
            initSettingsPanel();
            
            // SUBS位置の初期設定と動的更新
            updateSubsPosition();
            // ウィンドウリサイズ時に再計算
            window.addEventListener('resize', updateSubsPosition);
            // ヘッダー変更を監視(MutationObserver)
            const header = document.getElementById('header');
            if (header) {
                const observer = new MutationObserver(updateSubsPosition);
                observer.observe(header, { attributes: true, childList: true, subtree: true });
            }
            
            console.log('[NOR] Overlay MVP with Settings Panel initialized');
        }
    }
    
    init();
})();