미디어위키:Template.css: 두 판 사이의 차이

편집 요약 없음
편집 요약 없음
태그: 되돌려진 기여
1번째 줄: 1번째 줄:
@import url('/index.php?title=MediaWiki:Template.Infobox.css&action=raw&ctype=text/css');
mw.loader.load('https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js');


/* =========================================
function loadLangScript(done) {
  Proitem Animation
    $.getScript('/index.php?title=미디어위키:Lang.js&action=raw&ctype=text/javascript')
  ========================================= */
        .done(function() {
            if (typeof done === 'function') done();
        })
        .fail(function(a, b, c) {
            console.error('Lang.js load failed:', b, c);
            if (typeof done === 'function') done();
        });
}


.wip-stripe-layer {
$(function() {
     position: absolute;
     $('body').prepend(
    top: 0;
        '<div class="WW-bg" style="position:fixed;top:0;left:0;width:100%;height:100vh;"></div>'
    left: -280px;
    width: calc(100% + 560px);
    height: 100%;
    opacity: 0.08;
    background: repeating-linear-gradient(
        45deg,
        #969696,
        #969696 10px,
        transparent 10px,
        transparent 20px
     );
     );
     z-index: 0;
});
 
// 페이지 전환 사운드
var transitionSound = new Audio('/index.php?title=특수:Redirect/file/Sfx-ui-001.mp3');
 
(function() {
    var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
    var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
     var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';
    transitionSound.volume = sfxOn ? master * sfx : 0;
})();
 
function playStaticSound() {
    var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
    var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
    var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';
 
    if (!sfxOn) return;
 
    transitionSound.volume = master * sfx;
    transitionSound.currentTime = 0;
    transitionSound.play();
}
}


.wip-active .wip-stripe-layer {
// 현재 언어 감지
     animation: wip-stripe-move 8s linear infinite;
function getCurrentLang() {
    var langData = document.getElementById('clbi-lang-data');
     return langData ? (langData.getAttribute('data-lang') || 'ko') : 'ko';
}
}


.wip-standby .wip-stripe-layer {
function normalizePageName(value) {
    animation: none;
    return String(value || '')
        .split('?')[0]
        .replace(/^\/index\.php\//, '')
        .replace(/_/g, ' ')
        .trim();
}
}


@keyframes wip-stripe-move {
function buildWikiPath(title) {
     from { transform: translateX(0); }
     return '/index.php/' + encodeURI(String(title || '').replace(/ /g, '_'));
    to { transform: translateX(280px); }
}
}


/* =========================================
// 국가_및_조합 전용 왼쪽 사이드바 이미지
  Collapsible
function updateLeftSidebarNationsImage() {
  ========================================= */
    var imageId = 'clbi-left-nations-image';
 
    // SPA 이동 시 이전 문서에서 삽입했던 이미지를 먼저 제거한다.
    // 이 처리가 없으면 국가_및_조합에서 다른 문서로 이동했을 때 이미지가 남을 수 있다.
    $('#' + imageId).remove();
 
    // 현재 문서명이 국가 및 조합일 때만 삽입한다.
    // wgPageName은 보통 국가_및_조합처럼 언더스코어를 포함하므로 normalizePageName으로 통일해서 비교한다.
    var pageName = normalizePageName(mw.config.get('wgPageName'));
    var targetPage = normalizePageName('국가_및_조합');


[id^="collapsible"] {
    if (pageName !== targetPage) {
    overflow: hidden;
        return;
    transition: max-height .25s ease;
     }
     max-height: 0;
}


/* =========================================
    var $leftSidebar = $('#clbi-left-sidebar');
  Color
  ========================================= */


.color-text {
    if (!$leftSidebar.length) {
    color: var(--text-color);
        return;
}
    }


.color-text a,
    // 왼쪽 사이드바 기본 자식 순서:
.color-text a:visited,
    // 0 = 언어 섹션, 1 = 카테고리 섹션.
.color-text a:hover,
    // 이미지는 카테고리 섹션 바로 아래에 삽입한다.
.color-text a:active {
     var $categoryBox = $leftSidebar.children('.clbi-left-box').eq(1);
     color: var(--text-color) !important;
    text-decoration-color: var(--text-color) !important;
}


/* =========================================
    if (!$categoryBox.length) {
  CRT Glitch Panel
        return;
  ========================================= */
    }


.crt-glitch-panel {
    // 컨테이너나 테두리를 추가하지 않고 img 요소만 넣는다.
     position: relative;
     // 실제 파일을 바꾸려면 Redirect/file 뒤 파일명만 교체하면 된다.
     overflow: hidden;
     $categoryBox.after(
    isolation: isolate;
        '<img id="' + imageId + '" ' +
    border-radius: 5px;
        'class="clbi-left-page-image" ' +
    box-shadow:
         'src="/index.php?title=특수:Redirect/file/img-nations-sidebar-001.png" ' +
         inset 0 0 18px rgba(255,255,255,0.04),
         'alt="" loading="lazy">'
         inset 0 0 42px rgba(133,67,105,0.10),
    );
        0 0 18px rgba(133,67,105,0.20);
}
}


.crt-glitch-panel .vanishing-grid-bg {
// 사이드바 업데이트
     position: relative;
function updateSidebar() {
    overflow: hidden;
     if (!window.LANG || !window.LANG_NAMES || !window.CAT_LINKS) {
    border-radius: 5px;
         setTimeout(updateSidebar, 100);
    background:
        return;
        radial-gradient(circle at 50% 45%, rgba(212,90,162,0.18) 0%, transparent 32%),
    }
         linear-gradient(to bottom, #111 0%, #090909 58%, #030303 100%) !important;
}


.crt-glitch-panel > * {
    var langData = document.getElementById('clbi-lang-data');
     position: relative;
    var currentLang = getCurrentLang();
     z-index: 2;
     var t = (window.LANG && window.LANG[currentLang]) ? window.LANG[currentLang] : window.LANG.ko;
}
     var names = (window.LANG_NAMES && window.LANG_NAMES[currentLang]) ? window.LANG_NAMES[currentLang] : window.LANG_NAMES.ko;
    var cl = (window.CAT_LINKS && window.CAT_LINKS[currentLang]) ? window.CAT_LINKS[currentLang] : window.CAT_LINKS.ko;


.crt-glow-content {
    var allLangs = langData ? {
    position: relative;
        ko: langData.getAttribute('data-ko') || '',
    z-index: 2;
        en: langData.getAttribute('data-en') || '',
    text-shadow:
         zh: langData.getAttribute('data-zh') || '',
        -1px 0 0 rgba(80,160,255,0.75),
         ja: langData.getAttribute('data-ja') || ''
         1px 0 0 rgba(255,55,90,0.65),
    } : {
         2px 0 0 rgba(255,210,70,0.45),
         ko: '',
         0 0 2px rgba(255,255,255,0.35),
         en: '',
         0 0 6px rgba(212,90,162,0.22),
         zh: '',
         0 0 12px rgba(133,67,105,0.18);
        ja: ''
    filter: drop-shadow(0 0 5px rgba(212,90,162,0.18));
    };
}


.crt-glow-content::before {
     $('#clbi-lang-current').text(names[currentLang]);
     content: "";
    position: absolute;
    inset: -24px;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(
            circle at 50% 35%,
            rgba(255,255,255,0.08) 0%,
            rgba(212,90,162,0.10) 18%,
            rgba(133,67,105,0.08) 34%,
            transparent 68%
        );
    filter: blur(18px);
    opacity: 0.75;
}


.crt-glitch-panel .fake-h1,
    var langHtml = '';
.crt-glitch-panel h1,
     $.each(['ko', 'en', 'zh', 'ja'], function(i, lang) {
.crt-glitch-panel h2,
         if (lang === currentLang) return;
.crt-glitch-panel strong {
     text-shadow:
        -1px 0 0 rgba(80,160,255,0.95),
        1px 0 0 rgba(255,55,90,0.85),
        2px 0 0 rgba(255,210,70,0.55),
         0 0 3px rgba(255,255,255,0.65),
        0 0 10px rgba(255,255,255,0.28),
        0 0 18px rgba(212,90,162,0.35),
        0 0 32px rgba(133,67,105,0.24);
}


.crt-glitch-panel img {
        var target = allLangs[lang];
    filter:
        if (target) {
        drop-shadow(-1px 0 0 rgba(80,160,255,0.65))
            langHtml += '<div class="clbi-lang-link"><a href="' + buildWikiPath(target) + '?uselang=' + lang + '">' + names[lang] + '</a></div>';
         drop-shadow(1px 0 0 rgba(255,55,90,0.55))
         } else {
         drop-shadow(2px 0 0 rgba(255,210,70,0.35))
            langHtml += '<div class="clbi-lang-wip">' + names[lang] + '</div>';
        drop-shadow(0 0 6px rgba(212,90,162,0.18));
         }
}
    });
    $('#clbi-lang-list').html(langHtml);


.crt-glitch-panel::before {
    $('#clbi-title-language').text(t.language);
    content: "";
     $('#clbi-title-categories').text(t.categories);
     position: absolute;
     $('#clbi-title-links').text(t.links);
     inset: -80px 0;
     $('#clbi-title-search a').text(t.search);
     z-index: 3;
     $('#clbi-search-input').attr('placeholder', t.search + '...');
     pointer-events: none;
     $('#clbi-title-recent a').text(t.recentChanges);
     opacity: 0.22;
     $('#clbi-title-guide').text(t.guide);
     background:
     $('#clbi-guide-link').text(t.getStarted);
        repeating-linear-gradient(
     $('#clbi-title-playlist').text(t.playlist);
            to bottom,
            rgba(255,255,255,0.08) 0px,
            rgba(255,255,255,0.08) 1px,
            transparent 2px,
            transparent 5px
        );
     mix-blend-mode: screen;
     animation: crt-scanlines-up 7s linear infinite;
}


.crt-glitch-panel::after {
     $('#clbi-cat-main a').attr('href', buildWikiPath(cl.main));
     content: "";
     $('#clbi-cat-main .clbi-cat-label').text(t.mainMenu);
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
    opacity: 0;
    background:
        linear-gradient(
            to bottom,
            transparent 0%,
            rgba(255,255,255,0.18) 48%,
            rgba(212,90,162,0.28) 50%,
            rgba(90,120,255,0.16) 52%,
            transparent 56%
        );
     mix-blend-mode: screen;
    animation: crt-glitch-band-up 6.5s steps(1, end) infinite;
}


/* =========================================
    $('#clbi-cat-nations a').attr('href', buildWikiPath(cl.nations));
  Horizontal CRT Warp
    $('#clbi-cat-nations .clbi-cat-label').text(t.nations);
  ========================================= */


.crt-horizontal-warp {
    $('#clbi-cat-corporations a').attr('href', buildWikiPath(cl.corporations));
    position: relative;
     $('#clbi-cat-corporations .clbi-cat-label').text(t.corporations);
     overflow: hidden;
    isolation: isolate;
}


.crt-horizontal-warp .crt-glow-content {
     $('#clbi-cat-military a').attr('href', buildWikiPath(cl.military));
     animation: crt-base-drift 9s steps(1, end) infinite;
    $('#clbi-cat-military .clbi-cat-label').text(t.military);
    background-image:
        repeating-linear-gradient(
            to bottom,
            rgba(120,180,255,0.00) 0px,
            rgba(120,180,255,0.00) 3px,
            rgba(120,180,255,0.05) 4px,
            rgba(255,80,170,0.035) 5px,
            rgba(120,180,255,0.00) 7px
        );
    background-blend-mode: screen;
}


.crt-horizontal-warp .crt-glow-content::after {
     $('#clbi-cat-history a').attr('href', buildWikiPath(cl.history));
     content: "";
     $('#clbi-cat-history .clbi-cat-label').text(t.history);
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    opacity: 0;
    background:
        linear-gradient(
            to right,
            transparent 0%,
            rgba(120,180,255,0.18) 10%,
            rgba(255,255,255,0.18) 48%,
            rgba(212,90,162,0.20) 78%,
            transparent 100%
        );
     mix-blend-mode: screen;
    animation: crt-wide-horizontal-tear 3.8s steps(1, end) infinite;
}


/* =========================================
    $('#clbi-cat-personnel a').attr('href', buildWikiPath(cl.personnel));
  CRT Animations
    $('#clbi-cat-personnel .clbi-cat-label').text(t.personnel);
  ========================================= */


@keyframes crt-scanlines-up {
    $('#clbi-btn-contribution').text(t.contribution);
     from {
     $('#clbi-btn-watchlist').text(t.watchlist);
        transform: translateY(0);
    $('#clbi-btn-preferences').text(t.preferences);
     }
    $('#clbi-btn-logout').text(t.logout);
     $('#clbi-btn-login').text(t.login);


     to {
     var pageName = normalizePageName(mw.config.get('wgPageName'));
        transform: translateY(-80px);
     var specialPage = String(mw.config.get('wgCanonicalSpecialPageName') || '');
     }
}


@keyframes crt-glitch-band-up {
    $('.clbi-cat-btn').removeClass('clbi-cat-active');
     0%, 12%, 27%, 43%, 58%, 76%, 100% {
     $.each(['main', 'nations', 'corporations', 'military', 'history', 'personnel'], function(i, key) {
         opacity: 0;
         if (cl[key] && pageName === normalizePageName(cl[key])) {
        transform: translateY(100%);
            $('#clbi-cat-' + key).addClass('clbi-cat-active');
     }
        }
     });


     13% {
     $('.clbi-user-btn').removeClass('clbi-user-btn-active');
        opacity: 0.75;
        transform: translateY(70%);
    }


     14% {
     if (
         opacity: 0.15;
        specialPage === 'Contributions' ||
         transform: translateY(62%) skewX(-2deg);
        specialPage === '기여' ||
         pageName.indexOf('특수:기여') === 0 ||
        pageName.indexOf('Special:Contributions') === 0
    ) {
         $('#clbi-btn-contribution').addClass('clbi-user-btn-active');
     }
     }


     28% {
     if (specialPage === 'Watchlist') {
         opacity: 0.55;
         $('#clbi-btn-watchlist').addClass('clbi-user-btn-active');
        transform: translateY(35%);
     }
     }


     29% {
     if (
         opacity: 0.18;
        specialPage === '설정' ||
         transform: translateY(28%) skewX(2deg);
         pageName === '특수:설정' ||
         pageName === 'Special:설정'
    ) {
        $('#clbi-btn-preferences').addClass('clbi-user-btn-active');
     }
     }


     44% {
     $('.toggleBtn').each(function() {
         opacity: 0.65;
         var btn = $(this);
         transform: translateY(10%);
        if (!$('#' + btn.data('target')).hasClass('folding-open')) {
     }
            btn.text(t.expand);
         } else {
            btn.text(t.collapse);
        }
     });


     45% {
     // 왼쪽 사이드바의 문서별 전용 삽입물을 갱신한다.
        opacity: 0.12;
    // 현재는 국가_및_조합 문서에서만 카테고리 섹션 아래 이미지를 삽입한다.
        transform: translateY(4%) skewX(-1deg);
    updateLeftSidebarNationsImage();
    }
}


     59% {
function canShowContentTools() {
        opacity: 0.5;
     // 비로그인 사용자는 편집/역사/공유 버튼을 숨김
         transform: translateY(-20%);
    if (!mw.config.get('wgUserName')) {
         return false;
     }
     }


     60% {
     // MediaWiki가 현재 문서를 편집 가능하지 않다고 판단하면 숨김
        opacity: 0.16;
    var isEditable = mw.config.get('wgIsProbablyEditable');
        transform: translateY(-26%) skewX(2deg);
    if (isEditable === false) {
        return false;
     }
     }


     77% {
     var relevantEditable = mw.config.get('wgRelevantPageIsProbablyEditable');
        opacity: 0.55;
    if (relevantEditable === false) {
        transform: translateY(-55%);
        return false;
     }
     }


     78% {
     return true;
        opacity: 0;
        transform: translateY(-70%);
    }
}
}


@keyframes crt-base-drift {
function moveCatlinksToBottom() {
     0%, 88%, 100% {
    var main = $('.liberty-content-main');
         transform: translateX(0);
    var parserOutput = $('.liberty-content-main .mw-parser-output').first();
         filter: drop-shadow(0 0 5px rgba(212,90,162,0.18));
    var catlinks = $('.catlinks');
    }
 
    if (!main.length || !catlinks.length) return;
 
     catlinks.each(function () {
         var cat = $(this);
 
         if (parserOutput.length) {
            cat.appendTo(parserOutput);
        } else {
            cat.appendTo(main);
        }
    });
}


    89% {
// 대문 스타일
        transform: translateX(-2px) skewX(-0.4deg);
function applyMainPageStyle() {
        filter:
    var specialPage = mw.config.get('wgCanonicalSpecialPageName');
            contrast(1.08)
    if (specialPage === 'Preferences') return;
            brightness(1.05)
            drop-shadow(0 0 7px rgba(212,90,162,0.24));
    }


     90% {
     var pageName = normalizePageName(mw.config.get('wgPageName'));
        transform: translateX(3px) skewX(0.5deg);
     var hideTools = (pageName === '대문' || !canShowContentTools());
     }


     91% {
     // 모든 문서에서 분류 바를 본문 컨테이너 아래로 이동
        transform: translateX(-1px);
    moveCatlinksToBottom();
    }


     92% {
     if (pageName === '대문') {
         transform: translateX(0);
         $('.liberty-content-header').css('display', 'none');
         filter: drop-shadow(0 0 5px rgba(212,90,162,0.18));
         $('.mw-page-title-main').addClass('clbi-hide');
    }
        $('.catlinks').css('display', 'none');
}
        $('.liberty-content-main').css('border-radius', '5px 5px 0 0');


@keyframes crt-wide-horizontal-tear {
        if ($('#clbi-main-logo').length === 0) {
    0%, 8%, 17%, 29%, 43%, 61%, 78%, 100% {
            $('.liberty-content').prepend(
        opacity: 0;
                '<div id="clbi-main-logo" style="text-align:center;padding:10px 0;">' +
        transform: translateX(0);
                '<img src="/index.php?title=특수:Redirect/file/Img-clbi-001.png" style="width:900px;height:auto;">' +
        clip-path: inset(0 0 0 0);
                '</div>'
    }
            );
        }


    9% {
         if ($('#clbi-main-crt-hero').length && $('#clbi-main-crt-hero-wrap').length === 0) {
         opacity: 0.45;
            var heroWrap = $('<div id="clbi-main-crt-hero-wrap"></div>');
        transform: translateX(-18px) skewX(-3deg);
        clip-path: inset(18% 0 70% 0);
    }


    10% {
            if ($('#clbi-main-logo').length) {
        opacity: 0.28;
                heroWrap.insertAfter('#clbi-main-logo');
        transform: translateX(24px) skewX(4deg);
            } else {
        clip-path: inset(21% 0 64% 0);
                heroWrap.insertBefore('.liberty-content-main');
    }
            }


    18% {
            $('#clbi-main-crt-hero').appendTo('#clbi-main-crt-hero-wrap');
         opacity: 0.38;
        }
         transform: translateX(32px) skewX(5deg);
} else if ($('.screen-header').length > 0) {
         clip-path: inset(42% 0 47% 0);
         $('.liberty-content-header').css('display', 'none');
    }
        $('.mw-page-title-main').addClass('clbi-hide');
         $('.catlinks').css('display', '');
         $('.liberty-content-main').css('border-radius', '5px');
        $('#clbi-main-logo').remove();
        $('#clbi-main-crt-hero-wrap').remove();


    19% {
        if ($('#clbi-tools-box').length === 0 && canShowContentTools()) {
        opacity: 0.16;
            var $toolsBox = $('<div id="clbi-tools-box" class="clbi-left-box"></div>');
        transform: translateX(-28px) skewX(-4deg);
            var $toolsTitle = $('<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> 관리</div>');
        clip-path: inset(46% 0 43% 0);
            var $toolsContent = $('<div class="clbi-left-content"></div>');
    }
            $toolsContent.append($('.content-tools .btn-group').clone(true));
            $toolsBox.append($toolsTitle).append($toolsContent);
            $('#clbi-left-sidebar').append($toolsBox);
        }


     30% {
        $('.content-tools').css('display', 'none');
         opacity: 0.48;
        $('.liberty-content').addClass('content-tools-hidden');
         transform: translateX(-36px) skewX(-6deg);
     } else {
         clip-path: inset(58% 0 30% 0);
         $('.liberty-content-header').css('display', '');
        $('.mw-page-title-main').removeClass('clbi-hide');
        $('.catlinks').css('display', '');
        $('.liberty-content-main').css('border-radius', '');
         $('#clbi-main-logo').remove();
        $('#clbi-main-crt-hero-wrap').remove();
         $('#clbi-tools-box').remove();
     }
     }
 
        $('.liberty-content-header').css('display', '');
    31% {
        $('.mw-page-title-main').removeClass('clbi-hide');
         opacity: 0.18;
        $('.catlinks').css('display', '');
         transform: translateX(20px) skewX(3deg);
         $('.liberty-content-main').css('border-radius', '');
         clip-path: inset(61% 0 27% 0);
         $('#clbi-main-logo').remove();
         $('#clbi-main-crt-hero-wrap').remove();
     }
     }


     44% {
     if (hideTools) {
         opacity: 0.36;
         $('.content-tools').css('display', 'none');
         transform: translateX(18px) skewX(3deg);
         $('.liberty-content').addClass('content-tools-hidden');
         clip-path: inset(8% 0 84% 0);
    } else {
         $('.content-tools').css('display', '');
        $('.liberty-content').removeClass('content-tools-hidden');
     }
     }


     45% {
     updateSidebar();
        opacity: 0.12;
}
        transform: translateX(-14px) skewX(-2deg);
        clip-path: inset(11% 0 80% 0);
    }


    62% {
// 본문 기본 목차 제거
        opacity: 0.44;
function removeNativeTocFromContent() {
        transform: translateX(-42px) skewX(-7deg);
    $('.liberty-content-main #toc, .liberty-content-main .toc').remove();
        clip-path: inset(73% 0 16% 0);
}
    }


    63% {
// 왼쪽 목차: MediaWiki 문단 ID 가져오기
        opacity: 0.18;
function getHeadingId(heading) {
        transform: translateX(30px) skewX(4deg);
    if (heading.id) {
         clip-path: inset(76% 0 12% 0);
         return heading.id;
     }
     }


     79% {
     var headline = heading.querySelector('.mw-headline[id]');
        opacity: 0.42;
    if (headline && headline.id) {
        transform: translateX(26px) skewX(4deg);
         return headline.id;
         clip-path: inset(31% 0 58% 0);
     }
     }


     80% {
     return '';
        opacity: 0.14;
        transform: translateX(-20px) skewX(-3deg);
        clip-path: inset(34% 0 54% 0);
    }
}
}


/* =========================================
// 왼쪽 목차: MediaWiki 문단 제목 텍스트 가져오기
  Retro Horizon Grid Background
function getHeadingText(heading) {
  ========================================= */
    var headline = heading.querySelector('.mw-headline');
    var source = headline || heading;
    var clone = source.cloneNode(true);
 
    $(clone).find('.mw-editsection, .mw-editsection-bracket, .mw-editsection-divider').remove();


.retro-grid-bg {
     return (clone.textContent || '')
    position: relative;
        .replace(/\s+/g, ' ')
     overflow: hidden;
        .trim();
    border-radius: 5px;
    background:
        linear-gradient(
            to bottom,
            rgba(13, 6, 19, 0.98) 0%,
            rgba(25, 9, 33, 0.82) 44%,
            rgba(53, 19, 56, 0.50) 60%,
            rgba(4, 4, 7, 0.96) 61%,
            rgba(2, 2, 4, 1) 100%
        ) !important;
}
}


.retro-grid-bg .crt-glow-content {
// 왼쪽 목차: 긴 제목에 자동 스크롤 적용
    position: relative;
function initTocTitleScroll(root) {
    z-index: 5;
    var $items = root
        ? $(root).find('.toc-scroll-text')
        : $('#side-toc-box .toc-scroll-text');
 
    $items.each(function () {
        var $text = $(this);
        var $wrap = $text.closest('.toc-scroll-wrap');
 
        if (!$wrap.length) return;
 
        var wrapW = Math.floor($wrap.width());
        var textW = Math.ceil(this.scrollWidth);
 
        // 왼쪽 목차: 레이아웃 계산이 끝나지 않았으면 이번 실행에서는 건드리지 않는다.
        if (!wrapW || !textW) return;
 
        if (textW <= wrapW + 12) {
            // 왼쪽 목차: 칸을 넘지 않는 제목은 전체 텍스트를 그대로 보여준다.
            $wrap.removeClass('is-scrolling');
 
            if ($text.data('toc-scroll-enabled')) {
                $text.css({
                    animation: '',
                    'animation-delay': '',
                    '--scroll-dist': ''
                });
                $text.removeData('toc-scroll-enabled');
                $text.removeData('toc-scroll-key');
            }
 
            return;
        }
 
        var scrollDist = '-' + (textW - wrapW + 10) + 'px';
        var duration = Math.max(7, textW / 38) * 1.25;
        var scrollKey = scrollDist + '|' + duration;
 
        // 왼쪽 목차: 긴 제목에는 오른쪽 페이드와 스크롤을 적용한다.
        $wrap.addClass('is-scrolling');
 
        // 왼쪽 목차: 같은 값으로 이미 적용된 애니메이션은 다시 초기화하지 않는다.
        if ($text.data('toc-scroll-key') === scrollKey) {
            return;
        }
 
        $text.data('toc-scroll-enabled', true);
        $text.data('toc-scroll-key', scrollKey);
 
        $text.css({
            // 왼쪽 목차: 페이지 진입 직후에는 잠시 읽을 시간을 준 뒤 흐르게 한다.
            animation: 'toc-scroll-blink-reset ' + duration + 's linear infinite',
            'animation-delay': '1s',
            '--scroll-dist': scrollDist
        });
    });
}
}


.retro-grid-bg::before {
// 목차를 왼쪽 사이드바에 새로 생성
     content: "";
function moveTocToLeftSidebar() {
     position: absolute;
     // 왼쪽 목차: MediaWiki가 만든 원래 목차는 본문에서 제거한다.
     left: -10%;
     removeNativeTocFromContent();
     right: -10%;
 
     top: 56%;
     var leftSidebar = document.getElementById('clbi-left-sidebar');
    height: 16%;
     if (!leftSidebar) return;
    z-index: 1;
 
     pointer-events: none;
     var content =
     background:
        document.querySelector('.liberty-content-main .mw-parser-output') ||
         radial-gradient(
        document.querySelector('.liberty-content-main');
            ellipse at 50% 50%,
 
            rgba(230,210,225,0.28) 0%,
     if (!content) return;
            rgba(204,94,168,0.30) 20%,
 
            rgba(184,56,142,0.20) 44%,
     var headings = Array.prototype.slice.call(
            transparent 76%
         content.querySelectorAll('h2, h3')
         );
    ).filter(function (heading) {
    filter: blur(9px);
        if (heading.closest('#toc, .toc, #side-toc-box')) return false;
    opacity: 0.68;
 
}
        var id = getHeadingId(heading);
         var text = getHeadingText(heading);
 
        if (!id || !text) return false;
 
        return true;
    });


.retro-grid-bg::after {
     var tocKey = headings.map(function (heading) {
    content: "";
         return getHeadingId(heading) + '|' + getHeadingText(heading);
     position: absolute;
     }).join('||');
    left: 50%;
    top: 61%;
    width: 150%;
    height: 115%;
    z-index: 2;
    pointer-events: none;
    background-image:
        linear-gradient(
            to right,
            rgba(204,64,156,0.72) 1px,
            transparent 1px
        ),
        linear-gradient(
            to bottom,
            rgba(204,64,156,0.70) 1px,
            transparent 1px
        );
    background-size:
        48px 100%,
        100% 26px;
    transform-origin: 50% 0%;
    transform:
         translateX(-50%)
        perspective(430px)
        rotateX(66deg);
     opacity: 0.78;
    filter:
        drop-shadow(0 0 3px rgba(204,64,156,0.58))
        drop-shadow(0 0 10px rgba(204,64,156,0.30));
    animation: retro-grid-flow 1.8s linear infinite;
    mask-image:
        linear-gradient(
            to bottom,
            transparent 0%,
            black 4%,
            black 82%,
            transparent 100%
        );
    -webkit-mask-image:
        linear-gradient(
            to bottom,
            transparent 0%,
            black 4%,
            black 82%,
            transparent 100%
        );
}


.retro-grid-bg .crt-glow-content::before {
    var existingBox = document.getElementById('side-toc-box');
    content: "";
    position: absolute;
    inset: -42px;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(
            ellipse at 50% 56%,
            rgba(220,220,225,0.05) 0%,
            rgba(204,64,156,0.07) 22%,
            rgba(96,152,204,0.05) 42%,
            transparent 70%
        );
    filter: blur(20px);
    opacity: 0.52;
}


@keyframes retro-grid-flow {
    // 왼쪽 목차: 같은 문서에서 같은 목차를 이미 만들었다면 다시 지우고 만들지 않는다.
    from {
    if (existingBox && existingBox.getAttribute('data-toc-key') === tocKey) {
         background-position:
        initTocTitleScroll(existingBox);
            0 0,
         return;
            0 0;
     }
     }


     to {
     if (existingBox) {
         background-position:
         existingBox.remove();
            0 0,
            0 -26px;
     }
     }
}


/* =========================================
    if (!headings.length) return;
  CRT Monitor Frame
 
  ========================================= */
    var tocBox = document.createElement('div');
    tocBox.className = 'clbi-left-box';
    tocBox.id = 'side-toc-box';
    tocBox.setAttribute('data-toc-key', tocKey);
 
    var title = document.createElement('div');
    title.className = 'clbi-left-title';
 
    // 왼쪽 목차: 박스 제목은 Lang.js의 현재 UI 언어를 따른다.
    var currentLang = getCurrentLang();
    var t = (window.LANG && window.LANG[currentLang]) ? window.LANG[currentLang] : window.LANG.ko;
    var tocTitleText = (t && t.toc) ? t.toc : '목차';
 
    title.innerHTML =
        '<span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> ' + tocTitleText;
 
    var body = document.createElement('div');
    body.className = 'clbi-left-content toc-sidebar-content';
 
    var list = document.createElement('ul');
    list.className = 'generated-toc';
 
    headings.forEach(function (heading) {
        var id = getHeadingId(heading);
        var text = getHeadingText(heading);
        var level = heading.tagName.toLowerCase() === 'h3' ? 3 : 2;
 
        var item = document.createElement('li');
        item.className = 'toc-level-' + level;
 
        var link = document.createElement('a');
        link.setAttribute('href', '#' + id);
 
        // 왼쪽 목차: 긴 제목 스크롤을 위해 텍스트를 별도 span으로 감싼다.
        var textWrap = document.createElement('span');
        textWrap.className = 'toc-scroll-wrap';


.crt-monitor-frame {
        var textSpan = document.createElement('span');
    position: relative;
         textSpan.className = 'toc-scroll-text';
    box-sizing: border-box;
         textSpan.textContent = text;
    padding: 34px 46px 38px 46px;
    border-radius: 22px;
    background:
        linear-gradient(145deg, #303030 0%, #181818 22%, #080808 58%, #202020 100%);
    border: 2px solid #2f2f2f;
    box-shadow:
         inset 0 2px 0 rgba(255,255,255,0.18),
        inset 0 -3px 0 rgba(0,0,0,0.85),
        inset 3px 0 0 rgba(255,255,255,0.06),
        inset -3px 0 0 rgba(0,0,0,0.75),
        0 0 0 3px #050505,
        0 0 0 5px #242424,
        0 12px 20px rgba(0,0,0,0.62),
        0 24px 42px rgba(0,0,0,0.52),
         0 42px 70px rgba(0,0,0,0.34);
}


.crt-monitor-frame::before {
         textWrap.appendChild(textSpan);
    content: "";
         link.appendChild(textWrap);
    position: absolute;
    inset: 0;
    border-radius: 22px;
    pointer-events: none;
    opacity: 0.16;
    background:
         repeating-linear-gradient(
            135deg,
            rgba(255,255,255,0.035) 0px,
            rgba(255,255,255,0.035) 1px,
            transparent 1px,
            transparent 5px
         ),
        radial-gradient(
            ellipse at 28% 12%,
            rgba(255,255,255,0.12) 0%,
            transparent 45%
        );
    mix-blend-mode: screen;
}


.crt-monitor-frame::after {
        item.appendChild(link);
    content: none;
        list.appendChild(item);
}
    });


.crt-monitor-inner {
    body.appendChild(list);
    position: relative;
     tocBox.appendChild(title);
     box-sizing: border-box;
     tocBox.appendChild(body);
    padding: 18px;
    leftSidebar.appendChild(tocBox);
    border-radius: 16px;
    background:
        linear-gradient(145deg, #050505 0%, #111 44%, #070707 100%);
     border: 1px solid #050505;
    box-shadow:
        inset 0 5px 14px rgba(0,0,0,0.95),
        inset 0 -2px 4px rgba(255,255,255,0.06),
        inset 4px 0 10px rgba(0,0,0,0.85),
        inset -4px 0 10px rgba(0,0,0,0.85),
        0 1px 0 rgba(255,255,255,0.12);
}


.crt-monitor-screen {
     // 왼쪽 목차: DOM 배치가 끝난 뒤 긴 제목 스크롤 여부를 계산한다.
     position: relative;
     requestAnimationFrame(function () {
    overflow: hidden;
         initTocTitleScroll(tocBox);
    border-radius: 9px / 18px;
    background: #050505;
    border: 2px solid #050505;
     box-shadow:
        inset 0 0 0 2px #151515,
        inset 0 0 22px rgba(0,0,0,0.95),
        inset 0 0 48px rgba(0,0,0,0.75),
        0 0 0 1px rgba(255,255,255,0.08),
         0 0 18px rgba(133,67,105,0.18);
}


.crt-monitor-screen::before {
         setTimeout(function () {
    content: "";
             initTocTitleScroll(tocBox);
    position: absolute;
         }, 120);
    inset: 0;
     });
    z-index: 20;
    pointer-events: none;
    border-radius: 9px / 18px;
    background:
         radial-gradient(
            ellipse at 50% 50%,
            transparent 0%,
            transparent 58%,
            rgba(0,0,0,0.22) 82%,
             rgba(0,0,0,0.58) 100%
        ),
         linear-gradient(
            105deg,
            transparent 0%,
            rgba(255,255,255,0.05) 18%,
            transparent 38%,
            transparent 100%
        );
     box-shadow:
        inset 0 0 28px rgba(255,255,255,0.04),
        inset 0 0 60px rgba(0,0,0,0.65);
}
}


.crt-monitor-screen .crt-glitch-panel,
// 초기화 함수
.crt-monitor-screen .retro-grid-bg {
function initSidebars() {
     border-radius: 8px / 16px;
    var header = $('.liberty-content-header');
}
     var content = $('.liberty-content');


.crt-monitor-screen .crt-glitch-panel {
    if (header.length && content.length) {
    box-shadow: none;
        header.prependTo(content);
}
    }


#clbi-main-crt-hero-wrap {
    if ($('#clbi-right-sidebar').length === 0) {
    width: 100%;
        var username = mw.config.get('wgUserName');
    margin: 0 auto 14px auto;
        var isLoggedIn = username !== null;
}
        var avatarSrc = isLoggedIn
            ? '/index.php?title=특수:Redirect/file/Pfp-' + username + '.png'
            : '/index.php?title=특수:Redirect/file/Pfp-default.png';


#clbi-main-crt-hero {
        var userBox;
    width: 100%;
        if (isLoggedIn) {
}
            userBox =
                '<div class="clbi-right-box">' +
                    '<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
                        '<img src="' + avatarSrc + '" onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png\'" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
                        '<div style="position:relative;width:100%;margin-top:2px;height:18px;line-height:18px;text-align:center;">' +
                            '<button type="button" id="clbi-playlist-toggle" aria-label="플레이리스트" style="position:absolute;top:0;left:10px;background:none;border:none;padding:0;width:18px;height:18px;color:#E2E2E2;cursor:pointer;display:flex;align-items:center;justify-content:center;">' +
                                '<span class="clbi-icon" style="--icon:var(--ic-ui-009);width:16px;height:16px;"></span>' +
                            '</button>' +
                            '<a href="/index.php/사용자:' + username + '" style="font-size:13px;font-weight:700;color:#E2E2E2 !important;text-decoration:none !important;line-height:18px;display:inline-block;vertical-align:middle;max-width:calc(100% - 58px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + username + '</a>' +
                            '<button type="button" id="clbi-notification-toggle" aria-label="알림" style="position:absolute;top:0;right:10px;background:none;border:none;padding:0;width:18px;height:18px;color:#E2E2E2;cursor:pointer;display:flex;align-items:center;justify-content:center;">' +
                                '<span class="clbi-icon" style="--icon:var(--ic-ui-009);width:16px;height:16px;"></span>' +
                                '<span id="clbi-notification-badge" style="display:none;position:absolute;top:-6px;right:-8px;min-width:14px;height:14px;padding:0 3px;border-radius:999px;background:#854369;color:#fff;font-size:9px;line-height:14px;font-weight:700;text-align:center;"></span>' +
                            '</button>' +
                        '</div>' +
                    '</div>' +
                    '<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
                        '<a href="/index.php/특수:기여/' + username + '" class="clbi-user-btn" id="clbi-btn-contribution">기여</a>' +
                        '<a href="/index.php/특수:주시문서목록" class="clbi-user-btn" id="clbi-btn-watchlist">주시문서 목록</a>' +
                        '<a href="/index.php/특수:설정" class="clbi-user-btn" id="clbi-btn-preferences">설정</a>' +
                        '<a href="/index.php?title=특수:로그아웃&returnto=대문" class="clbi-user-btn clbi-user-btn-logout" id="clbi-btn-logout">로그아웃</a>' +
                    '</div>' +
                '</div>';
        } else {
            userBox =
                '<div class="clbi-right-box">' +
                    '<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
                        '<img src="/index.php?title=특수:Redirect/file/Pfp-default.png" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
                        '<span style="font-size:13px;font-weight:700;color:#E2E2E2;">Guest</span>' +
                    '</div>' +
                    '<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
                        '<a href="/index.php?title=특수:로그인&returnto=대문" class="clbi-user-btn" id="clbi-btn-login">로그인</a>' +
                    '</div>' +
                '</div>';
        }


/* =========================================
        var recentBox =
  Compact CRT Media Frame
            '<div class="clbi-right-box">' +
  ========================================= */
                '<div class="clbi-right-title" id="clbi-title-recent"><span class="clbi-icon" style="--icon:var(--ic-ui-006)"></span> <a href="/index.php/특수:최근바뀜" style="color:#E2E2E2 !important;text-decoration:none !important;">최근 변경</a></div>' +
                '<div class="clbi-right-content" id="clbi-recent-list">불러오는 중...</div>' +
            '</div>';


.compact-crt-media {
        var linkBox =
    position: relative;
            '<div class="clbi-right-box">' +
    box-sizing: border-box;
                '<div class="clbi-right-title" id="clbi-title-links"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> 링크</div>' +
    width: fit-content;
                '<div class="clbi-right-content clbi-link-box">' +
    max-width: 100%;
                    '<ul>' +
    margin: 6px auto;
                        '<li><a href="https://discord.gg/ctaeJ9d3Q5" target="_blank">Discord</a></li>' +
    padding: 10px 14px;
                        '<li><a href="https://www.youtube.com/@nxdsxn" target="_blank">YouTube</a></li>' +
    border-radius: 10px;
                        '<li><a href="https://x.com/nxd_sxn" target="_blank">X</a></li>' +
    background:
                    '</ul>' +
        linear-gradient(
                '</div>' +
            145deg,
            '</div>';
            #242424 0%,
            #141414 34%,
            #070707 70%,
            #171717 100%
        );
    border: 1px solid #2b2b2b;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.12),
        inset 0 -2px 0 rgba(0,0,0,0.82),
        inset 2px 0 0 rgba(255,255,255,0.035),
        inset -2px 0 0 rgba(0,0,0,0.62),
        0 0 0 2px #050505,
        0 0 0 3px #242424,
        0 4px 12px rgba(0,0,0,0.68);
    overflow: hidden;
}


.compact-crt-media .mw-default-size,
        var sidebar =
.compact-crt-media .mw-file-element,
            userBox +
.compact-crt-media img {
            '<div class="clbi-right-box">' +
    position: relative;
                '<div class="clbi-right-title" id="clbi-title-search"><span class="clbi-icon" style="--icon:var(--ic-ui-005)"></span> <a href="/index.php/특수:검색" style="color:#E2E2E2 !important;text-decoration:none !important;">검색</a></div>' +
    z-index: 1;
                '<div class="clbi-right-content">' +
}
                    '<input id="clbi-search-input" type="text" placeholder="검색...">' +
                    '<button id="clbi-search-btn">GO</button>' +
                '</div>' +
            '</div>' +
            recentBox +
            '<div class="clbi-right-box">' +
                '<div class="clbi-right-title" id="clbi-title-guide"><span class="clbi-icon" style="--icon:var(--ic-ui-007)"></span> 가이드</div>' +
                '<div class="clbi-right-content">' +
                    '<a href="/index.php/CLBI_Wiki/KR_시작하기_(CLBI)" id="clbi-guide-link">시작하기</a>' +
                '</div>' +
            '</div>' +
            linkBox;


.compact-crt-media img {
         $('.content-wrapper').append('<div id="clbi-right-sidebar">' + sidebar + '</div>');
    max-width: 100%;
    height: auto;
    filter:
         drop-shadow(0 1px 3px rgba(0,0,0,0.72))
        drop-shadow(0 0 4px rgba(255,255,255,0.06));
    animation: compact-crt-image-jitter 1.25s steps(1, end) infinite;
}


.compact-crt-media::before {
        $('#clbi-search-btn').click(function() {
    content: "";
             var query = $('#clbi-search-input').val();
    position: absolute;
             if (query) {
    inset: 6px;
                window.location.href = '/index.php?search=' + encodeURIComponent(query);
    z-index: 3;
            }
    border-radius: 7px;
         });
    pointer-events: none;
    background:
        radial-gradient(
            ellipse at 50% 50%,
            transparent 0%,
            transparent 54%,
            rgba(0,0,0,0.20) 78%,
             rgba(0,0,0,0.58) 100%
        ),
        linear-gradient(
            105deg,
            transparent 0%,
             rgba(255,255,255,0.040) 18%,
            transparent 38%,
            transparent 100%
        ),
        linear-gradient(
            to bottom,
            rgba(255,255,255,0.030) 0%,
            transparent 34%,
            rgba(0,0,0,0.26) 100%
        );
    box-shadow:
         inset 0 0 18px rgba(255,255,255,0.028),
        inset 0 0 38px rgba(0,0,0,0.66);
}


.compact-crt-media::after {
        $('#clbi-search-input').keypress(function(e) {
    content: "";
             if (e.which === 13) $('#clbi-search-btn').click();
    position: absolute;
         });
    inset: 6px;
    z-index: 4;
    border-radius: 7px;
    pointer-events: none;
    opacity: 0.34;
    background:
        repeating-linear-gradient(
             to bottom,
            rgba(255,255,255,0.070) 0px,
            rgba(255,255,255,0.070) 1px,
            transparent 2px,
            transparent 4px
         );
    mix-blend-mode: screen;
}


.compact-crt-static {
        $.getJSON(
    position: absolute;
             '/api.php?action=query&list=recentchanges&rclimit=5&rcprop=title|timestamp&format=json&rcnamespace=0&rctype=edit|new',
    inset: 6px;
             function(data) {
    z-index: 5;
                var items = data.query.recentchanges;
    border-radius: 7px;
                var html = '';
    pointer-events: none;
    opacity: 0.30;
    background:
        repeating-linear-gradient(
             to bottom,
            transparent 0px,
            transparent 4px,
            rgba(255,255,255,0.10) 5px,
            transparent 6px,
            transparent 10px
        ),
        linear-gradient(
            to bottom,
            transparent 0%,
             transparent 43%,
            rgba(255,255,255,0.11) 49%,
            rgba(180,180,180,0.08) 50%,
            rgba(90,90,90,0.08) 51%,
            transparent 56%,
            transparent 100%
        );
    mix-blend-mode: screen;
    animation:
        compact-crt-static-roll 3.8s linear infinite,
        compact-crt-static-flicker 1.7s steps(1, end) infinite;
}


.compact-crt-media:hover::after {
                $.each(items, function(i, item) {
    opacity: 0.40;
                    var label = timeAgo(item.timestamp);
}
                    html +=
                        '<div class="clbi-recent-item">' +
                            '<div class="clbi-recent-title-wrap">' +
                                '<a href="/index.php/' + encodeURIComponent(item.title) + '" class="clbi-recent-title">' + item.title + '</a>' +
                            '</div>' +
                            '<span class="clbi-recent-time">' + label + '</span>' +
                        '</div>';
                });


.compact-crt-media:hover .compact-crt-static {
                $('#clbi-recent-list').html(html);
    opacity: 0.36;
}


.compact-crt-media:hover img {
                $('#clbi-recent-list .clbi-recent-item').each(function() {
    filter:
                    var wrap = $(this).find('.clbi-recent-title-wrap');
        drop-shadow(0 1px 3px rgba(0,0,0,0.78))
                    var title = $(this).find('.clbi-recent-title');
        drop-shadow(0 0 5px rgba(255,255,255,0.08));
                    var wrapW = wrap.width();
}
                    var titleW = title[0].scrollWidth;


@keyframes compact-crt-static-roll {
                    if (titleW > wrapW + 20) {
    from {
                        var duration = titleW / 40;
        background-position:
                        title.css({
             0 0,
                            animation: 'clbi-scroll ' + duration + 's linear infinite',
             0 110%;
                            '--scroll-dist': '-' + (titleW - wrapW + 8) + 'px'
                        });
                    }
                });
             }
        ).fail(function() {
             $('#clbi-recent-list').html('불러오기 실패');
        });
     }
     }


     to {
     if ($('#clbi-left-sidebar').length === 0) {
         background-position:
         var leftSidebar =
             0 -32px,
            '<div id="clbi-left-sidebar">' +
             0 -30%;
                '<div class="clbi-left-box">' +
                    '<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-001)"></span> <span id="clbi-title-language">언어</span></div>' +
                    '<div class="clbi-left-content clbi-lang-box" id="clbi-lang-box">' +
                        '<div class="clbi-lang-current" id="clbi-lang-current">한국어</div>' +
                        '<div id="clbi-lang-list" style="display:none;"></div>' +
                    '</div>' +
                '</div>' +
                '<div class="clbi-left-box">' +
                    '<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> <span id="clbi-title-categories">카테고리</span></div>' +
                    '<div class="clbi-left-content clbi-cat-box">' +
                        '<div class="clbi-cat-btn" id="clbi-cat-main"><a href="/index.php/대문"><div class="clbi-cat-text"><span class="clbi-cat-label">메인 메뉴</span><span class="clbi-cat-sub">MAIN MENU</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-nations"><a href="/index.php/국가_및_조합"><div class="clbi-cat-text"><span class="clbi-cat-label">국가 및 조합</span><span class="clbi-cat-sub">NATIONS & FACTIONS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-corporations"><a href="/index.php/기업_및_공동체"><div class="clbi-cat-text"><span class="clbi-cat-label">기업 및 공동체</span><span class="clbi-cat-sub">CORPORATIONS & COMMUNITIES</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-military"><a href="/index.php/군_정치집단"><div class="clbi-cat-text"><span class="clbi-cat-label">군, 정치집단</span><span class="clbi-cat-sub">MILITARY & POLITICS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-history"><a href="/index.php/역사적_사건"><div class="clbi-cat-text"><span class="clbi-cat-label">역사적 사건</span><span class="clbi-cat-sub">HISTORICAL EVENTS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-personnel"><a href="/index.php/인물"><div class="clbi-cat-text"><span class="clbi-cat-label">인물</span><span class="clbi-cat-sub">KEY PERSONNEL</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
                    '</div>' +
                '</div>' +
             '</div>';
 
        $('.content-wrapper').prepend(leftSidebar);
 
        // 왼쪽 사이드바가 처음 생성된 직후에도 한 번 실행한다.
        // 이후 언어 갱신과 SPA 이동 시에는 updateSidebar()에서 다시 실행된다.
        updateLeftSidebarNationsImage();
 
        $(document).on('click', '#clbi-lang-current', function() {
            if ($('#clbi-lang-list').is(':hidden')) {
                $('#clbi-lang-current').css('margin-bottom', '8px');
                $('#clbi-lang-list').slideDown(200);
             } else {
                $('#clbi-lang-list').slideUp(200, function() {
                    $('#clbi-lang-current').css('margin-bottom', '0');
                });
            }
        });
     }
     }
    applyMainPageStyle();
    $('#side-toc-box').remove();
    mw.loader.using(['mediawiki.api']).then(function() {
        setTimeout(function() {
            initNotifications();
            initPlaylistPopup();
            initProfile();
            moveTocToLeftSidebar();
        }, 300);
        setTimeout(moveTocToLeftSidebar, 800);
        setTimeout(moveTocToLeftSidebar, 1500);
    });
}
$(function() {
    loadLangScript(function() {
        setTimeout(function() {
            initSidebars();
        }, 100);
    });
});
// SPA 네비게이션
function shouldSkip(url) {
    return url.match(/action=edit|action=submit|action=history|action=delete|action=protect|action=purge|특수:로그인|특수:로그아웃|Special:UserLogin|Special:UserLogout|특수:사용자정보|특수:비밀번호바꾸기|uselang=/);
}
}


@keyframes compact-crt-static-flicker {
$(function() {
     0%, 100% {
     if (window._spaInitialized) return;
         transform: translateX(0);
    window._spaInitialized = true;
         opacity: 0.26;
 
    function isInternal(url) {
         var a = document.createElement('a');
         a.href = url;
        return a.hostname === window.location.hostname;
     }
     }


     11% {
     function loadPage(url) {
         transform: translateX(-1px);
         fetch(url)
        opacity: 0.36;
            .then(function(res) {
    }
                return res.text();
            })
            .then(function(html) {
                var parser = new DOMParser();
                var doc = parser.parseFromString(html, 'text/html');
 
                var scripts = doc.querySelectorAll('script');
                for (var i = 0; i < scripts.length; i++) {
                    var src = scripts[i].textContent;
 
                    if (src.indexOf('wgNamespaceNumber') !== -1) {
                        var match = src.match(/"wgNamespaceNumber":(-?\d+)/);
                        if (match) mw.config.set('wgNamespaceNumber', parseInt(match[1], 10));
 
                        var matchTitle = src.match(/"wgTitle":"([^"]+)"/);
                        if (matchTitle) mw.config.set('wgTitle', matchTitle[1]);
 
                        var matchPage = src.match(/"wgPageName":"([^"]+)"/);
                        if (matchPage) mw.config.set('wgPageName', matchPage[1]);
 
                        var matchSpecial = src.match(/"wgCanonicalSpecialPageName":"([^"]+)"/);
                        if (matchSpecial) {
                            mw.config.set('wgCanonicalSpecialPageName', matchSpecial[1]);
                        } else {
                            mw.config.set('wgCanonicalSpecialPageName', false);
                        }
                        break;
                    }
                }
 
                var newContent = doc.querySelector('.liberty-content-main');
                var newTitle = doc.querySelector('.mw-page-title-main');
                var newHead = doc.querySelector('title');
                var newHeader = doc.querySelector('.liberty-content-header');
 
                if (newContent) {
                    $('#side-toc-box').remove();
                    $('.liberty-content-main').html(newContent.innerHTML);
                    $('.profile-card').remove();
                    $('body').removeClass('page-loading');
                }


    12% {
                if (newTitle) {
        transform: translateX(1px);
                    $('.mw-page-title-main').html(newTitle.innerHTML);
        opacity: 0.22;
                }
    }


    36% {
                if (newHead) {
        transform: translateX(0);
                    document.title = newHead.textContent;
        opacity: 0.30;
                }
    }


    37% {
                if (newHeader) {
        transform: translateX(1px);
                    $('.liberty-content-header').html(newHeader.innerHTML);
        opacity: 0.38;
                }
    }


    38% {
                window.scrollTo(0, 0);
        transform: translateX(-1px);
                $('#clbi-lang-list').hide();
        opacity: 0.24;
                $('#clbi-lang-current').css('margin-bottom', '0');
    }


    64% {
                mw.hook('wikipage.content').fire($('.liberty-content-main'));
        transform: translateX(0);
                applyMainPageStyle();
        opacity: 0.32;
    }


    65% {
                $('#side-toc-box').remove();
        transform: translateX(-1px);
                setTimeout(moveTocToLeftSidebar, 100);
        opacity: 0.40;
                setTimeout(moveTocToLeftSidebar, 500);
    }
                setTimeout(moveTocToLeftSidebar, 1200);


    66% {
                mw.loader.using(['mediawiki.api']).then(function() {
        transform: translateX(0);
                    initProfile();
        opacity: 0.26;
                    moveTocToLeftSidebar();
                });
            });
     }
     }
}


@keyframes compact-crt-image-jitter {
// 목차 링크는 전용 처리
    0%, 82%, 100% {
$(document).on('click', '#side-toc-box a, #toc a, .toc a', function(e) {
        transform: translateX(0);
    var href = $(this).attr('href');
     }
     if (!href || href.charAt(0) !== '#') return;


     83% {
     var rawId = href.slice(1);
        transform: translateX(-1px);
     if (!rawId) return;
     }


     84% {
     var decodedId = rawId;
        transform: translateX(1px);
    }


     85% {
     try {
         transform: translateX(0);
         decodedId = decodeURIComponent(rawId);
    } catch (err) {
        decodedId = rawId;
     }
     }


     92% {
     var target = document.getElementById(decodedId);
        transform: translateX(1px);
    }


     93% {
     if (!target && window.CSS && CSS.escape) {
         transform: translateX(-1px);
         target = document.querySelector('#' + CSS.escape(decodedId));
     }
     }


     94% {
     if (!target) return;
        transform: translateX(0);
    }
}


.compact-crt-media.compact-crt-portrait {
    e.preventDefault();
    padding: 4px 5px;
     e.stopPropagation();
     border-radius: 8px;
}


.compact-crt-media.compact-crt-portrait::before {
    var scrollTarget = target.closest('h2, h3') || target;
    inset: 3px;
    border-radius: 5px;
}


.compact-crt-media.compact-crt-portrait::after {
    scrollTarget.scrollIntoView({
    inset: 3px;
        behavior: 'auto',
    border-radius: 5px;
        block: 'start'
}
    });


.compact-crt-media.compact-crt-portrait .compact-crt-static {
    history.replaceState(null, '', '#' + rawId);
    inset: 3px;
});
    border-radius: 5px;
}


.compact-crt-media.compact-crt-portrait img,
    $(document).on('click', 'a', function(e) {
.compact-crt-media.compact-crt-portrait .mw-file-element {
        var href = $(this).attr('href');
    display: block;
        if (!href) return;
}


/* =========================================
        // 목차 링크는 별도 핸들러에서 처리
  Related Template Title
        if ($(this).closest('#side-toc-box, #toc, .toc').length) return;
  ========================================= */


.related-template-title {
         // 단순 해시 링크는 SPA 가로채기 제외
    position: relative;
         if (href.startsWith('#')) return;
    overflow: hidden;
    width: 100%;
    border-radius: 5px;
    background: #111111;
    color: #fff;
    box-shadow:
         inset 1px 0 0 #494949,
         inset 0 -1px 0 #555;
}


.related-template-title::before {
         var link = document.createElement('a');
    content: "";
         link.href = href;
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    border-radius: inherit;
    background:
         linear-gradient(
            to bottom,
            rgba(255,255,255,0.070) 0%,
            rgba(255,255,255,0.026) 18%,
            transparent 45%,
            rgba(0,0,0,0.18) 100%
        ),
        linear-gradient(
            105deg,
            transparent 0%,
            rgba(255,255,255,0.045) 18%,
            transparent 36%,
            transparent 100%
        );
    box-shadow:
         inset 0 0 0 1px rgba(255,255,255,0.026),
        inset 0 0 14px rgba(0,0,0,0.30);
}


.related-template-title::after {
        var samePath = decodeURIComponent(link.pathname) === decodeURIComponent(window.location.pathname);
    content: "";
         var sameSearch = (link.search || '') === (window.location.search || '');
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    border-radius: inherit;
    opacity: 0.20;
    background:
         repeating-linear-gradient(
            to bottom,
            rgba(255,255,255,0.055) 0px,
            rgba(255,255,255,0.055) 1px,
            transparent 2px,
            transparent 4px
        );
    mix-blend-mode: screen;
}


.related-template-title > * {
        if (link.hash && samePath && sameSearch) return;
    position: relative;
    z-index: 3;
}


/* =========================================
        var currentBase = window.location.href.split('#')[0];
  Page Top CRT Banner
        var targetBase = link.href.split('#')[0];
  Template use:
  {{Banner|[[파일:example.png{{!}}1000px{{!}}link=]]|80px|20px}}
  2번 인자: 양수면 좌측 이동
  3번 인자: 양수면 아래 이동
  ========================================= */


.crt-page-monitor-frame {
         if (link.hash && currentBase === targetBase) return;
    position: relative;
    box-sizing: border-box;
    width: 100%;
    aspect-ratio: 2560 / 580;
    margin: 0 auto 16px auto;
    padding: 14px 20px 16px 20px;
    border-radius: 18px;
    background:
         linear-gradient(
            145deg,
            #151515 0%,
            #0b0b0b 28%,
            #050505 58%,
            #181818 100%
        );
    border: 2px solid #242424;
    box-shadow:
        inset 0 3px 8px rgba(0,0,0,0.78),
        inset 0 -2px 0 rgba(255,255,255,0.030),
        inset 4px 0 10px rgba(0,0,0,0.72),
        inset -3px 0 0 rgba(0,0,0,0.78),
        0 0 0 3px #050505,
        0 0 0 5px #242424;
    overflow: hidden;
}


.crt-page-monitor-frame::before {
         if (!isInternal(href)) return;
    content: "";
         if (shouldSkip(href)) return;
    position: absolute;
    inset: 0;
    border-radius: 18px;
    pointer-events: none;
    opacity: 0.08;
    background:
         linear-gradient(
            to bottom,
            rgba(0,0,0,0.38) 0%,
            transparent 20%,
            transparent 100%
        ),
         linear-gradient(
            to right,
            rgba(0,0,0,0.34) 0%,
            transparent 18%,
            transparent 100%
        );
    mix-blend-mode: normal;
}


.crt-page-monitor-frame::after {
        e.preventDefault();
    content: none;
        playStaticSound();
}
        $('body').addClass('page-loading');
        history.pushState(null, '', href);
        loadPage(href);
    });


.crt-page-monitor-inner {
    window.addEventListener('popstate', function() {
    position: relative;
         loadPage(window.location.href);
    box-sizing: border-box;
    });
    width: 100%;
});
    height: 100%;
    padding: 6px;
    border-radius: 13px;
    background:
        linear-gradient(
            145deg,
            #020202 0%,
            #080808 45%,
            #030303 100%
        );
    border: 1px solid #020202;
    box-shadow:
        inset 0 11px 24px rgba(0,0,0,0.98),
         inset 0 -2px 3px rgba(255,255,255,0.028),
        inset 8px 0 20px rgba(0,0,0,0.94),
        inset -8px 0 20px rgba(0,0,0,0.94),
        0 0 0 1px rgba(255,255,255,0.030);
}


.crt-page-monitor-screen {
// 시간 계산 함수
     position: relative;
function timeAgo(timestamp) {
     width: 100%;
     var now = new Date();
     height: 100%;
     var date = new Date(timestamp);
    overflow: hidden;
     var diff = Math.floor((now - date) / 1000);
    border-radius: 8px / 15px;
    background: #050505;
    border: 1px solid #060606;
    box-shadow:
        inset 0 0 18px rgba(0,0,0,0.88),
        inset 0 0 42px rgba(0,0,0,0.70),
        0 0 0 1px rgba(255,255,255,0.026);
}


.crt-page-monitor-image,
     if (diff < 60) return diff + '초 전';
.crt-page-monitor-image-base,
     if (diff < 3600) return Math.floor(diff / 60) + '분 전';
.crt-page-monitor-image-bloom,
     if (diff < 86400) return Math.floor(diff / 3600) + '시간 전';
.crt-page-monitor-slice {
     return Math.floor(diff / 86400) + '일 전';
     position: absolute;
     inset: 0;
     line-height: 0;
     overflow: hidden;
}
}


.crt-page-monitor-image {
// 펼접 토글
     z-index: 1;
// 펼접 토글
function getFoldTexts() {
     var lang = getCurrentLang();
    return (window.LANG && window.LANG[lang])
        ? window.LANG[lang]
        : (window.LANG ? window.LANG.ko : { expand: '펼치기', collapse: '접기' });
}
}


.crt-page-monitor-image-base {
function refreshOpenAncestors($start) {
    z-index: 3;
    $start.parents('[id^="collapsible"]').each(function () {
}
        var $parent = $(this);
        if (!$parent.hasClass('folding-open')) return;


.crt-page-monitor-image-bloom {
         // 이미 fully open 상태면 굳이 다시 잠그지 않음
    z-index: 4;
         if ($parent.data('fold-state') === 'open') {
    pointer-events: none;
            return;
    opacity: 0.38;
        }
    mix-blend-mode: normal;
    filter:
         blur(9px)
         brightness(0.92)
        contrast(1.02)
        saturate(1.04);
}


.crt-page-monitor-image-base a,
        $parent.css('max-height', this.scrollHeight + 'px');
.crt-page-monitor-image-base span,
     });
.crt-page-monitor-image-base .mw-file-description,
.crt-page-monitor-image-base .mw-default-size,
.crt-page-monitor-image-base .mw-image-border,
.crt-page-monitor-image-bloom a,
.crt-page-monitor-image-bloom span,
.crt-page-monitor-image-bloom .mw-file-description,
.crt-page-monitor-image-bloom .mw-default-size,
.crt-page-monitor-image-bloom .mw-image-border,
.crt-page-monitor-slice a,
.crt-page-monitor-slice span,
.crt-page-monitor-slice .mw-file-description,
.crt-page-monitor-slice .mw-default-size,
.crt-page-monitor-slice .mw-image-border {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
    width: 100% !important;
     height: 100% !important;
}
}


.crt-page-monitor-image-base img,
function bindInnerResizeUpdates($target) {
.crt-page-monitor-image-base .mw-file-element,
    // 이미지 늦게 로드될 때 높이 갱신
.crt-page-monitor-image-bloom img,
    $target.find('img').off('.foldimg').on('load.foldimg', function () {
.crt-page-monitor-image-bloom .mw-file-element,
         if ($target.hasClass('folding-open')) {
.crt-page-monitor-slice img,
             if ($target.data('fold-state') !== 'open') {
.crt-page-monitor-slice .mw-file-element,
                $target.css('max-height', $target[0].scrollHeight + 'px');
.crt-page-monitor-slice-img {
            }
    position: absolute !important;
            refreshOpenAncestors($target);
    inset: 0 !important;
        }
    display: block !important;
     });
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    object-fit: cover !important;
    object-position: center center;
    border: 0 !important;
    transform:
         translate(
            calc(0px - var(--crt-img-left, 0px)),
             var(--crt-img-down, 0px)
        )
        scale(var(--crt-img-scale, 1.04));
     transform-origin: center center;
}
}


.crt-page-monitor-image-base img,
function openFold($target, $btn) {
.crt-page-monitor-image-base .mw-file-element {
     var t = getFoldTexts();
     filter:
        brightness(0.84)
        contrast(1.08)
        saturate(0.90);
}


.crt-page-monitor-image-bloom img,
    $target.data('fold-state', 'opening');
.crt-page-monitor-image-bloom .mw-file-element {
    $target.addClass('folding-open');
    transform:
        translate(
            calc(0px - var(--crt-img-left, 0px)),
            var(--crt-img-down, 0px)
        )
        scale(calc(var(--crt-img-scale, 1.04) + 0.02));
}


.crt-page-monitor-slice img,
    // 열린 뒤 자연 확장 가능하게 만들기 위해 먼저 px로 열기
.crt-page-monitor-slice .mw-file-element,
    $target.css('max-height', '0px');
.crt-page-monitor-slice-img {
     $target[0].offsetHeight;
     filter:
    $target.css('max-height', $target[0].scrollHeight + 'px');
        brightness(0.96)
        contrast(1.18)
        saturate(0.82);
}


.crt-page-monitor-image::after {
     $btn.text(t.collapse);
     content: "";
    position: absolute;
    inset: -3%;
    z-index: 6;
    pointer-events: none;
    opacity: 0.16;
    background:
        radial-gradient(
            ellipse at 50% 45%,
            rgba(220,235,230,0.12) 0%,
            rgba(170,190,185,0.05) 34%,
            rgba(70,85,82,0.03) 62%,
            transparent 100%
        ),
        linear-gradient(
            to bottom,
            rgba(255,255,255,0.04) 0%,
            rgba(255,255,255,0.014) 28%,
            transparent 70%,
            rgba(0,0,0,0.08) 100%
        );
    mix-blend-mode: normal;
}


.crt-page-monitor-slice {
     bindInnerResizeUpdates($target);
     z-index: 5;
    opacity: 0;
    pointer-events: none;
    mix-blend-mode: screen;
    will-change: transform, opacity, clip-path, filter;
}


.crt-page-monitor-slice-a {
    // 바깥 펼접 즉시 갱신
     animation: crt-slice-tear-a 4.7s steps(1, end) infinite;
     refreshOpenAncestors($target);
}


.crt-page-monitor-slice-b {
    // 전환 끝나면 none으로 풀어서 중첩 펼접/동적 내용 증가를 자연스럽게 허용
     animation: crt-slice-tear-b 6.1s steps(1, end) infinite;
     $target.off('transitionend.foldopen').on('transitionend.foldopen', function (e) {
}
        if (e.target !== this) return;
        if (!$target.hasClass('folding-open')) return;


.crt-page-monitor-slice-c {
        $target.css('max-height', 'none');
    animation: crt-slice-tear-c 7.4s steps(1, end) infinite;
        $target.data('fold-state', 'open');
}


.crt-page-monitor-screen::before {
         refreshOpenAncestors($target);
    content: "";
     });
    position: absolute;
    inset: 0;
    z-index: 20;
    pointer-events: none;
    border-radius: 8px / 15px;
    background:
         radial-gradient(
            ellipse at 50% 50%,
            transparent 0%,
            transparent 62%,
            rgba(0,0,0,0.16) 78%,
            rgba(0,0,0,0.42) 100%
        ),
        linear-gradient(
            to bottom,
            rgba(0,0,0,0.18) 0%,
            transparent 14%,
            transparent 82%,
            rgba(0,0,0,0.24) 100%
        ),
        linear-gradient(
            to right,
            rgba(0,0,0,0.18) 0%,
            transparent 8%,
            transparent 92%,
            rgba(0,0,0,0.18) 100%
        ),
        linear-gradient(
            105deg,
            transparent 0%,
            rgba(255,255,255,0.030) 18%,
            transparent 38%,
            transparent 100%
        );
     box-shadow:
        inset 0 0 20px rgba(255,255,255,0.018),
        inset 0 0 54px rgba(0,0,0,0.54);
}


.crt-page-monitor-screen::after {
     // 늦게 렌더되는 콘텐츠 대응
     content: "";
     requestAnimationFrame(function () {
     position: absolute;
         if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
    inset: -80px 0;
             $target.css('max-height', $target[0].scrollHeight + 'px');
    z-index: 21;
             refreshOpenAncestors($target);
    pointer-events: none;
        }
    opacity: 0.20;
     });
    background:
         repeating-linear-gradient(
            to bottom,
            rgba(255,255,255,0.08) 0px,
             rgba(255,255,255,0.08) 1px,
             transparent 2px,
            transparent 5px
        );
    mix-blend-mode: screen;
     animation: crt-scanlines-up 7s linear infinite;
}


.crt-page-monitor-glitch {
    setTimeout(function () {
    position: absolute;
         if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
    inset: 0;
             $target.css('max-height', $target[0].scrollHeight + 'px');
    z-index: 22;
             refreshOpenAncestors($target);
    pointer-events: none;
         }
    opacity: 0;
     }, 80);
    background:
         linear-gradient(
            to bottom,
            transparent 0%,
            rgba(255,255,255,0.12) 48%,
             rgba(212,90,162,0.14) 50%,
             rgba(90,120,255,0.08) 52%,
            transparent 56%
         );
     mix-blend-mode: screen;
    animation: crt-soft-glitch-band 8.2s steps(1, end) infinite;
}


.crt-page-monitor-tear {
    setTimeout(function () {
    position: absolute;
         if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
    inset: 0;
             $target.css('max-height', $target[0].scrollHeight + 'px');
    z-index: 23;
             refreshOpenAncestors($target);
    pointer-events: none;
         }
    opacity: 0;
     }, 220);
    background:
         linear-gradient(
            to right,
            transparent 0%,
            rgba(120,180,255,0.07) 10%,
             rgba(255,255,255,0.11) 48%,
             rgba(212,90,162,0.08) 78%,
            transparent 100%
         );
     mix-blend-mode: screen;
    animation: crt-soft-horizontal-tear 5.6s steps(1, end) infinite;
}
}


@keyframes crt-slice-tear-a {
function closeFold($target, $btn) {
     0%, 11%, 100% {
     var t = getFoldTexts();
        opacity: 0;
        transform: translateX(0);
        clip-path: inset(0 0 100% 0);
    }


     12% {
     // none 상태에서 닫으면 transition이 안 되므로 실제 높이로 고정
         opacity: 0.38;
    if ($target.css('max-height') === 'none' || $target.data('fold-state') === 'open') {
        transform: translateX(-18px);
         $target.css('max-height', $target[0].scrollHeight + 'px');
         clip-path: inset(17% 0 78% 0);
    } else {
         $target.css('max-height', $target[0].scrollHeight + 'px');
     }
     }


     13% {
     $target.data('fold-state', 'closing');
        opacity: 0.22;
    $target[0].offsetHeight;
        transform: translateX(11px);
    $target.css('max-height', '0px');
        clip-path: inset(18% 0 77% 0);
    $target.removeClass('folding-open');
        filter: hue-rotate(-8deg);
    }


     14% {
     $btn.text(t.expand);
        opacity: 0;
        transform: translateX(0);
        clip-path: inset(0 0 100% 0);
    }


     63% {
     refreshOpenAncestors($target);
        opacity: 0.30;
        transform: translateX(9px);
        clip-path: inset(24% 0 70% 0);
    }


     64% {
     setTimeout(function () {
         opacity: 0;
         refreshOpenAncestors($target);
        transform: translateX(0);
         $target.data('fold-state', 'closed');
         clip-path: inset(0 0 100% 0);
     }, 250);
     }
}
}


@keyframes crt-slice-tear-b {
$(function () {
     0%, 37%, 100% {
     $(document)
        opacity: 0;
        .off('click.clbiToggle')
        transform: translateX(0);
        .on('click.clbiToggle', '.toggleBtn', function () {
        clip-path: inset(0 0 100% 0);
            var $btn = $(this);
    }
            var targetId = $btn.data('target');
            var $target = $('#' + targetId);
            if (!$target.length) return;
 
            var scrollY = window.scrollY;
 
            if ($target.hasClass('folding-open')) {
                closeFold($target, $btn);
            } else {
                openFold($target, $btn);
            }


    38% {
            window.scrollTo(0, scrollY);
        opacity: 0.42;
         });
        transform: translateX(24px);
});
         clip-path: inset(43% 0 50% 0);
    }


    39% {
// ========== 프로필 시스템 ==========
        opacity: 0.26;
function initProfile() {
        transform: translateX(-14px);
    $('.profile-card').remove();
        clip-path: inset(45% 0 48% 0);
        filter: hue-rotate(10deg) brightness(1.08);
    }


     40% {
     var ns = mw.config.get('wgNamespaceNumber');
        opacity: 0;
    var title = mw.config.get('wgTitle');
        transform: translateX(0);
        clip-path: inset(0 0 100% 0);
    }


     78% {
     if (ns === 2) {
         opacity: 0.34;
         var profileUser = title.split('/')[0];
        transform: translateX(-20px);
         renderProfile(profileUser);
         clip-path: inset(51% 0 43% 0);
     }
     }


     79% {
     if (mw.config.get('wgCanonicalSpecialPageName') === '사용자정보') {
        opacity: 0;
         initUserProfilePage();
        transform: translateX(0);
         clip-path: inset(0 0 100% 0);
     }
     }
}
}


@keyframes crt-slice-tear-c {
function renderProfile(username) {
     0%, 21%, 100% {
     var api = new mw.Api();
         opacity: 0;
    api.get({
         transform: translateX(0);
        action: 'query',
         clip-path: inset(0 0 100% 0);
        list: 'users',
    }
        ususers: username,
        usprop: 'editcount'
    }).then(function(data) {
         var user = data.query.users[0];
         var contentEl = document.getElementById('mw-content-text');
         if (!contentEl) return;


    22% {
         var pageContent = contentEl.querySelector('.mw-parser-output') || contentEl;
         opacity: 0.28;
         injectProfileCard(username, user, pageContent);
        transform: translateX(-30px);
     });
         clip-path: inset(67% 0 25% 0);
}
     }


    23% {
function injectProfileCard(username, userData, container) {
        opacity: 0.18;
    var isOwnPage = mw.config.get('wgUserName') === username;
        transform: translateX(16px);
    var editCount = (userData && userData.editcount) ? userData.editcount : 0;
         clip-path: inset(68% 0 23% 0);
    var editBtn = isOwnPage
         filter: contrast(1.25) brightness(1.06);
         ? '<a href="/index.php/특수:사용자정보" class="profile-edit-btn">프로필 수정</a>'
    }
         : '';


     24% {
     var card = document.createElement('div');
         opacity: 0;
    card.className = 'profile-card';
         transform: translateX(0);
    card.innerHTML =
         clip-path: inset(0 0 100% 0);
         '<div class="profile-header">' +
    }
            '<div class="profile-avatar">' +
                '<img src="/index.php?title=특수:Redirect/file/Pfp-' + username + '.png&width=120"' +
                ' onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png&width=120\'"' +
                ' alt="' + username + '">' +
            '</div>' +
            '<div class="profile-info">' +
                '<h2 class="profile-username">' + username + '</h2>' +
                '<div class="profile-name" data-field="name"></div>' +
                '<div class="profile-role" data-field="role"></div>' +
                '<div class="profile-discord" data-field="discord"></div>' +
                '<div class="profile-bio" data-field="bio"></div>' +
                '<div class="profile-badges" data-field="badges"></div>' +
            '</div>' +
            editBtn +
         '</div>' +
         '<div class="profile-stats">' +
            '<div class="profile-stat">' +
                '<span class="clbi-stat-value">' + editCount + '</span>' +
                '<span class="clbi-stat-label">수정 횟수</span>' +
            '</div>' +
        '</div>';


     91% {
     $('.profile-card').remove();
        opacity: 0.36;
    container.insertBefore(card, container.firstChild);
        transform: translateX(18px);
    loadProfileFields(username, card);
        clip-path: inset(73% 0 18% 0);
}
    }


     92% {
function loadProfileFields(username, card) {
         opacity: 0;
    var api = new mw.Api();
         transform: translateX(0);
     api.get({
         clip-path: inset(0 0 100% 0);
         action: 'userprofile',
     }
         user: username
    }).then(function(data) {
        var profile = data.userprofile;
         updateProfileFields(card, {
            name: profile.name || '',
            discord: profile.discord || '',
            role: profile.role || '',
            bio: profile.bio || '',
            badges: profile.badges || ''
        });
    }).fail(function() {
        updateProfileFields(card, {
            name: '',
            discord: '',
            role: '',
            bio: '',
            badges: ''
        });
     });
}
}


@keyframes crt-soft-glitch-band {
function updateProfileFields(card, data) {
     0%, 52%, 100% {
    var nameEl = card.querySelector('[data-field="name"]');
        opacity: 0;
    var roleEl = card.querySelector('[data-field="role"]');
        transform: translateY(80%);
    var discordEl = card.querySelector('[data-field="discord"]');
     }
    var bioEl = card.querySelector('[data-field="bio"]');
    var badgesEl = card.querySelector('[data-field="badges"]');
 
     if (nameEl) nameEl.textContent = data.name || '';
    if (roleEl) roleEl.textContent = data.role || '';
    if (discordEl) discordEl.textContent = data.discord ? ('디스코드: ' + data.discord) : '';
     if (bioEl) bioEl.textContent = data.bio || '';


     53% {
     if (badgesEl) {
         opacity: 0.42;
         if (data.badges) {
        transform: translateY(36%);
            var badges = data.badges.split(',');
    }
            var html = '';


    54% {
            for (var i = 0; i < badges.length; i++) {
        opacity: 0.16;
                html += '<span class="clbi-badge">' + badges[i].trim() + '</span>';
        transform: translateY(20%) skewX(-1deg);
            }
    }


    55% {
            badgesEl.innerHTML = html;
        opacity: 0;
        } else {
         transform: translateY(-20%);
            badgesEl.innerHTML = '';
         }
     }
     }
}
}
// ========== 프로필 시스템 끝 ==========


@keyframes crt-soft-horizontal-tear {
// ========== 알림 시스템 ==========
     0%, 68%, 100% {
function ensureNotificationPopup() {
        opacity: 0;
     if (document.getElementById('clbi-notification-popup')) return;
        transform: translateX(0);
        clip-path: inset(0 0 0 0);
    }


     69% {
     var popup = document.createElement('div');
         opacity: 0.32;
    popup.id = 'clbi-notification-popup';
         transform: translateX(-16px);
    popup.style.cssText =
         clip-path: inset(34% 0 58% 0);
         'display:none;position:fixed;z-index:99999;width:320px;max-height:420px;' +
    }
         'background:#0a0909;border:2px solid #854369;border-radius:10px;' +
         'box-shadow:0 0 0 1px #1a1a1a, 0 8px 24px rgba(0,0,0,0.55);overflow:hidden;';


     70% {
     popup.innerHTML =
         opacity: 0.18;
         '<div style="padding:10px 12px;border-bottom:2px solid #854369;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);color:#E2E2E2;font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:space-between;gap:8px;">' +
         transform: translateX(12px);
            '<span>알림</span>' +
         clip-path: inset(37% 0 55% 0);
            '<button type="button" id="clbi-notification-readall" style="background:#171717;border:1px solid #854369;border-radius:6px;color:#E2E2E2;font-size:11px;font-weight:700;padding:4px 8px;cursor:pointer;">전체 읽음</button>' +
    }
        '</div>' +
         '<div id="clbi-notification-list" style="max-height:320px;overflow-y:auto;padding:8px 0;color:#E2E2E2;font-size:12px;">불러오는 중...</div>' +
         '<div style="padding:8px;border-top:1px solid #2a2a2a;background:#111;">' +
            '<a href="/index.php?title=Special:Notifications" id="clbi-notification-more" style="display:block;width:100%;text-align:center;padding:8px 10px;border-radius:6px;background:#171717;border:1px solid #854369;color:#E2E2E2 !important;text-decoration:none !important;font-size:12px;font-weight:700;">더보기</a>' +
        '</div>';


     71% {
     document.body.appendChild(popup);
        opacity: 0;
        transform: translateX(0);
        clip-path: inset(0 0 0 0);
    }
}
}


@media (prefers-reduced-motion: reduce) {
function positionNotificationPopup() {
     .crt-page-monitor-slice-a,
     var btn = document.getElementById('clbi-notification-toggle');
     .crt-page-monitor-slice-b,
    var popup = document.getElementById('clbi-notification-popup');
     .crt-page-monitor-slice-c,
    if (!btn || !popup) return;
     .crt-page-monitor-screen::after,
 
     .crt-page-monitor-glitch,
     var rect = btn.getBoundingClientRect();
     .crt-page-monitor-tear {
     var top = rect.bottom + 2;
         animation: none !important;
     var left = rect.right - popup.offsetWidth;
 
    if (left < 8) left = 8;
     if (left + popup.offsetWidth > window.innerWidth - 8) {
        left = window.innerWidth - popup.offsetWidth - 8;
    }
     if (top + popup.offsetHeight > window.innerHeight - 8) {
         top = Math.max(8, window.innerHeight - popup.offsetHeight - 8);
     }
     }


     .crt-page-monitor-slice,
     popup.style.top = top + 'px';
     .crt-page-monitor-glitch,
     popup.style.left = left + 'px';
    .crt-page-monitor-tear {
        opacity: 0 !important;
    }
}
}


/* =========================================
function parseNotificationItemsFromHtml(html) {
  Historical Events Timeline
    var parser = new DOMParser();
  ========================================= */
    var doc = parser.parseFromString(html, 'text/html');
 
    var selectors = [
        '.mw-echo-ui-notificationItemWidget',
        '.mw-echo-ui-notificationsInboxWidgetRow',
        '.echo-ui-notificationItemWidget',
        'li[data-notification-id]',
        '.mw-echo-notifications-list li'
    ];


.timeline-event {
     var items = [];
     position: relative;
     for (var i = 0; i < selectors.length; i++) {
     display: block;
        items = Array.prototype.slice.call(doc.querySelectorAll(selectors[i]));
    width: 100%;
        if (items.length) break;
    box-sizing: border-box;
     }
    line-height: inherit;
     text-align: center;
}


.timeline-title {
    return items.slice(0, 5).map(function(item) {
    position: relative;
        var link = item.querySelector('a[href]');
    z-index: 2;
        var href = link ? link.getAttribute('href') : '/index.php?title=Special:Notifications';
    display: inline-block;
        var text = (item.textContent || '').replace(/\s+/g, ' ').trim();
    text-align: center;
}


.timeline-event.he-linked::before {
         var notificationId =
    content: "";
             item.getAttribute('data-notification-id') ||
    position: absolute;
             item.getAttribute('data-id') ||
    top: -2px;
             item.getAttribute('data-notification') ||
    left: 2px;
             '';
    right: 2px;
    height: 3px;
    border-radius: 999px;
    pointer-events: none;
    background:
         linear-gradient(
            to right,
            transparent 0%,
            color-mix(in srgb, var(--he-color) 18%, transparent) 8%,
             color-mix(in srgb, var(--he-color) 92%, transparent) 14%,
             color-mix(in srgb, var(--he-color) 92%, transparent) 86%,
             color-mix(in srgb, var(--he-color) 18%, transparent) 92%,
             transparent 100%
        );
    box-shadow:
        0 0 7px color-mix(in srgb, var(--he-color) 28%, transparent);
}


.timeline-event.he-root {
        if (!notificationId) {
    overflow: visible;
            var anyWithId = item.querySelector('[data-notification-id], [data-id], [data-notification]');
}
            if (anyWithId) {
                notificationId =
                    anyWithId.getAttribute('data-notification-id') ||
                    anyWithId.getAttribute('data-id') ||
                    anyWithId.getAttribute('data-notification') ||
                    '';
            }
        }


.timeline-event.he-root::before {
        if (href && href.indexOf('http') !== 0) {
    content: "";
             href = href.charAt(0) === '/'
    position: absolute;
                ? href
    left: 6%;
                : '/index.php' + (href.charAt(0) === '?' ? href : '/' + href);
    right: 6%;
         }
    top: 50%;
    height: 16px;
    transform: translateY(-50%);
    border-radius: 999px;
    pointer-events: none;
    background:
        linear-gradient(
            to bottom,
            rgba(255,255,255,0.05) 0%,
             rgba(0,0,0,0.24) 100%
        ),
        linear-gradient(
            to right,
            transparent 0%,
            color-mix(in srgb, var(--he-color) 10%, transparent) 8%,
            color-mix(in srgb, var(--he-color) 24%, transparent) 18%,
            color-mix(in srgb, var(--he-color) 36%, transparent) 50%,
            color-mix(in srgb, var(--he-color) 24%, transparent) 82%,
            color-mix(in srgb, var(--he-color) 10%, transparent) 92%,
            transparent 100%
        );
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.05),
        inset 0 -1px 0 rgba(0,0,0,0.42),
        0 0 8px color-mix(in srgb, var(--he-color) 16%, transparent);
    -webkit-mask-image:
        linear-gradient(
            to right,
            transparent 0%,
            black 10%,
            black 90%,
            transparent 100%
        );
    mask-image:
         linear-gradient(
            to right,
            transparent 0%,
            black 10%,
            black 90%,
            transparent 100%
        );
}


.timeline-event.he-root::after {
        return {
    content: "";
            id: notificationId,
    position: absolute;
             href: href,
    left: 10%;
             text: text || '알림'
    right: 10%;
         };
    top: 50%;
    });
    height: 22px;
    transform: translateY(-50%);
    pointer-events: none;
    background:
        linear-gradient(
            to right,
             transparent 0%,
             color-mix(in srgb, var(--he-color) 48%, transparent) 16%,
            color-mix(in srgb, var(--he-color) 48%, transparent) 84%,
            transparent 100%
         ) top center / 100% 1px no-repeat,
        linear-gradient(
            to right,
            transparent 0%,
            color-mix(in srgb, var(--he-color) 24%, transparent) 16%,
            color-mix(in srgb, var(--he-color) 24%, transparent) 84%,
            transparent 100%
        ) bottom center / 100% 1px no-repeat;
}
}


.timeline-event.he-root .timeline-title {
function renderNotificationPopup(items) {
     text-shadow:
     var list = document.getElementById('clbi-notification-list');
        0 0 4px color-mix(in srgb, var(--he-color) 34%, transparent),
    var badge = document.getElementById('clbi-notification-badge');
        0 0 9px color-mix(in srgb, var(--he-color) 16%, transparent);
    if (!list) return;
}


/* =========================================
    if (!items || !items.length) {
  Tree List
        list.innerHTML = '<div style="padding:14px 12px;color:#999;">표시할 알림이 없습니다.</div>';
  ========================================= */
        if (badge) badge.style.display = 'none';
        return;
    }


.tree-container {
    var html = '';
    margin: 0;
    for (var i = 0; i < items.length; i++) {
    padding: 0;
        html +=
}
            '<a href="' + items[i].href + '" class="clbi-notification-item" data-notification-id="' + (items[i].id || '') + '" style="display:block;padding:10px 12px;color:#E2E2E2 !important;text-decoration:none !important;border-bottom:1px solid #1f1f1f;line-height:1.5;">' +
                items[i].text +
            '</a>';
    }
    list.innerHTML = html;


.tree-node {
    if (badge) {
    position: relative;
        badge.textContent = items.length;
    padding-left: 20px;
        badge.style.display = 'block';
    margin: 0;
     }
    text-indent: 0;
     line-height: 1.55;
}
}


.tree-node::before {
function loadNotificationsIntoPopup() {
     content: "";
     var list = document.getElementById('clbi-notification-list');
     position: absolute;
     if (list) {
    top: 0;
        list.innerHTML = '<div style="padding:14px 12px;color:#999;">불러오는 중...</div>';
    left: 7px;
     }
    width: 1.2px;
    height: 100%;
     background: #7F7F7F;
}


.tree-node::after {
    fetch('/index.php?title=Special:Notifications', { credentials: 'same-origin' })
    content: "";
        .then(function(res) {
    position: absolute;
            return res.text();
    top: 12px;
        })
    left: 7px;
        .then(function(html) {
    width: 8px;
            var items = parseNotificationItemsFromHtml(html);
    height: 0;
            renderNotificationPopup(items);
    border-top: 1.2px solid #7F7F7F;
        })
        .catch(function(err) {
            console.error(err);
            if (list) {
                list.innerHTML = '<div style="padding:14px 12px;color:#999;">알림을 불러오지 못했습니다.</div>';
            }
        });
}
}


.tree-node:last-of-type::before {
function markAllNotificationsRead() {
     height: 12px;
    return new mw.Api().postWithToken('csrf', {
        action: 'echomarkread',
        list: 'all'
     });
}
}


/* =========================================
function markNotificationReadById(notificationId) {
  Meeting Composition Panel
    if (!notificationId) {
  ========================================= */
        return $.Deferred().resolve().promise();
    }


.meeting-panel {
    return new mw.Api().postWithToken('csrf', {
    position: relative;
         action: 'echomarkread',
    isolation: isolate;
         list: notificationId
    z-index: 0;
     });
    width: calc(100% + 4px);
    left: -2px;
    margin: 0 auto 15px auto;
    border: 2px solid #1a1a1a;
    border-radius: 5px;
    background:
        radial-gradient(
            ellipse at 50% 35%,
            rgba(255,255,255,0.025) 0%,
            transparent 46%
        ),
        linear-gradient(
            to bottom,
            #1A1A1A 0%,
            #181818 52%,
            #151515 100%
         );
    color: #FFF;
    box-shadow:
         0 4px 12px #000;
    text-align: center;
     font-size: 11px;
    box-sizing: border-box;
    overflow: visible;
}
}


.meeting-panel::before {
function initNotifications() {
     content: "";
     var btn = document.getElementById('clbi-notification-toggle');
    position: absolute;
     if (!btn) return;
    inset: -5px;
    z-index: -2;
    border-radius: 8px;
     background: #854369;
    pointer-events: none;
}


.meeting-panel::after {
     ensureNotificationPopup();
     content: "";
     loadNotificationsIntoPopup();
     position: absolute;
    inset: -3px;
    z-index: -1;
    border-radius: 7px;
    background: #1A1A1A;
    pointer-events: none;
}


.meeting-panel-body {
     $(document)
     width: 100%;
        .off('click.clbiNotificationToggle')
    color: #E4E4E4;
        .on('click.clbiNotificationToggle', '#clbi-notification-toggle', function(e) {
    padding: 4.5px 9px 9px 9px;
            e.preventDefault();
    box-sizing: border-box;
            e.stopPropagation();
    border-radius: 3px;
    background: transparent;
    overflow: hidden;
}


.meeting-title-wrap {
            var popup = document.getElementById('clbi-notification-popup');
    width: 100%;
            var playlistPopup = document.getElementById('clbi-playlist-popup');
    max-width: 760px;
            if (!popup) return;
    margin: 5px auto 8px auto;
}


.meeting-title-wrap .infobox-glass-title {
            if (playlistPopup) {
    top: 0;
                playlistPopup.style.display = 'none';
    margin-top: 0;
            }
    padding: 5px;
    line-height: 1.2;
}


.meeting-title-table {
            if (popup.style.display === 'none' || popup.style.display === '') {
    width: auto;
                popup.style.display = 'block';
    max-width: 100%;
                positionNotificationPopup();
    margin-left: auto;
                loadNotificationsIntoPopup();
    margin-right: auto;
            } else {
    border: 2px solid transparent;
                popup.style.display = 'none';
    border-collapse: collapse;
            }
    table-layout: auto;
        });
}


.meeting-title-table td,
    $(document)
.meeting-title-table th {
        .off('click.clbiNotificationOutside')
    background: none !important;
        .on('click.clbiNotificationOutside', function(e) {
    border: none !important;
            var popup = document.getElementById('clbi-notification-popup');
    vertical-align: middle !important;
            var toggle = document.getElementById('clbi-notification-toggle');
}
            if (!popup || !toggle) return;


.meeting-title-table .meeting-title-image-left {
            if (!popup.contains(e.target) && !toggle.contains(e.target)) {
    width: auto;
                popup.style.display = 'none';
    text-align: right !important;
            }
    padding: 0 6px 0 0 !important;
        });
    line-height: 0;
    white-space: nowrap;
}


.meeting-title-table .meeting-title-image-right {
    $(document)
    width: auto;
        .off('click.clbiNotificationReadAll')
    text-align: left !important;
        .on('click.clbiNotificationReadAll', '#clbi-notification-readall', function(e) {
    padding: 0 0 0 6px !important;
            e.preventDefault();
    line-height: 0;
            e.stopPropagation();
    white-space: nowrap;
}


.meeting-title-table .meeting-title-cell {
            var button = this;
    width: auto;
            button.disabled = true;
    line-height: 1.15 !important;
            button.textContent = '처리 중...';
    background: none !important;
    text-align: center !important;
    white-space: nowrap;
    padding: 0 !important;
}


.meeting-title-image-left img,
            markAllNotificationsRead()
.meeting-title-image-right img,
                .then(function() {
.meeting-title-image-left .mw-file-element,
                    loadNotificationsIntoPopup();
.meeting-title-image-right .mw-file-element {
                })
    display: block;
                .always(function() {
    border: 0 !important;
                    button.disabled = false;
    vertical-align: middle;
                    button.textContent = '전체 읽음';
    filter:
                });
         drop-shadow(0 1px 2px rgba(0,0,0,0.75))
         });
        drop-shadow(0 0 3px rgba(255,255,255,0.08));
}


.meeting-title-name {
    $(document)
    display: block;
        .off('click.clbiNotificationItem')
    line-height: 1.15;
        .on('click.clbiNotificationItem', '.clbi-notification-item', function(e) {
    text-align: center;
            e.preventDefault();
}
            e.stopPropagation();


.meeting-title-meta {
            var href = this.getAttribute('href');
    display: block;
            var notificationId = this.getAttribute('data-notification-id') || '';
    margin-top: 1px;
    color: #7F7F7F;
    font-size: 10px;
    font-weight: normal;
    line-height: 1.15;
    text-align: center;
}


.meeting-seatmap {
            markNotificationReadById(notificationId).always(function() {
    position: relative;
                loadNotificationsIntoPopup();
    margin: 0 auto;
                if (href) {
    padding: 5px 0 4px 0;
                    window.location.href = href;
    background: transparent;
                }
    border-radius: 0;
            });
    overflow: visible;
        });
}


.meeting-seatmap::before {
     $(window)
     content: "";
        .off('resize.clbiNotification')
    position: absolute;
         .on('resize.clbiNotification', function() {
    left: 50%;
             var popup = document.getElementById('clbi-notification-popup');
    top: 84px;
             if (popup && popup.style.display === 'block') {
    bottom: 145px;
                positionNotificationPopup();
    width: 2px;
             }
    transform: translateX(-50%);
         });
    background:
         linear-gradient(
            to bottom,
             transparent 0%,
             rgba(65,65,65,0.72) 18%,
            #414141 50%,
            rgba(65,65,65,0.72) 82%,
             transparent 100%
         );
    box-shadow: 0 0 6px rgba(65,65,65,0.16);
    pointer-events: none;
}
}
// ========== 알림 시스템 끝 ==========


.meeting-table-zone::before {
// ========== 플레이리스트 팝업 시스템 ==========
    content: "";
function ensurePlaylistPopup() {
    position: absolute;
     if (document.getElementById('clbi-playlist-popup')) return;
    left: 7%;
    right: 7%;
    top: 50%;
    height: 2px;
    transform: translateY(-50%);
     background:
        linear-gradient(
            to right,
            transparent 0%,
            rgba(65,65,65,0.72) 14%,
            #414141 50%,
            rgba(65,65,65,0.72) 86%,
            transparent 100%
        );
    box-shadow: 0 0 6px rgba(65,65,65,0.14);
    pointer-events: none;
}


.meeting-axis-label {
    var popup = document.createElement('div');
    position: relative;
     popup.id = 'clbi-playlist-popup';
     z-index: 4;
     popup.style.cssText =
     display: inline-block;
        'display:none;position:fixed;z-index:99999;width:690px;height:540px;' +
    min-width: 42px;
        'background:#0a0909;border:2px solid #854369;border-radius:10px;' +
    margin: 2px auto 4px auto;
        'box-shadow:0 0 0 1px #1a1a1a, 0 8px 24px rgba(0,0,0,0.55);overflow:hidden;';
    padding: 2px 9px;
    border-radius: 5px;
    background: #0c0c0c;
    color: #E4E4E4;
    font-weight: bold;
    line-height: 1.35;
    box-shadow:
        inset 0 -1px 0 #525252,
        0 0 0 3px #1A1A1A;
}


.meeting-chair-line {
    popup.innerHTML =
    position: relative;
        '<div style="padding:10px 12px;border-bottom:2px solid #854369;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);color:#E2E2E2;font-size:13px;font-weight:700;">' +
    z-index: 3;
            '<span>플레이리스트</span>' +
    display: flex;
        '</div>' +
    justify-content: center;
        '<div style="padding:0;background:#111;height:calc(100% - 42px);">' +
    margin: 0 0 6px 0;
            '<iframe style="border:none;width:100%;height:100%;" src="https://open.spotify.com/embed/playlist/32l4ke6djdQn8LoBp1ipR9?utm_source=generator&theme=0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>' +
    background: transparent;
        '</div>';
}


.meeting-table-zone {
    document.body.appendChild(popup);
    position: relative;
    z-index: 3;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 48px minmax(0, 1fr);
    align-items: center;
    width: 100%;
    max-width: 980px;
    min-height: 210px;
    margin: 0 auto;
    background: transparent;
}
}


.meeting-side {
function positionPlaylistPopup() {
     display: flex;
     var btn = document.getElementById('clbi-playlist-toggle');
    flex-wrap: nowrap;
     var popup = document.getElementById('clbi-playlist-popup');
    justify-content: center;
     if (!btn || !popup) return;
     align-items: center;
    gap: 0;
    min-width: 0;
     background: transparent;
}


.meeting-side-left {
    var rect = btn.getBoundingClientRect();
     padding-right: 4px;
    var top = rect.bottom + 2;
}
     var left = rect.right - popup.offsetWidth;


.meeting-side-right {
    if (left < 8) left = 8;
    padding-left: 4px;
    if (left + popup.offsetWidth > window.innerWidth - 8) {
}
        left = window.innerWidth - popup.offsetWidth - 8;
    }
    if (top + popup.offsetHeight > window.innerHeight - 8) {
        top = Math.max(8, window.innerHeight - popup.offsetHeight - 8);
    }


.meeting-center-spine {
    popup.style.top = top + 'px';
    position: relative;
     popup.style.left = left + 'px';
     display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    min-height: 210px;
    z-index: 4;
    background: transparent;
}
}


.meeting-center-spine .meeting-axis-label {
function initPlaylistPopup() {
     position: absolute;
     var btn = document.getElementById('clbi-playlist-toggle');
    top: 50%;
     if (!btn) return;
    left: 50%;
    transform: translate(-50%, -50%);
     writing-mode: vertical-rl;
    text-orientation: mixed;
    min-width: 0;
    min-height: 42px;
    margin: 0;
    padding: 8px 3px;
}


.meeting-advisory-zone {
     ensurePlaylistPopup();
     position: relative;
    z-index: 3;
    margin-top: 6px;
    background: transparent;
}


.meeting-advisory-zone > .meeting-axis-label {
    $(document)
    margin-bottom: 6px;
        .off('click.clbiPlaylistToggle')
}
        .on('click.clbiPlaylistToggle', '#clbi-playlist-toggle', function(e) {
            e.preventDefault();
            e.stopPropagation();


.meeting-advisory-grid {
            var popup = document.getElementById('clbi-playlist-popup');
    position: relative;
            var notificationPopup = document.getElementById('clbi-notification-popup');
    display: flex;
            if (!popup) return;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    gap: 0;
    width: 100%;
    max-width: 760px;
    margin: 0 auto;
    padding: 0;
    box-sizing: border-box;
    background: transparent;
}


.meeting-advisory-grid::before {
             if (notificationPopup) {
    content: "";
                notificationPopup.style.display = 'none';
    position: absolute;
            }
    left: 7%;
    right: 7%;
    top: 94px;
    height: 2px;
    background:
        linear-gradient(
            to right,
            transparent 0%,
             rgba(65,65,65,0.72) 14%,
            #414141 50%,
            rgba(65,65,65,0.72) 86%,
            transparent 100%
        );
    box-shadow: 0 0 6px rgba(65,65,65,0.14);
    pointer-events: none;
}


.meeting-person-card {
            if (popup.style.display === 'none' || popup.style.display === '') {
    position: relative;
                popup.style.display = 'block';
    z-index: 3;
                positionPlaylistPopup();
    background: transparent;
            } else {
    color: #fff;
                popup.style.display = 'none';
    text-align: center;
            }
    vertical-align: top;
        });
    padding: 0;
    box-sizing: border-box;
}


.meeting-chair-card {
    $(document)
    width: 180px;
        .off('click.clbiPlaylistOutside')
}
        .on('click.clbiPlaylistOutside', function(e) {
            var popup = document.getElementById('clbi-playlist-popup');
            var toggle = document.getElementById('clbi-playlist-toggle');
            if (!popup || !toggle) return;


.meeting-side .meeting-person-card {
            if (!popup.contains(e.target) && !toggle.contains(e.target)) {
    width: 50%;
                popup.style.display = 'none';
    min-width: 0;
            }
}
        });


.meeting-advisory-card {
    $(window)
    width: 33.333%;
        .off('resize.clbiPlaylist')
    min-width: 0;
        .on('resize.clbiPlaylist', function() {
            var popup = document.getElementById('clbi-playlist-popup');
            if (popup && popup.style.display === 'block') {
                positionPlaylistPopup();
            }
        });
}
}
// ========== 플레이리스트 팝업 시스템 끝 ==========


.meeting-person-inner {
function initUserProfilePage() {
     display: flex;
     var saveBtn = document.getElementById('pref-save');
    justify-content: center;
     if (!saveBtn) return;
     padding: 0;
    background: transparent;
}


.meeting-person-stack {
    var adminEl = document.getElementById('pref-is-admin');
    width: 90%;
     var isAdmin = adminEl && adminEl.getAttribute('data-admin') === '1';
     margin-top: 3px;
     var api = new mw.Api();
     background: transparent;
     var selectedFile = null;
     display: flex;
     var cropper = null;
     flex-direction: column;
    align-items: center;
}


.meeting-side .meeting-person-stack {
    if (!document.getElementById('clbi-gallery-modal')) {
    width: 94%;
        var gModal = document.createElement('div');
}
        gModal.id = 'clbi-gallery-modal';
        gModal.style.cssText =
            'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';


.meeting-advisory-card .meeting-person-stack {
        gModal.innerHTML =
    width: 92%;
            '<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:480px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
}
                '<div style="display:flex;justify-content:space-between;align-items:center;">' +
                    '<span style="font-size:14px;font-weight:700;color:#e2e2e2;">프로필 사진 선택</span>' +
                    '<button type="button" id="clbi-gallery-close" style="background:none;border:none;color:#aaa;font-size:18px;cursor:pointer;">✕</button>' +
                '</div>' +
                '<button type="button" id="clbi-gallery-upload-btn" style="background:#2a2a2a;border:2px dashed #854369;border-radius:8px;padding:32px;color:#e2e2e2;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:8px;font-size:13px;width:100%;">' +
                    '<span style="font-size:32px;">🖼️</span>새 사진 업로드' +
                '</button>' +
                '<div id="clbi-gallery-history-section" style="display:none;">' +
                    '<div style="font-size:11px;color:#888;margin-bottom:8px;">이전 사진 — 클릭하면 바로 적용</div>' +
                    '<div id="clbi-gallery-history" style="display:flex;gap:8px;flex-wrap:wrap;"></div>' +
                '</div>' +
            '</div>';


.meeting-person-stack .compact-crt-media {
        document.body.appendChild(gModal);
    margin-left: auto;
     }
     margin-right: auto;
    flex: 0 0 auto;
}


.meeting-person-stack .office-card-name-cell {
    if (!document.getElementById('clbi-crop-modal')) {
    width: 170px;
        var cModal = document.createElement('div');
    max-width: 100%;
        cModal.id = 'clbi-crop-modal';
    margin-top: 6px;
        cModal.style.cssText =
    margin-left: auto;
            'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';
    margin-right: auto;
    flex: 0 0 auto;
}


.meeting-chair-card .office-card-name-cell {
        cModal.innerHTML =
    width: 170px;
            '<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:500px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
}
                '<div style="font-size:14px;font-weight:700;color:#e2e2e2;">사진 조정</div>' +
                '<div style="width:100%;max-height:380px;overflow:hidden;border-radius:8px;">' +
                    '<img id="clbi-crop-image" style="max-width:100%;">' +
                '</div>' +
                '<div style="display:flex;gap:8px;justify-content:flex-end;">' +
                    '<button type="button" id="clbi-crop-cancel" style="background:#2a2a2a;color:#e2e2e2;border:1px solid #444;padding:8px 16px;border-radius:6px;cursor:pointer;">취소</button>' +
                    '<button type="button" id="clbi-crop-confirm" style="background:#854369;color:#fff;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;">확정</button>' +
                '</div>' +
            '</div>';


.meeting-advisory-card .office-card-name-cell {
        document.body.appendChild(cModal);
     width: 170px;
     }
}


.meeting-office {
    var gModal = document.getElementById('clbi-gallery-modal');
    display: block;
     var cModal = document.getElementById('clbi-crop-modal');
     margin-top: 2px;
     var cropImage = document.getElementById('clbi-crop-image');
     color: #7F7F7F;
     var pfpInput = document.getElementById('pref-pfp-input');
    font-size: 10px;
     line-height: 1.3;
    font-weight: normal;
}


/* =========================================
    function openGallery() {
  ScreenHeader
        gModal.style.display = 'flex';
  ========================================= */


.liberty-content:has(.screen-header) .liberty-content-header .title {
        var username = mw.config.get('wgUserName');
    display: none !important;
        api.get({
}
            action: 'query',
            titles: '파일:Pfp-' + username + '.png',
            prop: 'imageinfo',
            iiprop: 'url|timestamp',
            iilimit: 6
        }).then(function(data) {
            var pages = data.query.pages;
            var page = pages[Object.keys(pages)[0]];
            if (!page.imageinfo || page.imageinfo.length === 0) return;


.Liberty .content-wrapper .liberty-content:has(.screen-header) .liberty-content-main {
            var historyEl = document.getElementById('clbi-gallery-history');
    position: relative !important;
            var sectionEl = document.getElementById('clbi-gallery-history-section');
    box-sizing: border-box !important;
            historyEl.innerHTML = '';
    background: #171717 !important;
    border: 2px solid #854369 !important;
    border-bottom: none !important;
}


.liberty-content:has(.screen-header) .screen-title-plate,
            page.imageinfo.forEach(function(info, idx) {
.liberty-content:has(.screen-header) .screen-header-frame,
                var wrap = document.createElement('div');
.liberty-content:has(.screen-header) .screen-header-inner,
                wrap.style.cssText = 'position:relative;cursor:pointer;';
.liberty-content:has(.screen-header) .screen-header-media,
.liberty-content:has(.screen-header) .screen-header-vignette,
.liberty-content:has(.screen-header) .screen-header-scanline,
.liberty-content:has(.screen-header) .screen-header-mask,
.liberty-content:has(.screen-header) .screen-header-glass,
.liberty-content:has(.screen-header) .screen-header-subtitle {
    display: none !important;
}


.screen-header {
                var img = document.createElement('img');
    position: relative !important;
                img.src = info.url;
    width: calc(100% + 32px) !important;
                img.style.cssText =
    height: var(--screen-header-height, 360px) !important;
                    'width:72px;height:72px;object-fit:cover;border-radius:8px;border:2px solid #444;flex-shrink:0;';
    margin: -16px -16px 0 -16px !important;
    padding: 8px 8px 4px 8px !important;
    box-sizing: border-box !important;
    overflow: hidden !important;
    background: #171717 !important;
    border: 0 !important;
    border-bottom: 0 !important;
    isolation: isolate !important;
}


.screen-header-recess {
                if (idx === 0) {
    position: relative !important;
                    img.style.borderColor = '#854369';
    width: 100% !important;
                    var badge = document.createElement('div');
    height: 100% !important;
                    badge.textContent = '현재';
    margin: 0 !important;
                    badge.style.cssText =
    padding: 4px !important;
                        'position:absolute;bottom:4px;left:50%;transform:translateX(-50%);background:#854369;color:#fff;font-size:9px;padding:1px 6px;border-radius:10px;';
    box-sizing: border-box !important;
                    wrap.appendChild(badge);
    overflow: hidden !important;
                }
    background: #171717 !important;
    border: none !important;
    border-radius: 6px !important;
    box-shadow:
        inset 0 18px 30px rgba(0,0,0,0.55),
        inset 0 -9px 16px rgba(255,255,255,0.018),
        inset 18px 0 30px rgba(0,0,0,0.40),
        inset -18px 0 30px rgba(0,0,0,0.40) !important;
}


.screen-header-recess::before {
                img.addEventListener('mouseenter', function() {
    content: "";
                    if (idx !== 0) img.style.borderColor = '#854369';
    position: absolute;
                });
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0.10;
    background:
        repeating-linear-gradient(
            135deg,
            rgba(255,255,255,0.030) 0px,
            rgba(255,255,255,0.030) 1px,
            transparent 1px,
            transparent 5px
        );
    mix-blend-mode: screen;
}


.screen-header-recess::after {
                img.addEventListener('mouseleave', function() {
    content: "";
                    if (idx !== 0) img.style.borderColor = '#444';
    position: absolute;
                });
    inset: 14px;
    z-index: 12;
    pointer-events: none;
    border-radius: 0;
    box-shadow:
        0 -20px 32px rgba(0,0,0,0.86),
        0 12px 18px rgba(255,255,255,0.024),
        -20px 0 32px rgba(0,0,0,0.78),
        20px 0 32px rgba(0,0,0,0.78),
        inset 0 0 0 1px rgba(255,255,255,0.018) !important;
}


.screen-header .screen-header-crt {
                img.addEventListener('click', function() {
    position: relative !important;
                    fetch(info.url)
    z-index: 2 !important;
                        .then(function(r) {
    width: 100% !important;
                            return r.blob();
    height: 100% !important;
                        })
    aspect-ratio: auto !important;
                        .then(function(blob) {
    margin: 0 !important;
                            selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
    padding: 0 !important;
                            document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
    box-sizing: border-box !important;
                            gModal.style.display = 'none';
    overflow: hidden !important;
                            document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
    background: transparent !important;
                        });
    border: 0 !important;
                });
    border-radius: 0 !important;
    box-shadow: none !important;
}


.screen-header .screen-header-crt::before,
                wrap.appendChild(img);
.screen-header .screen-header-crt::after {
                historyEl.appendChild(wrap);
    content: none !important;
            });
}


.screen-header .crt-page-monitor-inner {
            sectionEl.style.display = 'block';
    position: relative !important;
         });
    width: 100% !important;
     }
    height: 100% !important;
    padding: 5px !important;
    margin: 0 !important;
    box-sizing: border-box !important;
    border-radius: 0 !important;
    background:
        linear-gradient(
            145deg,
            #111111 0%,
            #1a1a1a 46%,
            #111111 100%
         ) !important;
    border: none !important;
     box-shadow:
        inset 0 10px 20px rgba(0,0,0,0.70),
        inset 0 -2px 4px rgba(255,255,255,0.022),
        inset 8px 0 18px rgba(0,0,0,0.55),
        inset -8px 0 18px rgba(0,0,0,0.55) !important;
}


.screen-header .crt-page-monitor-screen {
     function openCrop(src) {
     position: relative !important;
         cropImage.src = src;
    width: 100% !important;
         cModal.style.display = 'flex';
    height: 100% !important;
    overflow: hidden !important;
    border-radius: 0 !important;
    background: #050505 !important;
    border: 1px solid #050505 !important;
    box-shadow:
        inset 0 0 0 1px #111,
        inset 0 0 24px rgba(0,0,0,0.94),
         inset 0 0 64px rgba(0,0,0,0.80),
         inset 0 0 118px rgba(0,0,0,0.50),
        0 0 0 1px rgba(255,255,255,0.030) !important;
}


.screen-header .crt-page-monitor-image,
        if (cropper) {
.screen-header .crt-page-monitor-image-base,
            cropper.destroy();
.screen-header .crt-page-monitor-image-bloom,
            cropper = null;
.screen-header .crt-page-monitor-slice {
        }
    position: absolute !important;
    inset: 0 !important;
    border-radius: 0 !important;
}


.screen-header .crt-page-monitor-image-base a,
        setTimeout(function() {
.screen-header .crt-page-monitor-image-base span,
            cropper = new Cropper(cropImage, {
.screen-header .crt-page-monitor-image-base .mw-file-description,
                aspectRatio: 1,
.screen-header .crt-page-monitor-image-base .mw-default-size,
                viewMode: 1,
.screen-header .crt-page-monitor-image-base .mw-image-border,
                dragMode: 'move',
.screen-header .crt-page-monitor-image-bloom a,
                autoCropArea: 0.8,
.screen-header .crt-page-monitor-image-bloom span,
                cropBoxResizable: true,
.screen-header .crt-page-monitor-image-bloom .mw-file-description,
                cropBoxMovable: true
.screen-header .crt-page-monitor-image-bloom .mw-default-size,
            });
.screen-header .crt-page-monitor-image-bloom .mw-image-border,
        }, 150);
.screen-header .crt-page-monitor-slice a,
     }
.screen-header .crt-page-monitor-slice span,
.screen-header .crt-page-monitor-slice .mw-file-description,
.screen-header .crt-page-monitor-slice .mw-default-size,
.screen-header .crt-page-monitor-slice .mw-image-border {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
     width: 100% !important;
    height: 100% !important;
}


.screen-header .crt-page-monitor-image-base img,
    document.getElementById('pref-pfp-btn').addEventListener('click', function() {
.screen-header .crt-page-monitor-image-base .mw-file-element,
        openGallery();
.screen-header .crt-page-monitor-image-bloom img,
     });
.screen-header .crt-page-monitor-image-bloom .mw-file-element,
.screen-header .crt-page-monitor-slice img,
.screen-header .crt-page-monitor-slice .mw-file-element,
.screen-header .crt-page-monitor-slice-img {
    position: absolute !important;
    inset: 0 !important;
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    object-fit: cover !important;
    object-position: var(--screen-image-position, center center) !important;
     border: 0 !important;
    border-radius: 0 !important;
}


.screen-header .crt-page-monitor-screen::before {
    document.getElementById('clbi-gallery-upload-btn').addEventListener('click', function() {
    content: "" !important;
         pfpInput.click();
    position: absolute !important;
     });
    inset: -4px !important;
    z-index: 20 !important;
    pointer-events: none !important;
    border-radius: 0 !important;
    background:
        radial-gradient(
            ellipse at 50% 50%,
            transparent 0%,
            transparent 62%,
            rgba(0,0,0,0.18) 78%,
            rgba(0,0,0,0.48) 100%
        ),
        linear-gradient(
            to bottom,
            rgba(0,0,0,0.20) 0%,
            transparent 14%,
            transparent 82%,
            rgba(0,0,0,0.28) 100%
        ),
         linear-gradient(
            to right,
            rgba(0,0,0,0.20) 0%,
            transparent 8%,
            transparent 92%,
            rgba(0,0,0,0.20) 100%
        ),
        linear-gradient(
            105deg,
            transparent 0%,
            rgba(255,255,255,0.030) 18%,
            transparent 38%,
            transparent 100%
        ) !important;
     box-shadow:
        inset 0 0 0 1px rgba(255,255,255,0.018),
        inset 0 0 24px rgba(255,255,255,0.015),
        inset 0 0 74px rgba(0,0,0,0.64),
        inset 0 22px 38px rgba(0,0,0,0.60),
        inset 0 -20px 36px rgba(0,0,0,0.44),
        inset 18px 0 36px rgba(0,0,0,0.56),
        inset -18px 0 36px rgba(0,0,0,0.56) !important;
}


.screen-header .crt-page-monitor-screen::after {
    document.getElementById('clbi-gallery-close').addEventListener('click', function() {
    content: "" !important;
        gModal.style.display = 'none';
    position: absolute !important;
     });
    inset: -80px -4px !important;
    z-index: 21 !important;
    pointer-events: none !important;
    opacity: 0.22 !important;
    border-radius: 0 !important;
    background:
        repeating-linear-gradient(
            to bottom,
            rgba(255,255,255,0.08) 0px,
            rgba(255,255,255,0.08) 1px,
            transparent 2px,
            transparent 5px
        ) !important;
     mix-blend-mode: screen !important;
    animation: crt-scanlines-up 7s linear infinite !important;
}


.screen-header .crt-page-monitor-glitch,
    pfpInput.addEventListener('change', function() {
.screen-header .crt-page-monitor-tear {
        var file = this.files[0];
    inset: -4px !important;
        if (!file) return;
    border-radius: 0 !important;
}


.screen-header .crt-page-monitor-glitch {
        gModal.style.display = 'none';
    z-index: 22 !important;
}


.screen-header .crt-page-monitor-tear {
        var reader = new FileReader();
    z-index: 23 !important;
        reader.onload = function(e) {
}
            openCrop(e.target.result);
        };
        reader.readAsDataURL(file);
    });


.screen-header-title {
    document.getElementById('clbi-crop-cancel').addEventListener('click', function() {
    position: absolute !important;
        cModal.style.display = 'none';
    left: 5px !important;
        if (cropper) {
    bottom: 5px !important;
            cropper.destroy();
    z-index: 40 !important;
            cropper = null;
    margin: 0 !important;
        }
    padding: 0 !important;
        pfpInput.value = '';
    max-width: calc(100% - 10px) !important;
     });
    box-sizing: border-box !important;
    background: transparent !important;
    border: 0 !important;
     box-shadow: none !important;
}


.screen-header-title-main {
    document.getElementById('clbi-crop-confirm').addEventListener('click', function() {
    margin: 0 !important;
         if (!cropper) return;
    padding: 0 0 8px 10px !important;
    border: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
    color: #ffffff !important;
    font-family: 'BoldRound', sans-serif !important;
    font-size: 40px !important;
    font-weight: 700 !important;
    line-height: 0.95 !important;
    letter-spacing: 0.06em !important;
    text-shadow:
        3px 3px 0 rgba(0,0,0,0.98),
        0 0 6px rgba(0,0,0,0.98),
        0 0 14px rgba(0,0,0,0.95),
        0 0 26px rgba(0,0,0,0.92),
         -1px 0 0 rgba(80,160,255,0.24),
        1px 0 0 rgba(255,55,90,0.22) !important;
    white-space: nowrap !important;
}


.screen-header-subtitle {
        var canvas = cropper.getCroppedCanvas({ width: 256, height: 256 });
    display: none !important;
        if (!canvas) return;
}


.screen-header + .screen-body {
        canvas.toBlob(function(blob) {
    border-top: 0 !important;
            selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
    margin-top: 0 !important;
            document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
}
            cModal.style.display = 'none';
            cropper.destroy();
            cropper = null;
            document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
        }, 'image/png');
    });


/* =========================================
    var emailSaveBtn = document.getElementById('pref-email-save');
  Document View System — doc-*
    if (emailSaveBtn) {
  ========================================= */
        emailSaveBtn.addEventListener('click', function() {
            var statusEl = document.getElementById('pref-email-status');
            var newEmail = document.getElementById('pref-new-email').value;
            var password = document.getElementById('pref-email-password').value;


.screen-body {
            if (!newEmail || !password) {
    position: relative;
                statusEl.textContent = '이메일과 비밀번호를 입력해주세요.';
    width: calc(100% + 32px);
                return;
    margin: 0 -16px 0 -16px;
            }
    padding: 4px 8px 4px 8px;
    box-sizing: border-box;
    background: #171717;
    box-shadow:
        inset 1px 0 0 #494949,
        inset 0 2px 8px rgba(0,0,0,0.60);
    border-top: 0;
    overflow: visible;
}


.doc-body {
            statusEl.textContent = '변경 중...';
    position: relative;
    z-index: 3;
    box-sizing: border-box;
    width: 100%;
    padding: 0;
    background: transparent;
}


/* =========================================
            api.postWithToken('csrf', {
  doc-header
                action: 'changeemail',
  ========================================= */
                email: newEmail,
                password: password
            }).then(function() {
                statusEl.textContent = '✓ 이메일 변경됨';
                document.getElementById('pref-new-email').value = '';
                document.getElementById('pref-email-password').value = '';


.doc-header {
                setTimeout(function() {
    position: relative;
                    statusEl.textContent = '';
    display: grid;
                }, 3000);
    grid-template-columns: 24px 1fr auto 24px;
            }).fail(function(code, data) {
    align-items: center;
                var msg = data && data.error && data.error.info ? data.error.info : '변경 실패';
    height: 28px;
                statusEl.textContent = msg;
    margin-bottom: 7px;
            });
    padding: 0 8px;
        });
    box-sizing: border-box;
     }
    background:
        linear-gradient(
            to bottom,
            #282828 0%,
            #1a1a1a 40%,
            #161616 100%
        );
    border-top: 1px solid #3a3a3a;
    border-bottom: 1px solid #0a0a0a;
    border-left: 1px solid #222;
    border-right: 1px solid #222;
     border-radius: 2px;
}


.doc-header::before {
     saveBtn.addEventListener('click', function() {
    content: "";
         var statusEl = document.getElementById('pref-status');
    position: absolute;
         statusEl.textContent = '저장 중...';
    inset: 0;
    pointer-events: none;
     opacity: 0.06;
    background: repeating-linear-gradient(
        135deg,
        rgba(255,255,255,0.08) 0px,
         rgba(255,255,255,0.08) 1px,
         transparent 1px,
        transparent 4px
    );
    border-radius: inherit;
}


.doc-header-id {
        var promises = [];
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.18em;
    color: #c0c0c0;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
    position: relative;
    z-index: 1;
}


.doc-screw {
        if (selectedFile) {
    position: relative;
            var username = mw.config.get('wgUserName');
    z-index: 1;
            promises.push(
    width: 10px;
                api.postWithToken('csrf', {
    height: 10px;
                    action: 'upload',
    border-radius: 50%;
                    filename: 'Pfp-' + username + '.png',
    background:
                    ignorewarnings: true,
        radial-gradient(circle at 36% 32%, rgba(255,255,255,0.35) 0%, transparent 28%),
                    file: selectedFile,
        linear-gradient(145deg, #505050 0%, #282828 55%, #0e0e0e 100%);
                    format: 'json'
    border: 1px solid #090909;
                }, {
    box-shadow:
                    contentType: 'multipart/form-data'
        inset 0 1px 0 rgba(255,255,255,0.15),
                })
        inset 0 -1px 0 rgba(0,0,0,0.60),
            );
        0 0 0 1px #333;
        }
    justify-self: center;
}


.doc-screw::after {
        var fields = ['name', 'discord', 'role', 'bio'];
    content: "";
        if (isAdmin) fields.push('badges');
    position: absolute;
    top: 50%;
    left: 15%;
    right: 15%;
    height: 1px;
    transform: translateY(-50%);
    background: rgba(0,0,0,0.55);
    border-radius: 1px;
}


.doc-status {
        for (var i = 0; i < fields.length; i++) {
    display: flex;
            var el = document.getElementById('pref-' + fields[i]);
    align-items: center;
            if (!el) continue;
    gap: 5px;
    position: relative;
    z-index: 1;
}


.doc-status-lamp {
            promises.push(
    display: inline-block;
                api.postWithToken('csrf', {
    width: 8px;
                    action: 'options',
    height: 8px;
                    optionname: 'profile-' + fields[i],
    border-radius: 50%;
                    optionvalue: el.value
    background: #111;
                })
    border: 1px solid #060606;
            );
    box-shadow:
         }
        inset 0 1px 0 rgba(255,255,255,0.06),
         0 0 0 1px #252525;
}


.doc-status-lamp.on {
        $.when.apply($, promises)
    background: radial-gradient(circle at 38% 32%, #d9ff9d 0%, #7ec43e 45%, #253d18 100%);
            .then(function() {
    box-shadow:
                statusEl.textContent = '✓ 저장됨';
        inset 0 1px 0 rgba(255,255,255,0.28),
                selectedFile = null;
        0 0 0 1px #1a3010,
                document.getElementById('pref-pfp-btn').textContent = '사진 선택';
        0 0 5px rgba(100,200,50,0.45);
}


.doc-status-lamp.warn {
                setTimeout(function() {
    background: radial-gradient(circle at 38% 32%, #ffe8b0 0%, #d89a42 45%, #4d2a0d 100%);
                    statusEl.textContent = '';
    box-shadow:
                }, 2000);
        inset 0 1px 0 rgba(255,255,255,0.28),
            })
        0 0 0 1px #3a1f06,
            .fail(function() {
        0 0 5px rgba(220,140,40,0.45);
                statusEl.textContent = '저장 실패';
            });
    });
}
}


/* =========================================
/* =========================================
   doc-layout
   Banner / CRT Page Monitor thumbnail slices
  - base 이미지는 틀에 들어간 파일 문법 그대로 사용
  - slice 레이어에는 300px MediaWiki 썸네일만 삽입
   ========================================= */
   ========================================= */


.doc-layout {
(function ($, mw) {
     display: grid;
     var thumbCache = {};
     grid-template-columns: 200px minmax(0, 1fr) 320px;
 
    gap: 7px;
     function parseSliceWidth(value) {
     align-items: stretch;
        var parsed = parseInt(value, 10);
     min-height: 480px;
 
}
        if (!isFinite(parsed) || parsed < 120) {
            return 300;
        }
 
        return parsed;
     }
 
     function getImageSrc(img) {
        return img ? (img.currentSrc || img.getAttribute('src') || img.src || '') : '';
    }


.doc-layout-left,
    function getFileNameFromSrc(src) {
.doc-layout-center,
        var a;
.doc-layout-right {
        var parts;
    min-width: 0;
        var fileName;
}


.doc-layout-center,
        if (!src) return '';
.doc-layout-right {
    display: flex;
    flex-direction: column;
    gap: 7px;
}


.doc-layout-lower {
        a = document.createElement('a');
    display: grid;
        a.href = src;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 7px;
    flex: 1;
}


/* =========================================
        parts = (a.pathname || '').split('/').filter(function (part) {
  doc-panel
            return !!part;
  ========================================= */
        });


.doc-panel {
        if (!parts.length) return '';
    position: relative;
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    background: #1a1a1a;
    border-top: 1px solid #303030;
    border-bottom: 1px solid #080808;
    border-left: 1px solid #222;
    border-right: 1px solid #111;
    border-radius: 2px;
    overflow: hidden;
}


.doc-panel::before {
         fileName = parts.pop();
    content: "";
    position: absolute;
    top: 4px;
    left: 4px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background:
         radial-gradient(circle at 35% 30%, rgba(255,255,255,0.25) 0%, transparent 40%),
        linear-gradient(145deg, #444 0%, #1e1e1e 100%);
    border: 1px solid #080808;
    box-shadow: 0 0 0 1px #2a2a2a;
    pointer-events: none;
    z-index: 10;
}


.doc-panel::after {
        /*
    content: "";
        * MediaWiki thumb URL 예시:
    position: absolute;
        * /images/thumb/a/ab/File.png/1000px-File.png
    top: 4px;
        * /images/thumb/a/ab/File.svg/1000px-File.svg.png
    right: 4px;
        *
    width: 5px;
        * 이 경우 실제 파일명은 마지막 조각이 아니라 그 앞 조각이다.
    height: 5px;
        */
    border-radius: 50%;
         if (/^\d+px-/.test(fileName) && parts.length) {
    background:
            fileName = parts.pop();
         radial-gradient(circle at 35% 30%, rgba(255,255,255,0.25) 0%, transparent 40%),
        }
        linear-gradient(145deg, #444 0%, #1e1e1e 100%);
    border: 1px solid #080808;
    box-shadow: 0 0 0 1px #2a2a2a;
    pointer-events: none;
    z-index: 10;
}


.doc-panel-title {
        fileName = fileName.replace(/^\d+px-/, '');
    position: relative;
    z-index: 3;
    box-sizing: border-box;
    padding: 6px 10px 5px 10px;
    font-family: 'Galmuri11', sans-serif !important;
    font-size: 10px;
    font-weight: 700;
    text-align: center;
    line-height: 1.2;
    letter-spacing: 0.10em;
    color: #888;
    background: linear-gradient(to bottom, #161616 0%, #121212 100%);
    border-bottom: 1px solid #0d0d0d;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
    text-transform: uppercase;
}


.doc-panel-heading {
        try {
    padding: 10px 18px;
            fileName = decodeURIComponent(fileName);
    font-size: 11px;
        } catch (e) {}
    font-weight: 700;
    color: #7a7a7a;
    letter-spacing: 0.06em;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
    position: relative;
    z-index: 3;
}


.doc-panel table {
        return fileName;
    width: 100%;
     }
     border-collapse: collapse;
    margin: 0;
}


.doc-panel table th,
    function resolveThumbUrl(img, width, callback) {
.doc-panel table td {
        var src = getImageSrc(img);
    background: transparent !important;
        var fileName = getFileNameFromSrc(src);
    border: none !important;
        var cacheKey;
    padding: 0 !important;
        var entry;
    vertical-align: middle !important;
}


.doc-panel table th[style],
        if (!src) return;
.doc-panel table td[style] {
    background: revert !important;
    border: revert !important;
    padding: revert !important;
}


/* =========================================
        if (!fileName || !mw || !mw.loader) {
  doc-display
            callback(src);
  ========================================= */
            return;
        }


.doc-display {
        cacheKey = fileName + '|' + width;
    position: relative;
         entry = thumbCache[cacheKey];
    z-index: 3;
    box-sizing: border-box;
    padding: 4px 14px;
    margin-bottom: 0;
    min-height: 90px;
    line-height: 1.8;
    color: #d8d8d8;
    background: #111111;
    border-top: 1px solid #0a0a0a;
    box-shadow:
        inset 0 2px 6px rgba(0,0,0,0.55),
         inset 0 0 1px rgba(0,0,0,0.80);
}


/* =========================================
        if (entry) {
  doc-info
            if (entry.resolved) {
  ========================================= */
                callback(entry.url || src);
            } else {
                entry.callbacks.push(callback);
            }
            return;
        }


.doc-info-row {
        entry = {
    position: relative;
            resolved: false,
    z-index: 3;
            url: '',
    display: grid;
            callbacks: [callback]
    grid-template-columns: 68px minmax(0, 1fr);
        };
    gap: 8px;
    align-items: center;
    margin: 7px 10px 0 10px;
}


.doc-info-key {
        thumbCache[cacheKey] = entry;
    font-size: 10px;
    font-weight: 700;
    color: #7a7a7a;
    letter-spacing: 0.06em;
    text-shadow: 0 1px 0 rgba(0,0,0,0.70);
}


.doc-info-value {
         function finish(url) {
    position: relative;
            var callbacks = entry.callbacks.slice();
    overflow: hidden;
            var i;
    text-align: right;
    border-radius: 2px;
    background: #0e0e0e;
    color: #d8d8d8;
    padding: 4px 9px;
    font-size: 11px;
    font-weight: 700;
    box-sizing: border-box;
    border-top: 1px solid #0a0a0a;
    border-bottom: 1px solid #252525;
    border-left: 1px solid #111;
    border-right: 1px solid #111;
    box-shadow:
         inset 0 2px 4px rgba(0,0,0,0.60),
        inset 1px 0 0 rgba(255,255,255,0.015);
}


.doc-info-value::after {
            entry.resolved = true;
    content: "";
            entry.url = url || src;
    position: absolute;
            entry.callbacks = [];
    inset: 0;
    z-index: 2;
    pointer-events: none;
    opacity: 0.15;
    background: repeating-linear-gradient(
        to bottom,
        rgba(255,255,255,0.055) 0px,
        rgba(255,255,255,0.055) 1px,
        transparent 2px,
        transparent 4px
    );
    mix-blend-mode: screen;
}


.doc-info-block {
            for (i = 0; i < callbacks.length; i++) {
    padding: 0 8px 6px 8px;
                callbacks[i](entry.url);
    background: #1a1a1a;
            }
}
        }


.doc-info-block-label {
        mw.loader.using('mediawiki.api').done(function () {
    display: block;
            var api = new mw.Api();
    padding: 6px 2px 4px 2px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #9a9a9a;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
}


/* =========================================
            api.get({
  doc-nav
                action: 'query',
  ========================================= */
                titles: 'File:' + fileName,
                prop: 'imageinfo',
                iiprop: 'url',
                iiurlwidth: width,
                formatversion: 2
            }).done(function (data) {
                var page;
                var info;


.doc-nav {
                if (
    height: 100%;
                    data &&
    display: flex;
                    data.query &&
    flex-direction: column;
                    data.query.pages &&
}
                    data.query.pages.length
                ) {
                    page = data.query.pages[0];


.doc-nav-item {
                    if (
    position: relative;
                        page &&
    z-index: 3;
                        page.imageinfo &&
    box-sizing: border-box;
                        page.imageinfo.length
    width: calc(100% - 16px);
                    ) {
    margin: 5px 8px 0 8px;
                        info = page.imageinfo[0];
    padding: 7px 24px 7px 10px;
                    }
    font-size: 11px;
                }
    font-weight: 700;
    line-height: 1.35;
    color: #666;
    background: linear-gradient(to bottom, #232323 0%, #1a1a1a 50%, #161616 100%);
    border-top: 1px solid #2e2e2e;
    border-bottom: 1px solid #0a0a0a;
    border-left: 1px solid #222;
    border-right: 1px solid #111;
    border-radius: 2px;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.025),
        0 1px 0 rgba(255,255,255,0.012);
    cursor: pointer;
    display: block;
    text-decoration: none;
    text-shadow: 0 1px 0 rgba(0,0,0,0.70);
}


.doc-nav-item::after {
                finish((info && (info.thumburl || info.url)) || src);
    content: "›";
            }).fail(function () {
    position: absolute;
                finish(src);
    right: 8px;
            });
    top: 50%;
        }).fail(function () {
    transform: translateY(-50%);
            finish(src);
    color: #333;
        });
     font-size: 13px;
     }
}


.doc-nav-item.active {
     function applySliceImages(frame, thumbUrl) {
    color: #e8e8e8;
        var slices;
     background: linear-gradient(to bottom, #5a2040 0%, #3d1a2f 52%, #2e1222 100%);
        var i;
    border-top-color: #7a3055;
         var img;
    border-bottom-color: #1a0810;
    border-left-color: #6a2848;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.06),
         0 0 6px rgba(212,90,162,0.10);
}


.doc-nav-item.active::after {
        if (!frame || !thumbUrl) return;
    color: #d45aa2;
}


/* =========================================
        slices = frame.querySelectorAll('.crt-page-monitor-slice');
  doc-status-row
  ========================================= */


.doc-status-row {
        for (i = 0; i < slices.length; i++) {
    position: relative;
            slices[i].innerHTML = '';
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 5px 8px 0 8px;
    padding: 5px 9px;
    box-sizing: border-box;
    font-size: 10px;
    font-weight: 700;
    color: #555;
    background: #111;
    border-top: 1px solid #0a0a0a;
    border-bottom: 1px solid #222;
    border-left: 1px solid #111;
    border-right: 1px solid #111;
    border-radius: 2px;
    box-shadow: inset 0 2px 4px rgba(0,0,0,0.50);
    letter-spacing: 0.06em;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
}


/* =========================================
            img = document.createElement('img');
  doc-link
            img.className = 'crt-page-monitor-slice-img';
  ========================================= */
            img.src = thumbUrl;
            img.alt = '';
            img.decoding = 'async';
            img.loading = 'eager';
            img.setAttribute('aria-hidden', 'true');


.doc-link {
            slices[i].appendChild(img);
    position: relative;
        }
    z-index: 3;
    box-sizing: border-box;
    margin: 5px 10px 0 10px;
    padding: 6px 10px 6px 14px;
    font-size: 11px;
    font-weight: 700;
    color: #888;
    background: linear-gradient(to bottom, #1a1a1a 0%, #151515 100%);
    border-top: 1px solid #242424;
    border-bottom: 1px solid #080808;
    border-left: 1px solid #1e1e1e;
    border-right: 1px solid #111;
    border-radius: 2px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.018);
    display: block;
    text-decoration: none;
    cursor: pointer;
    text-shadow: 0 1px 0 rgba(0,0,0,0.70);
}


.doc-link::before {
        frame.setAttribute('data-crt-slices-ready', '1');
    content: "";
     }
     position: absolute;
    left: 5px;
    top: 20%;
    bottom: 20%;
    width: 2px;
    background: #2e2e2e;
    border-radius: 1px;
}


.doc-link:hover {
     function initBannerFrame(frame) {
     color: #c0c0c0;
        var baseImg;
    background: linear-gradient(to bottom, #222 0%, #1a1a1a 100%);
        var width;
    border-top-color: #333;
}


.doc-link:hover::before {
        if (!frame) return;
    background: #854369;
        if (frame.getAttribute('data-crt-slices-ready') === '1') return;
}


/* =========================================
        baseImg = frame.querySelector('.crt-page-monitor-image-base img');
  doc-tab-bar
  ========================================= */


.doc-tab-bar {
        if (!baseImg) return;
    position: relative;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 5px 6px;
    background: linear-gradient(to bottom, #141414 0%, #111 100%);
    border-top: 1px solid #0a0a0a;
    box-shadow: inset 0 1px 0 rgba(0,0,0,0.60);
}


.doc-tab-key {
        width = parseSliceWidth(frame.getAttribute('data-crt-slice-width'));
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    gap: 3px;
    padding: 0 4px;
    user-select: none;
    white-space: nowrap;
}


.doc-tab-key kbd {
        resolveThumbUrl(baseImg, width, function (thumbUrl) {
    display: inline-flex;
            if (!frame || !frame.parentNode) return;
    align-items: center;
            applySliceImages(frame, thumbUrl);
    justify-content: center;
        });
    width: 20px;
    }
    height: 20px;
    font-size: 10px;
    font-weight: 700;
    font-family: inherit;
    color: #e2e2e2;
    background: linear-gradient(to bottom, #2a2a2a 0%, #1e1e1e 100%);
    border-top: 1px solid #3a3a3a;
    border-bottom: 1px solid #080808;
    border-left: 1px solid #2e2e2e;
    border-right: 1px solid #111;
    border-radius: 2px;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.06),
        0 1px 0 rgba(255,255,255,0.02);
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
}


.doc-tab-key .doc-tab-arrow {
    function initBannerFrames(root) {
    font-size: 10px;
        var scope = root && root.querySelectorAll ? root : document;
    color: #333;
        var frames = scope.querySelectorAll('.crt-page-monitor-frame');
    line-height: 1;
        var i;
}


.doc-tab {
        for (i = 0; i < frames.length; i++) {
    flex: 1;
            initBannerFrame(frames[i]);
    box-sizing: border-box;
        }
    padding: 6px 4px;
     }
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: #e2e2e2;
    background: linear-gradient(to bottom, #1e1e1e 0%, #181818 100%);
    border-top: 1px solid #2a2a2a;
    border-bottom: 1px solid #080808;
    border-left: 1px solid #222;
    border-right: 1px solid #111;
    border-radius: 2px;
    cursor: pointer;
    text-align: center;
    text-shadow: 0 1px 0 rgba(0,0,0,0.80);
    text-transform: uppercase;
    user-select: none;
    white-space: nowrap;
     overflow: hidden;
    text-overflow: ellipsis;
}


.doc-tab:hover {
    $(function () {
    color: #ffffff;
        initBannerFrames(document);
    background: linear-gradient(to bottom, #242424 0%, #1a1a1a 100%);
     });
     border-top-color: #333;
}


.doc-tab.active {
    if (mw && mw.hook) {
    color: #ffffff;
        mw.hook('wikipage.content').add(function ($content) {
    background: linear-gradient(to bottom, #2e1222 0%, #3d1a2f 52%, #5a2040 100%);
            initBannerFrames($content && $content[0] ? $content[0] : document);
    border-top-color: #7a3055;
        });
     border-bottom-color: #1a0810;
     }
    border-left-color: #6a2848;
})(jQuery, window.mw);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
}


/* =========================================
/* =========================================
   doc-footer
   Doc Tab System — Q/E 단축키 탭 전환
  글리치 플리커 + RGB split + 방향 슬라이드
   ========================================= */
   ========================================= */


.doc-footer {
(function () {
     position: relative;
     'use strict';
    display: grid;
 
    grid-template-columns: 1fr 110px 44px 110px 200px;
var keydownBound = false;
    gap: 12px;
 
     align-items: center;
     function initDocTabs() {
    height: 44px;
        var tabBars = document.querySelectorAll('.doc-tab-bar');
    margin-top: 7px;
        if (!tabBars.length) return;
    padding: 0 10px;
 
    box-sizing: border-box;
         tabBars.forEach(function (bar) {
    background: linear-gradient(to bottom, #282828 0%, #1a1a1a 40%, #161616 100%);
            if (bar.getAttribute('data-tabs-init')) return;
    box-shadow:
            bar.setAttribute('data-tabs-init', '1');
        inset 1px 0 0 #494949,
 
         inset 0 -1px 0 #555,
            var tabs = Array.from(bar.querySelectorAll('.doc-tab'));
        inset 0 2px 6px rgba(0,0,0,0.55);
            if (!tabs.length) return;
    border-top: 1px solid #3a3a3a;
    border-bottom: 1px solid #0a0a0a;
    border-left: 1px solid #222;
    border-right: 1px solid #222;
    border-radius: 2px;
}


.doc-footer::before {
            var panel = bar.closest('.doc-panel');
    content: "";
            var display = panel ? panel.querySelector('.doc-display') : null;
    position: absolute;
            if (!display) display = document.getElementById('doc-main-display');
    inset: 0;
            if (!display) return;
    pointer-events: none;
    opacity: 0.05;
    background: repeating-linear-gradient(
        135deg,
        rgba(255,255,255,0.08) 0px,
        rgba(255,255,255,0.08) 1px,
        transparent 1px,
        transparent 4px
    );
}


.doc-footer-sliders {
            tabs.forEach(function (tab, i) {
    display: flex;
                tab.addEventListener('click', function () {
    align-items: center;
                    var currentIdx = tabs.findIndex(function (t) {
    gap: 10px;
                        return t.classList.contains('active');
}
                    });
                    if (currentIdx === i) return;
                    switchTab(tabs, display, i, i > currentIdx ? 1 : -1);
                });
            });


.doc-slider {
            var initIdx = tabs.findIndex(function (t) { return t.classList.contains('active'); });
    position: relative;
            if (initIdx !== -1) {
    width: 80px;
                var initRef = tabs[initIdx].dataset.ref;
    height: 6px;
                var initEl = initRef ? document.getElementById(initRef) : null;
    background: #0a0a0a;
                display.innerHTML = initEl ? initEl.innerHTML : (tabs[initIdx].dataset.content || '');
    border-top: 1px solid #080808;
            }
    border-bottom: 1px solid #222;
        });
    border-radius: 99px;
    box-shadow: inset 0 1px 3px rgba(0,0,0,0.70);
}


.doc-slider.short {
        if (!keydownBound) {
    width: 54px;
            keydownBound = true;
}
            document.addEventListener('keydown', function (e) {
                var tag = document.activeElement.tagName;
                if (tag === 'INPUT' || tag === 'TEXTAREA') return;


.doc-slider::after {
                var bar = document.querySelector('.doc-tab-bar');
    content: "";
                if (!bar) return;
    position: absolute;
                var tabs = Array.from(bar.querySelectorAll('.doc-tab'));
    top: -6px;
    left: 48%;
    width: 10px;
    height: 18px;
    border-radius: 2px;
    background: linear-gradient(to bottom, #c8c0b2 0%, #8a8278 100%);
    border-top: 1px solid #d8d0c2;
    border-bottom: 1px solid #2a2622;
    border-left: 1px solid #aaa49c;
    border-right: 1px solid #5a544e;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.30),
        0 1px 3px rgba(0,0,0,0.40);
}


.doc-date {
                var panel = bar.closest('.doc-panel');
    box-sizing: border-box;
                var display = panel ? panel.querySelector('.doc-display') : null;
    height: 26px;
                if (!display) display = document.getElementById('doc-main-display');
    display: flex;
                if (!display) return;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.12em;
    color: #d45aa2;
    background: #0a0a0a;
    border-top: 1px solid #080808;
    border-bottom: 1px solid #2a1a22;
    border-left: 1px solid #0e0e0e;
    border-right: 1px solid #0e0e0e;
    border-radius: 2px;
    box-shadow: inset 0 2px 5px rgba(0,0,0,0.70);
    text-shadow: 0 0 8px rgba(212,90,162,0.40);
}


.doc-knob {
                var activeIdx = tabs.findIndex(function (t) {
    justify-self: center;
                    return t.classList.contains('active');
    width: 32px;
                });
    height: 32px;
                if (activeIdx === -1) return;
    border-radius: 50%;
    background:
        radial-gradient(circle at 36% 28%, rgba(255,255,255,0.30) 0%, transparent 24%),
        linear-gradient(145deg, #c8bfb0 0%, #8a8278 48%, #464039 100%);
    border: 1px solid #2a2520;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.30),
        inset 0 -2px 0 rgba(0,0,0,0.22),
        0 2px 5px rgba(0,0,0,0.50);
}


.doc-keys {
                if (e.key === 'q' || e.key === 'Q') {
    display: grid;
                    e.preventDefault();
    grid-template-columns: repeat(5, 1fr);
                    var prev = (activeIdx - 1 + tabs.length) % tabs.length;
    gap: 5px;
                    if (prev !== activeIdx) switchTab(tabs, display, prev, -1);
}
                } else if (e.key === 'e' || e.key === 'E') {
                    e.preventDefault();
                    var next = (activeIdx + 1) % tabs.length;
                    if (next !== activeIdx) switchTab(tabs, display, next, 1);
                }
            });
        }
    }


.doc-keys span {
     var isAnimating = false;
     height: 22px;
    border-radius: 2px;
    background: linear-gradient(to bottom, #d8d4ca 0%, #a09890 55%, #6e6660 100%);
    border-top: 1px solid #e0dcd2;
    border-bottom: 1px solid #1e1c18;
    border-left: 1px solid #b8b0a8;
    border-right: 1px solid #4a4440;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.40),
        0 1px 2px rgba(0,0,0,0.30);
}


.doc-keys span.red {
     function switchTab(tabs, display, nextIdx, dir) {
     background: linear-gradient(to bottom, #cc6058 0%, #922e28 55%, #521410 100%);
        if (isAnimating) return;
    border-top-color: #d87070;
        isAnimating = true;
    border-bottom-color: #1e0808;
}


.doc-keys span.amber {
        tabs.forEach(function (t) { t.classList.remove('active'); });
    background: linear-gradient(to bottom, #e4bc6c 0%, #b87c28 55%, #6c3e08 100%);
        tabs[nextIdx].classList.add('active');
    border-top-color: #ecd080;
    border-bottom-color: #201008;
}


/* =========================================
        var ref = tabs[nextIdx].dataset.ref;
  Responsive
        var nextContent;
  ========================================= */
        if (ref) {
            var refEl = document.getElementById(ref);
            nextContent = refEl ? refEl.innerHTML : '';
        } else {
            nextContent = tabs[nextIdx].dataset.content || '';
        }


@media screen and (max-width: 1280px) {
        glitchOut(display, dir, function () {
    .doc-layout {
            display.innerHTML = nextContent;
         grid-template-columns: 210px minmax(0, 1fr);
            glitchIn(display, dir, function () {
                isAnimating = false;
            });
         });
     }
     }


     .doc-layout-right {
     function glitchOut(el, dir, cb) {
         grid-column: 1 / -1;
         var duration = 160;
        display: grid;
        var start = null;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        var slideX = dir * 16;
         gap: 7px;
 
        function step(ts) {
            if (!start) start = ts;
            var p = Math.min((ts - start) / duration, 1);
            var ease = p * p;
 
            var tx = slideX * ease;
            var skew = dir * ease * 1.0;
            var opacity = 1 - ease;
            var rgb = ease * 5;
 
            el.style.transform = 'translateX(' + tx + 'px) skewX(' + skew + 'deg)';
            el.style.opacity = opacity;
            el.style.filter =
                'drop-shadow(' + (-rgb) + 'px 0 0 rgba(80,160,255,0.75)) ' +
                'drop-shadow(' + rgb + 'px 0 0 rgba(255,55,90,0.65)) ' +
                'brightness(' + (1 + ease * 0.25) + ')';
 
            if (p < 1) {
                requestAnimationFrame(step);
            } else {
                el.style.opacity = '0';
                cb();
            }
         }
        requestAnimationFrame(step);
     }
     }


     .doc-footer {
     function glitchIn(el, dir, cb) {
        grid-template-columns: 1fr 110px 44px 110px;
        var duration = 200;
        var start = null;
        var startX = -dir * 16;
 
        el.style.transform = 'translateX(' + startX + 'px) skewX(' + (-dir * 1.0) + 'deg)';
        el.style.opacity = '0';
 
        function step(ts) {
            if (!start) start = ts;
            var p = Math.min((ts - start) / duration, 1);
            var ease = 1 - Math.pow(1 - p, 3);
 
            var tx = startX * (1 - ease);
            var skew = -dir * 1.0 * (1 - ease);
            var opacity = ease;
            var rgb = (1 - ease) * 3;
            var brightness = 1 + (1 - ease) * 0.35;
 
            el.style.transform = 'translateX(' + tx + 'px) skewX(' + skew + 'deg)';
            el.style.opacity = opacity;
            el.style.filter =
                'drop-shadow(' + (-rgb) + 'px 0 0 rgba(80,160,255,0.65)) ' +
                'drop-shadow(' + rgb + 'px 0 0 rgba(255,55,90,0.55)) ' +
                'brightness(' + brightness + ')';
 
            if (p < 1) {
                requestAnimationFrame(step);
            } else {
                el.style.transform = '';
                el.style.opacity = '';
                el.style.filter = '';
                cb();
            }
        }
        requestAnimationFrame(step);
     }
     }


     .doc-keys {
if (document.readyState === 'loading') {
         display: none;
    document.addEventListener('DOMContentLoaded', initDocTabs);
     }
} else {
    initDocTabs();
}
 
if (typeof mw !== 'undefined' && mw.hook) {
     mw.hook('wikipage.content').add(function () {
         initDocTabs();
     });
}
}


@media screen and (max-width: 980px) {
})();
     .doc-layout,
 
     .doc-layout-lower,
/* =========================================
     .doc-layout-right {
  Doc Section Switch — 좌측 섹션 전환
        grid-template-columns: 1fr;
  ========================================= */
    }
 
$(document).on('click', '.doc-nav-item[data-section]', function () {
     var name = $(this).attr('data-section');
     var display = document.getElementById('doc-main-display');
     var titleEl = document.getElementById('doc-center-title');
    var tabBar = document.getElementById('doc-tab-bar-text');


     .doc-footer {
     if (!display) return;
        grid-template-columns: 1fr;
        height: auto;
        padding: 8px 10px;
    }


     .doc-footer-sliders {
     $('.doc-nav-item[data-section]').removeClass('active');
        justify-content: center;
     $('.doc-nav-item[data-section="' + name + '"]').addClass('active');
     }


     .doc-date,
     if (name === 'text') {
    .doc-knob {
        if (titleEl) titleEl.textContent = '개요';
         justify-self: center;
        if (tabBar) $(tabBar).show();
        var activeTab = tabBar ? tabBar.querySelector('.doc-tab.active') : null;
        if (!activeTab && tabBar) activeTab = tabBar.querySelector('.doc-tab');
        if (activeTab) {
            var ref = activeTab.dataset.ref;
            var refEl = ref ? document.getElementById(ref) : null;
            display.innerHTML = refEl ? refEl.innerHTML : (activeTab.dataset.content || '');
        }
    } else {
        if (titleEl) titleEl.textContent = name === 'factions' ? '세력' : name === 'people' ? '인물' : name;
        if (tabBar) $(tabBar).hide();
         var refEl = document.getElementById('doc-content-' + name);
        display.innerHTML = refEl ? refEl.innerHTML : '';
     }
     }
}
});

2026년 5월 10일 (일) 09:21 판

mw.loader.load('https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js');

function loadLangScript(done) {
    $.getScript('/index.php?title=미디어위키:Lang.js&action=raw&ctype=text/javascript')
        .done(function() {
            if (typeof done === 'function') done();
        })
        .fail(function(a, b, c) {
            console.error('Lang.js load failed:', b, c);
            if (typeof done === 'function') done();
        });
}

$(function() {
    $('body').prepend(
        '<div class="WW-bg" style="position:fixed;top:0;left:0;width:100%;height:100vh;"></div>'
    );
});

// 페이지 전환 사운드
var transitionSound = new Audio('/index.php?title=특수:Redirect/file/Sfx-ui-001.mp3');

(function() {
    var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
    var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
    var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';
    transitionSound.volume = sfxOn ? master * sfx : 0;
})();

function playStaticSound() {
    var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
    var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
    var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';

    if (!sfxOn) return;

    transitionSound.volume = master * sfx;
    transitionSound.currentTime = 0;
    transitionSound.play();
}

// 현재 언어 감지
function getCurrentLang() {
    var langData = document.getElementById('clbi-lang-data');
    return langData ? (langData.getAttribute('data-lang') || 'ko') : 'ko';
}

function normalizePageName(value) {
    return String(value || '')
        .split('?')[0]
        .replace(/^\/index\.php\//, '')
        .replace(/_/g, ' ')
        .trim();
}

function buildWikiPath(title) {
    return '/index.php/' + encodeURI(String(title || '').replace(/ /g, '_'));
}

// 국가_및_조합 전용 왼쪽 사이드바 이미지
function updateLeftSidebarNationsImage() {
    var imageId = 'clbi-left-nations-image';

    // SPA 이동 시 이전 문서에서 삽입했던 이미지를 먼저 제거한다.
    // 이 처리가 없으면 국가_및_조합에서 다른 문서로 이동했을 때 이미지가 남을 수 있다.
    $('#' + imageId).remove();

    // 현재 문서명이 국가 및 조합일 때만 삽입한다.
    // wgPageName은 보통 국가_및_조합처럼 언더스코어를 포함하므로 normalizePageName으로 통일해서 비교한다.
    var pageName = normalizePageName(mw.config.get('wgPageName'));
    var targetPage = normalizePageName('국가_및_조합');

    if (pageName !== targetPage) {
        return;
    }

    var $leftSidebar = $('#clbi-left-sidebar');

    if (!$leftSidebar.length) {
        return;
    }

    // 왼쪽 사이드바 기본 자식 순서:
    // 0 = 언어 섹션, 1 = 카테고리 섹션.
    // 이미지는 카테고리 섹션 바로 아래에 삽입한다.
    var $categoryBox = $leftSidebar.children('.clbi-left-box').eq(1);

    if (!$categoryBox.length) {
        return;
    }

    // 컨테이너나 테두리를 추가하지 않고 img 요소만 넣는다.
    // 실제 파일을 바꾸려면 Redirect/file 뒤 파일명만 교체하면 된다.
    $categoryBox.after(
        '<img id="' + imageId + '" ' +
        'class="clbi-left-page-image" ' +
        'src="/index.php?title=특수:Redirect/file/img-nations-sidebar-001.png" ' +
        'alt="" loading="lazy">'
    );
}

// 사이드바 업데이트
function updateSidebar() {
    if (!window.LANG || !window.LANG_NAMES || !window.CAT_LINKS) {
        setTimeout(updateSidebar, 100);
        return;
    }

    var langData = document.getElementById('clbi-lang-data');
    var currentLang = getCurrentLang();
    var t = (window.LANG && window.LANG[currentLang]) ? window.LANG[currentLang] : window.LANG.ko;
    var names = (window.LANG_NAMES && window.LANG_NAMES[currentLang]) ? window.LANG_NAMES[currentLang] : window.LANG_NAMES.ko;
    var cl = (window.CAT_LINKS && window.CAT_LINKS[currentLang]) ? window.CAT_LINKS[currentLang] : window.CAT_LINKS.ko;

    var allLangs = langData ? {
        ko: langData.getAttribute('data-ko') || '',
        en: langData.getAttribute('data-en') || '',
        zh: langData.getAttribute('data-zh') || '',
        ja: langData.getAttribute('data-ja') || ''
    } : {
        ko: '',
        en: '',
        zh: '',
        ja: ''
    };

    $('#clbi-lang-current').text(names[currentLang]);

    var langHtml = '';
    $.each(['ko', 'en', 'zh', 'ja'], function(i, lang) {
        if (lang === currentLang) return;

        var target = allLangs[lang];
        if (target) {
            langHtml += '<div class="clbi-lang-link"><a href="' + buildWikiPath(target) + '?uselang=' + lang + '">' + names[lang] + '</a></div>';
        } else {
            langHtml += '<div class="clbi-lang-wip">' + names[lang] + '</div>';
        }
    });
    $('#clbi-lang-list').html(langHtml);

    $('#clbi-title-language').text(t.language);
    $('#clbi-title-categories').text(t.categories);
    $('#clbi-title-links').text(t.links);
    $('#clbi-title-search a').text(t.search);
    $('#clbi-search-input').attr('placeholder', t.search + '...');
    $('#clbi-title-recent a').text(t.recentChanges);
    $('#clbi-title-guide').text(t.guide);
    $('#clbi-guide-link').text(t.getStarted);
    $('#clbi-title-playlist').text(t.playlist);

    $('#clbi-cat-main a').attr('href', buildWikiPath(cl.main));
    $('#clbi-cat-main .clbi-cat-label').text(t.mainMenu);

    $('#clbi-cat-nations a').attr('href', buildWikiPath(cl.nations));
    $('#clbi-cat-nations .clbi-cat-label').text(t.nations);

    $('#clbi-cat-corporations a').attr('href', buildWikiPath(cl.corporations));
    $('#clbi-cat-corporations .clbi-cat-label').text(t.corporations);

    $('#clbi-cat-military a').attr('href', buildWikiPath(cl.military));
    $('#clbi-cat-military .clbi-cat-label').text(t.military);

    $('#clbi-cat-history a').attr('href', buildWikiPath(cl.history));
    $('#clbi-cat-history .clbi-cat-label').text(t.history);

    $('#clbi-cat-personnel a').attr('href', buildWikiPath(cl.personnel));
    $('#clbi-cat-personnel .clbi-cat-label').text(t.personnel);

    $('#clbi-btn-contribution').text(t.contribution);
    $('#clbi-btn-watchlist').text(t.watchlist);
    $('#clbi-btn-preferences').text(t.preferences);
    $('#clbi-btn-logout').text(t.logout);
    $('#clbi-btn-login').text(t.login);

    var pageName = normalizePageName(mw.config.get('wgPageName'));
    var specialPage = String(mw.config.get('wgCanonicalSpecialPageName') || '');

    $('.clbi-cat-btn').removeClass('clbi-cat-active');
    $.each(['main', 'nations', 'corporations', 'military', 'history', 'personnel'], function(i, key) {
        if (cl[key] && pageName === normalizePageName(cl[key])) {
            $('#clbi-cat-' + key).addClass('clbi-cat-active');
        }
    });

    $('.clbi-user-btn').removeClass('clbi-user-btn-active');

    if (
        specialPage === 'Contributions' ||
        specialPage === '기여' ||
        pageName.indexOf('특수:기여') === 0 ||
        pageName.indexOf('Special:Contributions') === 0
    ) {
        $('#clbi-btn-contribution').addClass('clbi-user-btn-active');
    }

    if (specialPage === 'Watchlist') {
        $('#clbi-btn-watchlist').addClass('clbi-user-btn-active');
    }

    if (
        specialPage === '설정' ||
        pageName === '특수:설정' ||
        pageName === 'Special:설정'
    ) {
        $('#clbi-btn-preferences').addClass('clbi-user-btn-active');
    }

    $('.toggleBtn').each(function() {
        var btn = $(this);
        if (!$('#' + btn.data('target')).hasClass('folding-open')) {
            btn.text(t.expand);
        } else {
            btn.text(t.collapse);
        }
    });

    // 왼쪽 사이드바의 문서별 전용 삽입물을 갱신한다.
    // 현재는 국가_및_조합 문서에서만 카테고리 섹션 아래 이미지를 삽입한다.
    updateLeftSidebarNationsImage();
}

function canShowContentTools() {
    // 비로그인 사용자는 편집/역사/공유 버튼을 숨김
    if (!mw.config.get('wgUserName')) {
        return false;
    }

    // MediaWiki가 현재 문서를 편집 가능하지 않다고 판단하면 숨김
    var isEditable = mw.config.get('wgIsProbablyEditable');
    if (isEditable === false) {
        return false;
    }

    var relevantEditable = mw.config.get('wgRelevantPageIsProbablyEditable');
    if (relevantEditable === false) {
        return false;
    }

    return true;
}

function moveCatlinksToBottom() {
    var main = $('.liberty-content-main');
    var parserOutput = $('.liberty-content-main .mw-parser-output').first();
    var catlinks = $('.catlinks');

    if (!main.length || !catlinks.length) return;

    catlinks.each(function () {
        var cat = $(this);

        if (parserOutput.length) {
            cat.appendTo(parserOutput);
        } else {
            cat.appendTo(main);
        }
    });
}

// 대문 스타일
function applyMainPageStyle() {
    var specialPage = mw.config.get('wgCanonicalSpecialPageName');
    if (specialPage === 'Preferences') return;

    var pageName = normalizePageName(mw.config.get('wgPageName'));
    var hideTools = (pageName === '대문' || !canShowContentTools());

    // 모든 문서에서 분류 바를 본문 컨테이너 아래로 이동
    moveCatlinksToBottom();

    if (pageName === '대문') {
        $('.liberty-content-header').css('display', 'none');
        $('.mw-page-title-main').addClass('clbi-hide');
        $('.catlinks').css('display', 'none');
        $('.liberty-content-main').css('border-radius', '5px 5px 0 0');

        if ($('#clbi-main-logo').length === 0) {
            $('.liberty-content').prepend(
                '<div id="clbi-main-logo" style="text-align:center;padding:10px 0;">' +
                '<img src="/index.php?title=특수:Redirect/file/Img-clbi-001.png" style="width:900px;height:auto;">' +
                '</div>'
            );
        }

        if ($('#clbi-main-crt-hero').length && $('#clbi-main-crt-hero-wrap').length === 0) {
            var heroWrap = $('<div id="clbi-main-crt-hero-wrap"></div>');

            if ($('#clbi-main-logo').length) {
                heroWrap.insertAfter('#clbi-main-logo');
            } else {
                heroWrap.insertBefore('.liberty-content-main');
            }

            $('#clbi-main-crt-hero').appendTo('#clbi-main-crt-hero-wrap');
        }
} else if ($('.screen-header').length > 0) {
        $('.liberty-content-header').css('display', 'none');
        $('.mw-page-title-main').addClass('clbi-hide');
        $('.catlinks').css('display', '');
        $('.liberty-content-main').css('border-radius', '5px');
        $('#clbi-main-logo').remove();
        $('#clbi-main-crt-hero-wrap').remove();

        if ($('#clbi-tools-box').length === 0 && canShowContentTools()) {
            var $toolsBox = $('<div id="clbi-tools-box" class="clbi-left-box"></div>');
            var $toolsTitle = $('<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> 관리</div>');
            var $toolsContent = $('<div class="clbi-left-content"></div>');
            $toolsContent.append($('.content-tools .btn-group').clone(true));
            $toolsBox.append($toolsTitle).append($toolsContent);
            $('#clbi-left-sidebar').append($toolsBox);
        }

        $('.content-tools').css('display', 'none');
        $('.liberty-content').addClass('content-tools-hidden');
    } else {
        $('.liberty-content-header').css('display', '');
        $('.mw-page-title-main').removeClass('clbi-hide');
        $('.catlinks').css('display', '');
        $('.liberty-content-main').css('border-radius', '');
        $('#clbi-main-logo').remove();
        $('#clbi-main-crt-hero-wrap').remove();
        $('#clbi-tools-box').remove();
    }
        $('.liberty-content-header').css('display', '');
        $('.mw-page-title-main').removeClass('clbi-hide');
        $('.catlinks').css('display', '');
        $('.liberty-content-main').css('border-radius', '');
        $('#clbi-main-logo').remove();
        $('#clbi-main-crt-hero-wrap').remove();
    }

    if (hideTools) {
        $('.content-tools').css('display', 'none');
        $('.liberty-content').addClass('content-tools-hidden');
    } else {
        $('.content-tools').css('display', '');
        $('.liberty-content').removeClass('content-tools-hidden');
    }

    updateSidebar();
}

// 본문 기본 목차 제거
function removeNativeTocFromContent() {
    $('.liberty-content-main #toc, .liberty-content-main .toc').remove();
}

// 왼쪽 목차: MediaWiki 문단 ID 가져오기
function getHeadingId(heading) {
    if (heading.id) {
        return heading.id;
    }

    var headline = heading.querySelector('.mw-headline[id]');
    if (headline && headline.id) {
        return headline.id;
    }

    return '';
}

// 왼쪽 목차: MediaWiki 문단 제목 텍스트 가져오기
function getHeadingText(heading) {
    var headline = heading.querySelector('.mw-headline');
    var source = headline || heading;
    var clone = source.cloneNode(true);

    $(clone).find('.mw-editsection, .mw-editsection-bracket, .mw-editsection-divider').remove();

    return (clone.textContent || '')
        .replace(/\s+/g, ' ')
        .trim();
}

// 왼쪽 목차: 긴 제목에 자동 스크롤 적용
function initTocTitleScroll(root) {
    var $items = root
        ? $(root).find('.toc-scroll-text')
        : $('#side-toc-box .toc-scroll-text');

    $items.each(function () {
        var $text = $(this);
        var $wrap = $text.closest('.toc-scroll-wrap');

        if (!$wrap.length) return;

        var wrapW = Math.floor($wrap.width());
        var textW = Math.ceil(this.scrollWidth);

        // 왼쪽 목차: 레이아웃 계산이 끝나지 않았으면 이번 실행에서는 건드리지 않는다.
        if (!wrapW || !textW) return;

        if (textW <= wrapW + 12) {
            // 왼쪽 목차: 칸을 넘지 않는 제목은 전체 텍스트를 그대로 보여준다.
            $wrap.removeClass('is-scrolling');

            if ($text.data('toc-scroll-enabled')) {
                $text.css({
                    animation: '',
                    'animation-delay': '',
                    '--scroll-dist': ''
                });
                $text.removeData('toc-scroll-enabled');
                $text.removeData('toc-scroll-key');
            }

            return;
        }

        var scrollDist = '-' + (textW - wrapW + 10) + 'px';
        var duration = Math.max(7, textW / 38) * 1.25;
        var scrollKey = scrollDist + '|' + duration;

        // 왼쪽 목차: 긴 제목에는 오른쪽 페이드와 스크롤을 적용한다.
        $wrap.addClass('is-scrolling');

        // 왼쪽 목차: 같은 값으로 이미 적용된 애니메이션은 다시 초기화하지 않는다.
        if ($text.data('toc-scroll-key') === scrollKey) {
            return;
        }

        $text.data('toc-scroll-enabled', true);
        $text.data('toc-scroll-key', scrollKey);

        $text.css({
            // 왼쪽 목차: 페이지 진입 직후에는 잠시 읽을 시간을 준 뒤 흐르게 한다.
            animation: 'toc-scroll-blink-reset ' + duration + 's linear infinite',
            'animation-delay': '1s',
            '--scroll-dist': scrollDist
        });
    });
}

// 목차를 왼쪽 사이드바에 새로 생성
function moveTocToLeftSidebar() {
    // 왼쪽 목차: MediaWiki가 만든 원래 목차는 본문에서 제거한다.
    removeNativeTocFromContent();

    var leftSidebar = document.getElementById('clbi-left-sidebar');
    if (!leftSidebar) return;

    var content =
        document.querySelector('.liberty-content-main .mw-parser-output') ||
        document.querySelector('.liberty-content-main');

    if (!content) return;

    var headings = Array.prototype.slice.call(
        content.querySelectorAll('h2, h3')
    ).filter(function (heading) {
        if (heading.closest('#toc, .toc, #side-toc-box')) return false;

        var id = getHeadingId(heading);
        var text = getHeadingText(heading);

        if (!id || !text) return false;

        return true;
    });

    var tocKey = headings.map(function (heading) {
        return getHeadingId(heading) + '|' + getHeadingText(heading);
    }).join('||');

    var existingBox = document.getElementById('side-toc-box');

    // 왼쪽 목차: 같은 문서에서 같은 목차를 이미 만들었다면 다시 지우고 만들지 않는다.
    if (existingBox && existingBox.getAttribute('data-toc-key') === tocKey) {
        initTocTitleScroll(existingBox);
        return;
    }

    if (existingBox) {
        existingBox.remove();
    }

    if (!headings.length) return;

    var tocBox = document.createElement('div');
    tocBox.className = 'clbi-left-box';
    tocBox.id = 'side-toc-box';
    tocBox.setAttribute('data-toc-key', tocKey);

    var title = document.createElement('div');
    title.className = 'clbi-left-title';

    // 왼쪽 목차: 박스 제목은 Lang.js의 현재 UI 언어를 따른다.
    var currentLang = getCurrentLang();
    var t = (window.LANG && window.LANG[currentLang]) ? window.LANG[currentLang] : window.LANG.ko;
    var tocTitleText = (t && t.toc) ? t.toc : '목차';

    title.innerHTML =
        '<span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> ' + tocTitleText;

    var body = document.createElement('div');
    body.className = 'clbi-left-content toc-sidebar-content';

    var list = document.createElement('ul');
    list.className = 'generated-toc';

    headings.forEach(function (heading) {
        var id = getHeadingId(heading);
        var text = getHeadingText(heading);
        var level = heading.tagName.toLowerCase() === 'h3' ? 3 : 2;

        var item = document.createElement('li');
        item.className = 'toc-level-' + level;

        var link = document.createElement('a');
        link.setAttribute('href', '#' + id);

        // 왼쪽 목차: 긴 제목 스크롤을 위해 텍스트를 별도 span으로 감싼다.
        var textWrap = document.createElement('span');
        textWrap.className = 'toc-scroll-wrap';

        var textSpan = document.createElement('span');
        textSpan.className = 'toc-scroll-text';
        textSpan.textContent = text;

        textWrap.appendChild(textSpan);
        link.appendChild(textWrap);

        item.appendChild(link);
        list.appendChild(item);
    });

    body.appendChild(list);
    tocBox.appendChild(title);
    tocBox.appendChild(body);
    leftSidebar.appendChild(tocBox);

    // 왼쪽 목차: DOM 배치가 끝난 뒤 긴 제목 스크롤 여부를 계산한다.
    requestAnimationFrame(function () {
        initTocTitleScroll(tocBox);

        setTimeout(function () {
            initTocTitleScroll(tocBox);
        }, 120);
    });
}

// 초기화 함수
function initSidebars() {
    var header = $('.liberty-content-header');
    var content = $('.liberty-content');

    if (header.length && content.length) {
        header.prependTo(content);
    }

    if ($('#clbi-right-sidebar').length === 0) {
        var username = mw.config.get('wgUserName');
        var isLoggedIn = username !== null;
        var avatarSrc = isLoggedIn
            ? '/index.php?title=특수:Redirect/file/Pfp-' + username + '.png'
            : '/index.php?title=특수:Redirect/file/Pfp-default.png';

        var userBox;
        if (isLoggedIn) {
            userBox =
                '<div class="clbi-right-box">' +
                    '<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
                        '<img src="' + avatarSrc + '" onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png\'" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
                        '<div style="position:relative;width:100%;margin-top:2px;height:18px;line-height:18px;text-align:center;">' +
                            '<button type="button" id="clbi-playlist-toggle" aria-label="플레이리스트" style="position:absolute;top:0;left:10px;background:none;border:none;padding:0;width:18px;height:18px;color:#E2E2E2;cursor:pointer;display:flex;align-items:center;justify-content:center;">' +
                                '<span class="clbi-icon" style="--icon:var(--ic-ui-009);width:16px;height:16px;"></span>' +
                            '</button>' +
                            '<a href="/index.php/사용자:' + username + '" style="font-size:13px;font-weight:700;color:#E2E2E2 !important;text-decoration:none !important;line-height:18px;display:inline-block;vertical-align:middle;max-width:calc(100% - 58px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + username + '</a>' +
                            '<button type="button" id="clbi-notification-toggle" aria-label="알림" style="position:absolute;top:0;right:10px;background:none;border:none;padding:0;width:18px;height:18px;color:#E2E2E2;cursor:pointer;display:flex;align-items:center;justify-content:center;">' +
                                '<span class="clbi-icon" style="--icon:var(--ic-ui-009);width:16px;height:16px;"></span>' +
                                '<span id="clbi-notification-badge" style="display:none;position:absolute;top:-6px;right:-8px;min-width:14px;height:14px;padding:0 3px;border-radius:999px;background:#854369;color:#fff;font-size:9px;line-height:14px;font-weight:700;text-align:center;"></span>' +
                            '</button>' +
                        '</div>' +
                    '</div>' +
                    '<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
                        '<a href="/index.php/특수:기여/' + username + '" class="clbi-user-btn" id="clbi-btn-contribution">기여</a>' +
                        '<a href="/index.php/특수:주시문서목록" class="clbi-user-btn" id="clbi-btn-watchlist">주시문서 목록</a>' +
                        '<a href="/index.php/특수:설정" class="clbi-user-btn" id="clbi-btn-preferences">설정</a>' +
                        '<a href="/index.php?title=특수:로그아웃&returnto=대문" class="clbi-user-btn clbi-user-btn-logout" id="clbi-btn-logout">로그아웃</a>' +
                    '</div>' +
                '</div>';
        } else {
            userBox =
                '<div class="clbi-right-box">' +
                    '<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
                        '<img src="/index.php?title=특수:Redirect/file/Pfp-default.png" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
                        '<span style="font-size:13px;font-weight:700;color:#E2E2E2;">Guest</span>' +
                    '</div>' +
                    '<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
                        '<a href="/index.php?title=특수:로그인&returnto=대문" class="clbi-user-btn" id="clbi-btn-login">로그인</a>' +
                    '</div>' +
                '</div>';
        }

        var recentBox =
            '<div class="clbi-right-box">' +
                '<div class="clbi-right-title" id="clbi-title-recent"><span class="clbi-icon" style="--icon:var(--ic-ui-006)"></span> <a href="/index.php/특수:최근바뀜" style="color:#E2E2E2 !important;text-decoration:none !important;">최근 변경</a></div>' +
                '<div class="clbi-right-content" id="clbi-recent-list">불러오는 ...</div>' +
            '</div>';

        var linkBox =
            '<div class="clbi-right-box">' +
                '<div class="clbi-right-title" id="clbi-title-links"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> 링크</div>' +
                '<div class="clbi-right-content clbi-link-box">' +
                    '<ul>' +
                        '<li><a href="https://discord.gg/ctaeJ9d3Q5" target="_blank">Discord</a></li>' +
                        '<li><a href="https://www.youtube.com/@nxdsxn" target="_blank">YouTube</a></li>' +
                        '<li><a href="https://x.com/nxd_sxn" target="_blank">X</a></li>' +
                    '</ul>' +
                '</div>' +
            '</div>';

        var sidebar =
            userBox +
            '<div class="clbi-right-box">' +
                '<div class="clbi-right-title" id="clbi-title-search"><span class="clbi-icon" style="--icon:var(--ic-ui-005)"></span> <a href="/index.php/특수:검색" style="color:#E2E2E2 !important;text-decoration:none !important;">검색</a></div>' +
                '<div class="clbi-right-content">' +
                    '<input id="clbi-search-input" type="text" placeholder="검색...">' +
                    '<button id="clbi-search-btn">GO</button>' +
                '</div>' +
            '</div>' +
            recentBox +
            '<div class="clbi-right-box">' +
                '<div class="clbi-right-title" id="clbi-title-guide"><span class="clbi-icon" style="--icon:var(--ic-ui-007)"></span> 가이드</div>' +
                '<div class="clbi-right-content">' +
                    '<a href="/index.php/CLBI_Wiki/KR_시작하기_(CLBI)" id="clbi-guide-link">시작하기</a>' +
                '</div>' +
            '</div>' +
            linkBox;

        $('.content-wrapper').append('<div id="clbi-right-sidebar">' + sidebar + '</div>');

        $('#clbi-search-btn').click(function() {
            var query = $('#clbi-search-input').val();
            if (query) {
                window.location.href = '/index.php?search=' + encodeURIComponent(query);
            }
        });

        $('#clbi-search-input').keypress(function(e) {
            if (e.which === 13) $('#clbi-search-btn').click();
        });

        $.getJSON(
            '/api.php?action=query&list=recentchanges&rclimit=5&rcprop=title|timestamp&format=json&rcnamespace=0&rctype=edit|new',
            function(data) {
                var items = data.query.recentchanges;
                var html = '';

                $.each(items, function(i, item) {
                    var label = timeAgo(item.timestamp);
                    html +=
                        '<div class="clbi-recent-item">' +
                            '<div class="clbi-recent-title-wrap">' +
                                '<a href="/index.php/' + encodeURIComponent(item.title) + '" class="clbi-recent-title">' + item.title + '</a>' +
                            '</div>' +
                            '<span class="clbi-recent-time">' + label + '</span>' +
                        '</div>';
                });

                $('#clbi-recent-list').html(html);

                $('#clbi-recent-list .clbi-recent-item').each(function() {
                    var wrap = $(this).find('.clbi-recent-title-wrap');
                    var title = $(this).find('.clbi-recent-title');
                    var wrapW = wrap.width();
                    var titleW = title[0].scrollWidth;

                    if (titleW > wrapW + 20) {
                        var duration = titleW / 40;
                        title.css({
                            animation: 'clbi-scroll ' + duration + 's linear infinite',
                            '--scroll-dist': '-' + (titleW - wrapW + 8) + 'px'
                        });
                    }
                });
            }
        ).fail(function() {
            $('#clbi-recent-list').html('불러오기 실패');
        });
    }

    if ($('#clbi-left-sidebar').length === 0) {
        var leftSidebar =
            '<div id="clbi-left-sidebar">' +
                '<div class="clbi-left-box">' +
                    '<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-001)"></span> <span id="clbi-title-language">언어</span></div>' +
                    '<div class="clbi-left-content clbi-lang-box" id="clbi-lang-box">' +
                        '<div class="clbi-lang-current" id="clbi-lang-current">한국어</div>' +
                        '<div id="clbi-lang-list" style="display:none;"></div>' +
                    '</div>' +
                '</div>' +
                '<div class="clbi-left-box">' +
                    '<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> <span id="clbi-title-categories">카테고리</span></div>' +
                    '<div class="clbi-left-content clbi-cat-box">' +
                        '<div class="clbi-cat-btn" id="clbi-cat-main"><a href="/index.php/대문"><div class="clbi-cat-text"><span class="clbi-cat-label">메인 메뉴</span><span class="clbi-cat-sub">MAIN MENU</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-nations"><a href="/index.php/국가_및_조합"><div class="clbi-cat-text"><span class="clbi-cat-label">국가  조합</span><span class="clbi-cat-sub">NATIONS & FACTIONS</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-corporations"><a href="/index.php/기업_및_공동체"><div class="clbi-cat-text"><span class="clbi-cat-label">기업  공동체</span><span class="clbi-cat-sub">CORPORATIONS & COMMUNITIES</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-military"><a href="/index.php/군_정치집단"><div class="clbi-cat-text"><span class="clbi-cat-label">, 정치집단</span><span class="clbi-cat-sub">MILITARY & POLITICS</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-history"><a href="/index.php/역사적_사건"><div class="clbi-cat-text"><span class="clbi-cat-label">역사적 사건</span><span class="clbi-cat-sub">HISTORICAL EVENTS</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                        '<div class="clbi-cat-btn" id="clbi-cat-personnel"><a href="/index.php/인물"><div class="clbi-cat-text"><span class="clbi-cat-label">인물</span><span class="clbi-cat-sub">KEY PERSONNEL</span></div></a><div class="clbi-cat-arrow"></div></div>' +
                    '</div>' +
                '</div>' +
            '</div>';

        $('.content-wrapper').prepend(leftSidebar);

        // 왼쪽 사이드바가 처음 생성된 직후에도 한 번 실행한다.
        // 이후 언어 갱신과 SPA 이동 시에는 updateSidebar()에서 다시 실행된다.
        updateLeftSidebarNationsImage();

        $(document).on('click', '#clbi-lang-current', function() {
            if ($('#clbi-lang-list').is(':hidden')) {
                $('#clbi-lang-current').css('margin-bottom', '8px');
                $('#clbi-lang-list').slideDown(200);
            } else {
                $('#clbi-lang-list').slideUp(200, function() {
                    $('#clbi-lang-current').css('margin-bottom', '0');
                });
            }
        });
    }

    applyMainPageStyle();
    $('#side-toc-box').remove();

    mw.loader.using(['mediawiki.api']).then(function() {
        setTimeout(function() {
            initNotifications();
            initPlaylistPopup();
            initProfile();
            moveTocToLeftSidebar();
        }, 300);

        setTimeout(moveTocToLeftSidebar, 800);
        setTimeout(moveTocToLeftSidebar, 1500);
    });
}

$(function() {
    loadLangScript(function() {
        setTimeout(function() {
            initSidebars();
        }, 100);
    });
});

// SPA 네비게이션
function shouldSkip(url) {
    return url.match(/action=edit|action=submit|action=history|action=delete|action=protect|action=purge|특수:로그인|특수:로그아웃|Special:UserLogin|Special:UserLogout|특수:사용자정보|특수:비밀번호바꾸기|uselang=/);
}

$(function() {
    if (window._spaInitialized) return;
    window._spaInitialized = true;

    function isInternal(url) {
        var a = document.createElement('a');
        a.href = url;
        return a.hostname === window.location.hostname;
    }

    function loadPage(url) {
        fetch(url)
            .then(function(res) {
                return res.text();
            })
            .then(function(html) {
                var parser = new DOMParser();
                var doc = parser.parseFromString(html, 'text/html');

                var scripts = doc.querySelectorAll('script');
                for (var i = 0; i < scripts.length; i++) {
                    var src = scripts[i].textContent;

                    if (src.indexOf('wgNamespaceNumber') !== -1) {
                        var match = src.match(/"wgNamespaceNumber":(-?\d+)/);
                        if (match) mw.config.set('wgNamespaceNumber', parseInt(match[1], 10));

                        var matchTitle = src.match(/"wgTitle":"([^"]+)"/);
                        if (matchTitle) mw.config.set('wgTitle', matchTitle[1]);

                        var matchPage = src.match(/"wgPageName":"([^"]+)"/);
                        if (matchPage) mw.config.set('wgPageName', matchPage[1]);

                        var matchSpecial = src.match(/"wgCanonicalSpecialPageName":"([^"]+)"/);
                        if (matchSpecial) {
                            mw.config.set('wgCanonicalSpecialPageName', matchSpecial[1]);
                        } else {
                            mw.config.set('wgCanonicalSpecialPageName', false);
                        }
                        break;
                    }
                }

                var newContent = doc.querySelector('.liberty-content-main');
                var newTitle = doc.querySelector('.mw-page-title-main');
                var newHead = doc.querySelector('title');
                var newHeader = doc.querySelector('.liberty-content-header');

                if (newContent) {
                    $('#side-toc-box').remove();
                    $('.liberty-content-main').html(newContent.innerHTML);
                    $('.profile-card').remove();
                    $('body').removeClass('page-loading');
                }

                if (newTitle) {
                    $('.mw-page-title-main').html(newTitle.innerHTML);
                }

                if (newHead) {
                    document.title = newHead.textContent;
                }

                if (newHeader) {
                    $('.liberty-content-header').html(newHeader.innerHTML);
                }

                window.scrollTo(0, 0);
                $('#clbi-lang-list').hide();
                $('#clbi-lang-current').css('margin-bottom', '0');

                mw.hook('wikipage.content').fire($('.liberty-content-main'));
                applyMainPageStyle();

                $('#side-toc-box').remove();
                setTimeout(moveTocToLeftSidebar, 100);
                setTimeout(moveTocToLeftSidebar, 500);
                setTimeout(moveTocToLeftSidebar, 1200);

                mw.loader.using(['mediawiki.api']).then(function() {
                    initProfile();
                    moveTocToLeftSidebar();
                });
            });
    }

// 목차 링크는 전용 처리
$(document).on('click', '#side-toc-box a, #toc a, .toc a', function(e) {
    var href = $(this).attr('href');
    if (!href || href.charAt(0) !== '#') return;

    var rawId = href.slice(1);
    if (!rawId) return;

    var decodedId = rawId;

    try {
        decodedId = decodeURIComponent(rawId);
    } catch (err) {
        decodedId = rawId;
    }

    var target = document.getElementById(decodedId);

    if (!target && window.CSS && CSS.escape) {
        target = document.querySelector('#' + CSS.escape(decodedId));
    }

    if (!target) return;

    e.preventDefault();
    e.stopPropagation();

    var scrollTarget = target.closest('h2, h3') || target;

    scrollTarget.scrollIntoView({
        behavior: 'auto',
        block: 'start'
    });

    history.replaceState(null, '', '#' + rawId);
});

    $(document).on('click', 'a', function(e) {
        var href = $(this).attr('href');
        if (!href) return;

        // 목차 링크는 별도 핸들러에서 처리
        if ($(this).closest('#side-toc-box, #toc, .toc').length) return;

        // 단순 해시 링크는 SPA 가로채기 제외
        if (href.startsWith('#')) return;

        var link = document.createElement('a');
        link.href = href;

        var samePath = decodeURIComponent(link.pathname) === decodeURIComponent(window.location.pathname);
        var sameSearch = (link.search || '') === (window.location.search || '');

        if (link.hash && samePath && sameSearch) return;

        var currentBase = window.location.href.split('#')[0];
        var targetBase = link.href.split('#')[0];

        if (link.hash && currentBase === targetBase) return;

        if (!isInternal(href)) return;
        if (shouldSkip(href)) return;

        e.preventDefault();
        playStaticSound();
        $('body').addClass('page-loading');
        history.pushState(null, '', href);
        loadPage(href);
    });

    window.addEventListener('popstate', function() {
        loadPage(window.location.href);
    });
});

// 시간 계산 함수
function timeAgo(timestamp) {
    var now = new Date();
    var date = new Date(timestamp);
    var diff = Math.floor((now - date) / 1000);

    if (diff < 60) return diff + ' ';
    if (diff < 3600) return Math.floor(diff / 60) + ' ';
    if (diff < 86400) return Math.floor(diff / 3600) + '시간 ';
    return Math.floor(diff / 86400) + ' ';
}

// 펼접 토글
// 펼접 토글
function getFoldTexts() {
    var lang = getCurrentLang();
    return (window.LANG && window.LANG[lang])
        ? window.LANG[lang]
        : (window.LANG ? window.LANG.ko : { expand: '펼치기', collapse: '접기' });
}

function refreshOpenAncestors($start) {
    $start.parents('[id^="collapsible"]').each(function () {
        var $parent = $(this);
        if (!$parent.hasClass('folding-open')) return;

        // 이미 fully open 상태면 굳이 다시 잠그지 않음
        if ($parent.data('fold-state') === 'open') {
            return;
        }

        $parent.css('max-height', this.scrollHeight + 'px');
    });
}

function bindInnerResizeUpdates($target) {
    // 이미지 늦게 로드될 때 높이 갱신
    $target.find('img').off('.foldimg').on('load.foldimg', function () {
        if ($target.hasClass('folding-open')) {
            if ($target.data('fold-state') !== 'open') {
                $target.css('max-height', $target[0].scrollHeight + 'px');
            }
            refreshOpenAncestors($target);
        }
    });
}

function openFold($target, $btn) {
    var t = getFoldTexts();

    $target.data('fold-state', 'opening');
    $target.addClass('folding-open');

    // 열린 뒤 자연 확장 가능하게 만들기 위해 먼저 px로 열기
    $target.css('max-height', '0px');
    $target[0].offsetHeight;
    $target.css('max-height', $target[0].scrollHeight + 'px');

    $btn.text(t.collapse);

    bindInnerResizeUpdates($target);

    // 바깥 펼접 즉시 갱신
    refreshOpenAncestors($target);

    // 전환 끝나면 none으로 풀어서 중첩 펼접/동적 내용 증가를 자연스럽게 허용
    $target.off('transitionend.foldopen').on('transitionend.foldopen', function (e) {
        if (e.target !== this) return;
        if (!$target.hasClass('folding-open')) return;

        $target.css('max-height', 'none');
        $target.data('fold-state', 'open');

        refreshOpenAncestors($target);
    });

    // 늦게 렌더되는 콘텐츠 대응
    requestAnimationFrame(function () {
        if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
            $target.css('max-height', $target[0].scrollHeight + 'px');
            refreshOpenAncestors($target);
        }
    });

    setTimeout(function () {
        if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
            $target.css('max-height', $target[0].scrollHeight + 'px');
            refreshOpenAncestors($target);
        }
    }, 80);

    setTimeout(function () {
        if ($target.hasClass('folding-open') && $target.data('fold-state') !== 'open') {
            $target.css('max-height', $target[0].scrollHeight + 'px');
            refreshOpenAncestors($target);
        }
    }, 220);
}

function closeFold($target, $btn) {
    var t = getFoldTexts();

    // none 상태에서 닫으면 transition이 안 되므로 실제 높이로 고정
    if ($target.css('max-height') === 'none' || $target.data('fold-state') === 'open') {
        $target.css('max-height', $target[0].scrollHeight + 'px');
    } else {
        $target.css('max-height', $target[0].scrollHeight + 'px');
    }

    $target.data('fold-state', 'closing');
    $target[0].offsetHeight;
    $target.css('max-height', '0px');
    $target.removeClass('folding-open');

    $btn.text(t.expand);

    refreshOpenAncestors($target);

    setTimeout(function () {
        refreshOpenAncestors($target);
        $target.data('fold-state', 'closed');
    }, 250);
}

$(function () {
    $(document)
        .off('click.clbiToggle')
        .on('click.clbiToggle', '.toggleBtn', function () {
            var $btn = $(this);
            var targetId = $btn.data('target');
            var $target = $('#' + targetId);
            if (!$target.length) return;

            var scrollY = window.scrollY;

            if ($target.hasClass('folding-open')) {
                closeFold($target, $btn);
            } else {
                openFold($target, $btn);
            }

            window.scrollTo(0, scrollY);
        });
});

// ========== 프로필 시스템 ==========
function initProfile() {
    $('.profile-card').remove();

    var ns = mw.config.get('wgNamespaceNumber');
    var title = mw.config.get('wgTitle');

    if (ns === 2) {
        var profileUser = title.split('/')[0];
        renderProfile(profileUser);
    }

    if (mw.config.get('wgCanonicalSpecialPageName') === '사용자정보') {
        initUserProfilePage();
    }
}

function renderProfile(username) {
    var api = new mw.Api();
    api.get({
        action: 'query',
        list: 'users',
        ususers: username,
        usprop: 'editcount'
    }).then(function(data) {
        var user = data.query.users[0];
        var contentEl = document.getElementById('mw-content-text');
        if (!contentEl) return;

        var pageContent = contentEl.querySelector('.mw-parser-output') || contentEl;
        injectProfileCard(username, user, pageContent);
    });
}

function injectProfileCard(username, userData, container) {
    var isOwnPage = mw.config.get('wgUserName') === username;
    var editCount = (userData && userData.editcount) ? userData.editcount : 0;
    var editBtn = isOwnPage
        ? '<a href="/index.php/특수:사용자정보" class="profile-edit-btn">프로필 수정</a>'
        : '';

    var card = document.createElement('div');
    card.className = 'profile-card';
    card.innerHTML =
        '<div class="profile-header">' +
            '<div class="profile-avatar">' +
                '<img src="/index.php?title=특수:Redirect/file/Pfp-' + username + '.png&width=120"' +
                ' onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png&width=120\'"' +
                ' alt="' + username + '">' +
            '</div>' +
            '<div class="profile-info">' +
                '<h2 class="profile-username">' + username + '</h2>' +
                '<div class="profile-name" data-field="name"></div>' +
                '<div class="profile-role" data-field="role"></div>' +
                '<div class="profile-discord" data-field="discord"></div>' +
                '<div class="profile-bio" data-field="bio"></div>' +
                '<div class="profile-badges" data-field="badges"></div>' +
            '</div>' +
            editBtn +
        '</div>' +
        '<div class="profile-stats">' +
            '<div class="profile-stat">' +
                '<span class="clbi-stat-value">' + editCount + '</span>' +
                '<span class="clbi-stat-label">수정 횟수</span>' +
            '</div>' +
        '</div>';

    $('.profile-card').remove();
    container.insertBefore(card, container.firstChild);
    loadProfileFields(username, card);
}

function loadProfileFields(username, card) {
    var api = new mw.Api();
    api.get({
        action: 'userprofile',
        user: username
    }).then(function(data) {
        var profile = data.userprofile;
        updateProfileFields(card, {
            name: profile.name || '',
            discord: profile.discord || '',
            role: profile.role || '',
            bio: profile.bio || '',
            badges: profile.badges || ''
        });
    }).fail(function() {
        updateProfileFields(card, {
            name: '',
            discord: '',
            role: '',
            bio: '',
            badges: ''
        });
    });
}

function updateProfileFields(card, data) {
    var nameEl = card.querySelector('[data-field="name"]');
    var roleEl = card.querySelector('[data-field="role"]');
    var discordEl = card.querySelector('[data-field="discord"]');
    var bioEl = card.querySelector('[data-field="bio"]');
    var badgesEl = card.querySelector('[data-field="badges"]');

    if (nameEl) nameEl.textContent = data.name || '';
    if (roleEl) roleEl.textContent = data.role || '';
    if (discordEl) discordEl.textContent = data.discord ? ('디스코드: ' + data.discord) : '';
    if (bioEl) bioEl.textContent = data.bio || '';

    if (badgesEl) {
        if (data.badges) {
            var badges = data.badges.split(',');
            var html = '';

            for (var i = 0; i < badges.length; i++) {
                html += '<span class="clbi-badge">' + badges[i].trim() + '</span>';
            }

            badgesEl.innerHTML = html;
        } else {
            badgesEl.innerHTML = '';
        }
    }
}
// ========== 프로필 시스템 끝 ==========

// ========== 알림 시스템 ==========
function ensureNotificationPopup() {
    if (document.getElementById('clbi-notification-popup')) return;

    var popup = document.createElement('div');
    popup.id = 'clbi-notification-popup';
    popup.style.cssText =
        'display:none;position:fixed;z-index:99999;width:320px;max-height:420px;' +
        'background:#0a0909;border:2px solid #854369;border-radius:10px;' +
        'box-shadow:0 0 0 1px #1a1a1a, 0 8px 24px rgba(0,0,0,0.55);overflow:hidden;';

    popup.innerHTML =
        '<div style="padding:10px 12px;border-bottom:2px solid #854369;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);color:#E2E2E2;font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:space-between;gap:8px;">' +
            '<span>알림</span>' +
            '<button type="button" id="clbi-notification-readall" style="background:#171717;border:1px solid #854369;border-radius:6px;color:#E2E2E2;font-size:11px;font-weight:700;padding:4px 8px;cursor:pointer;">전체 읽음</button>' +
        '</div>' +
        '<div id="clbi-notification-list" style="max-height:320px;overflow-y:auto;padding:8px 0;color:#E2E2E2;font-size:12px;">불러오는 중...</div>' +
        '<div style="padding:8px;border-top:1px solid #2a2a2a;background:#111;">' +
            '<a href="/index.php?title=Special:Notifications" id="clbi-notification-more" style="display:block;width:100%;text-align:center;padding:8px 10px;border-radius:6px;background:#171717;border:1px solid #854369;color:#E2E2E2 !important;text-decoration:none !important;font-size:12px;font-weight:700;">더보기</a>' +
        '</div>';

    document.body.appendChild(popup);
}

function positionNotificationPopup() {
    var btn = document.getElementById('clbi-notification-toggle');
    var popup = document.getElementById('clbi-notification-popup');
    if (!btn || !popup) return;

    var rect = btn.getBoundingClientRect();
    var top = rect.bottom + 2;
    var left = rect.right - popup.offsetWidth;

    if (left < 8) left = 8;
    if (left + popup.offsetWidth > window.innerWidth - 8) {
        left = window.innerWidth - popup.offsetWidth - 8;
    }
    if (top + popup.offsetHeight > window.innerHeight - 8) {
        top = Math.max(8, window.innerHeight - popup.offsetHeight - 8);
    }

    popup.style.top = top + 'px';
    popup.style.left = left + 'px';
}

function parseNotificationItemsFromHtml(html) {
    var parser = new DOMParser();
    var doc = parser.parseFromString(html, 'text/html');

    var selectors = [
        '.mw-echo-ui-notificationItemWidget',
        '.mw-echo-ui-notificationsInboxWidgetRow',
        '.echo-ui-notificationItemWidget',
        'li[data-notification-id]',
        '.mw-echo-notifications-list li'
    ];

    var items = [];
    for (var i = 0; i < selectors.length; i++) {
        items = Array.prototype.slice.call(doc.querySelectorAll(selectors[i]));
        if (items.length) break;
    }

    return items.slice(0, 5).map(function(item) {
        var link = item.querySelector('a[href]');
        var href = link ? link.getAttribute('href') : '/index.php?title=Special:Notifications';
        var text = (item.textContent || '').replace(/\s+/g, ' ').trim();

        var notificationId =
            item.getAttribute('data-notification-id') ||
            item.getAttribute('data-id') ||
            item.getAttribute('data-notification') ||
            '';

        if (!notificationId) {
            var anyWithId = item.querySelector('[data-notification-id], [data-id], [data-notification]');
            if (anyWithId) {
                notificationId =
                    anyWithId.getAttribute('data-notification-id') ||
                    anyWithId.getAttribute('data-id') ||
                    anyWithId.getAttribute('data-notification') ||
                    '';
            }
        }

        if (href && href.indexOf('http') !== 0) {
            href = href.charAt(0) === '/'
                ? href
                : '/index.php' + (href.charAt(0) === '?' ? href : '/' + href);
        }

        return {
            id: notificationId,
            href: href,
            text: text || '알림'
        };
    });
}

function renderNotificationPopup(items) {
    var list = document.getElementById('clbi-notification-list');
    var badge = document.getElementById('clbi-notification-badge');
    if (!list) return;

    if (!items || !items.length) {
        list.innerHTML = '<div style="padding:14px 12px;color:#999;">표시할 알림이 없습니다.</div>';
        if (badge) badge.style.display = 'none';
        return;
    }

    var html = '';
    for (var i = 0; i < items.length; i++) {
        html +=
            '<a href="' + items[i].href + '" class="clbi-notification-item" data-notification-id="' + (items[i].id || '') + '" style="display:block;padding:10px 12px;color:#E2E2E2 !important;text-decoration:none !important;border-bottom:1px solid #1f1f1f;line-height:1.5;">' +
                items[i].text +
            '</a>';
    }
    list.innerHTML = html;

    if (badge) {
        badge.textContent = items.length;
        badge.style.display = 'block';
    }
}

function loadNotificationsIntoPopup() {
    var list = document.getElementById('clbi-notification-list');
    if (list) {
        list.innerHTML = '<div style="padding:14px 12px;color:#999;">불러오는 ...</div>';
    }

    fetch('/index.php?title=Special:Notifications', { credentials: 'same-origin' })
        .then(function(res) {
            return res.text();
        })
        .then(function(html) {
            var items = parseNotificationItemsFromHtml(html);
            renderNotificationPopup(items);
        })
        .catch(function(err) {
            console.error(err);
            if (list) {
                list.innerHTML = '<div style="padding:14px 12px;color:#999;">알림을 불러오지 못했습니다.</div>';
            }
        });
}

function markAllNotificationsRead() {
    return new mw.Api().postWithToken('csrf', {
        action: 'echomarkread',
        list: 'all'
    });
}

function markNotificationReadById(notificationId) {
    if (!notificationId) {
        return $.Deferred().resolve().promise();
    }

    return new mw.Api().postWithToken('csrf', {
        action: 'echomarkread',
        list: notificationId
    });
}

function initNotifications() {
    var btn = document.getElementById('clbi-notification-toggle');
    if (!btn) return;

    ensureNotificationPopup();
    loadNotificationsIntoPopup();

    $(document)
        .off('click.clbiNotificationToggle')
        .on('click.clbiNotificationToggle', '#clbi-notification-toggle', function(e) {
            e.preventDefault();
            e.stopPropagation();

            var popup = document.getElementById('clbi-notification-popup');
            var playlistPopup = document.getElementById('clbi-playlist-popup');
            if (!popup) return;

            if (playlistPopup) {
                playlistPopup.style.display = 'none';
            }

            if (popup.style.display === 'none' || popup.style.display === '') {
                popup.style.display = 'block';
                positionNotificationPopup();
                loadNotificationsIntoPopup();
            } else {
                popup.style.display = 'none';
            }
        });

    $(document)
        .off('click.clbiNotificationOutside')
        .on('click.clbiNotificationOutside', function(e) {
            var popup = document.getElementById('clbi-notification-popup');
            var toggle = document.getElementById('clbi-notification-toggle');
            if (!popup || !toggle) return;

            if (!popup.contains(e.target) && !toggle.contains(e.target)) {
                popup.style.display = 'none';
            }
        });

    $(document)
        .off('click.clbiNotificationReadAll')
        .on('click.clbiNotificationReadAll', '#clbi-notification-readall', function(e) {
            e.preventDefault();
            e.stopPropagation();

            var button = this;
            button.disabled = true;
            button.textContent = '처리 ...';

            markAllNotificationsRead()
                .then(function() {
                    loadNotificationsIntoPopup();
                })
                .always(function() {
                    button.disabled = false;
                    button.textContent = '전체 읽음';
                });
        });

    $(document)
        .off('click.clbiNotificationItem')
        .on('click.clbiNotificationItem', '.clbi-notification-item', function(e) {
            e.preventDefault();
            e.stopPropagation();

            var href = this.getAttribute('href');
            var notificationId = this.getAttribute('data-notification-id') || '';

            markNotificationReadById(notificationId).always(function() {
                loadNotificationsIntoPopup();
                if (href) {
                    window.location.href = href;
                }
            });
        });

    $(window)
        .off('resize.clbiNotification')
        .on('resize.clbiNotification', function() {
            var popup = document.getElementById('clbi-notification-popup');
            if (popup && popup.style.display === 'block') {
                positionNotificationPopup();
            }
        });
}
// ========== 알림 시스템 끝 ==========

// ========== 플레이리스트 팝업 시스템 ==========
function ensurePlaylistPopup() {
    if (document.getElementById('clbi-playlist-popup')) return;

    var popup = document.createElement('div');
    popup.id = 'clbi-playlist-popup';
    popup.style.cssText =
        'display:none;position:fixed;z-index:99999;width:690px;height:540px;' +
        'background:#0a0909;border:2px solid #854369;border-radius:10px;' +
        'box-shadow:0 0 0 1px #1a1a1a, 0 8px 24px rgba(0,0,0,0.55);overflow:hidden;';

    popup.innerHTML =
        '<div style="padding:10px 12px;border-bottom:2px solid #854369;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);color:#E2E2E2;font-size:13px;font-weight:700;">' +
            '<span>플레이리스트</span>' +
        '</div>' +
        '<div style="padding:0;background:#111;height:calc(100% - 42px);">' +
            '<iframe style="border:none;width:100%;height:100%;" src="https://open.spotify.com/embed/playlist/32l4ke6djdQn8LoBp1ipR9?utm_source=generator&theme=0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>' +
        '</div>';

    document.body.appendChild(popup);
}

function positionPlaylistPopup() {
    var btn = document.getElementById('clbi-playlist-toggle');
    var popup = document.getElementById('clbi-playlist-popup');
    if (!btn || !popup) return;

    var rect = btn.getBoundingClientRect();
    var top = rect.bottom + 2;
    var left = rect.right - popup.offsetWidth;

    if (left < 8) left = 8;
    if (left + popup.offsetWidth > window.innerWidth - 8) {
        left = window.innerWidth - popup.offsetWidth - 8;
    }
    if (top + popup.offsetHeight > window.innerHeight - 8) {
        top = Math.max(8, window.innerHeight - popup.offsetHeight - 8);
    }

    popup.style.top = top + 'px';
    popup.style.left = left + 'px';
}

function initPlaylistPopup() {
    var btn = document.getElementById('clbi-playlist-toggle');
    if (!btn) return;

    ensurePlaylistPopup();

    $(document)
        .off('click.clbiPlaylistToggle')
        .on('click.clbiPlaylistToggle', '#clbi-playlist-toggle', function(e) {
            e.preventDefault();
            e.stopPropagation();

            var popup = document.getElementById('clbi-playlist-popup');
            var notificationPopup = document.getElementById('clbi-notification-popup');
            if (!popup) return;

            if (notificationPopup) {
                notificationPopup.style.display = 'none';
            }

            if (popup.style.display === 'none' || popup.style.display === '') {
                popup.style.display = 'block';
                positionPlaylistPopup();
            } else {
                popup.style.display = 'none';
            }
        });

    $(document)
        .off('click.clbiPlaylistOutside')
        .on('click.clbiPlaylistOutside', function(e) {
            var popup = document.getElementById('clbi-playlist-popup');
            var toggle = document.getElementById('clbi-playlist-toggle');
            if (!popup || !toggle) return;

            if (!popup.contains(e.target) && !toggle.contains(e.target)) {
                popup.style.display = 'none';
            }
        });

    $(window)
        .off('resize.clbiPlaylist')
        .on('resize.clbiPlaylist', function() {
            var popup = document.getElementById('clbi-playlist-popup');
            if (popup && popup.style.display === 'block') {
                positionPlaylistPopup();
            }
        });
}
// ========== 플레이리스트 팝업 시스템  ==========

function initUserProfilePage() {
    var saveBtn = document.getElementById('pref-save');
    if (!saveBtn) return;

    var adminEl = document.getElementById('pref-is-admin');
    var isAdmin = adminEl && adminEl.getAttribute('data-admin') === '1';
    var api = new mw.Api();
    var selectedFile = null;
    var cropper = null;

    if (!document.getElementById('clbi-gallery-modal')) {
        var gModal = document.createElement('div');
        gModal.id = 'clbi-gallery-modal';
        gModal.style.cssText =
            'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';

        gModal.innerHTML =
            '<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:480px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
                '<div style="display:flex;justify-content:space-between;align-items:center;">' +
                    '<span style="font-size:14px;font-weight:700;color:#e2e2e2;">프로필 사진 선택</span>' +
                    '<button type="button" id="clbi-gallery-close" style="background:none;border:none;color:#aaa;font-size:18px;cursor:pointer;">✕</button>' +
                '</div>' +
                '<button type="button" id="clbi-gallery-upload-btn" style="background:#2a2a2a;border:2px dashed #854369;border-radius:8px;padding:32px;color:#e2e2e2;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:8px;font-size:13px;width:100%;">' +
                    '<span style="font-size:32px;">🖼️</span>새 사진 업로드' +
                '</button>' +
                '<div id="clbi-gallery-history-section" style="display:none;">' +
                    '<div style="font-size:11px;color:#888;margin-bottom:8px;">이전 사진  클릭하면 바로 적용</div>' +
                    '<div id="clbi-gallery-history" style="display:flex;gap:8px;flex-wrap:wrap;"></div>' +
                '</div>' +
            '</div>';

        document.body.appendChild(gModal);
    }

    if (!document.getElementById('clbi-crop-modal')) {
        var cModal = document.createElement('div');
        cModal.id = 'clbi-crop-modal';
        cModal.style.cssText =
            'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';

        cModal.innerHTML =
            '<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:500px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
                '<div style="font-size:14px;font-weight:700;color:#e2e2e2;">사진 조정</div>' +
                '<div style="width:100%;max-height:380px;overflow:hidden;border-radius:8px;">' +
                    '<img id="clbi-crop-image" style="max-width:100%;">' +
                '</div>' +
                '<div style="display:flex;gap:8px;justify-content:flex-end;">' +
                    '<button type="button" id="clbi-crop-cancel" style="background:#2a2a2a;color:#e2e2e2;border:1px solid #444;padding:8px 16px;border-radius:6px;cursor:pointer;">취소</button>' +
                    '<button type="button" id="clbi-crop-confirm" style="background:#854369;color:#fff;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;">확정</button>' +
                '</div>' +
            '</div>';

        document.body.appendChild(cModal);
    }

    var gModal = document.getElementById('clbi-gallery-modal');
    var cModal = document.getElementById('clbi-crop-modal');
    var cropImage = document.getElementById('clbi-crop-image');
    var pfpInput = document.getElementById('pref-pfp-input');

    function openGallery() {
        gModal.style.display = 'flex';

        var username = mw.config.get('wgUserName');
        api.get({
            action: 'query',
            titles: '파일:Pfp-' + username + '.png',
            prop: 'imageinfo',
            iiprop: 'url|timestamp',
            iilimit: 6
        }).then(function(data) {
            var pages = data.query.pages;
            var page = pages[Object.keys(pages)[0]];
            if (!page.imageinfo || page.imageinfo.length === 0) return;

            var historyEl = document.getElementById('clbi-gallery-history');
            var sectionEl = document.getElementById('clbi-gallery-history-section');
            historyEl.innerHTML = '';

            page.imageinfo.forEach(function(info, idx) {
                var wrap = document.createElement('div');
                wrap.style.cssText = 'position:relative;cursor:pointer;';

                var img = document.createElement('img');
                img.src = info.url;
                img.style.cssText =
                    'width:72px;height:72px;object-fit:cover;border-radius:8px;border:2px solid #444;flex-shrink:0;';

                if (idx === 0) {
                    img.style.borderColor = '#854369';
                    var badge = document.createElement('div');
                    badge.textContent = '현재';
                    badge.style.cssText =
                        'position:absolute;bottom:4px;left:50%;transform:translateX(-50%);background:#854369;color:#fff;font-size:9px;padding:1px 6px;border-radius:10px;';
                    wrap.appendChild(badge);
                }

                img.addEventListener('mouseenter', function() {
                    if (idx !== 0) img.style.borderColor = '#854369';
                });

                img.addEventListener('mouseleave', function() {
                    if (idx !== 0) img.style.borderColor = '#444';
                });

                img.addEventListener('click', function() {
                    fetch(info.url)
                        .then(function(r) {
                            return r.blob();
                        })
                        .then(function(blob) {
                            selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
                            document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
                            gModal.style.display = 'none';
                            document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
                        });
                });

                wrap.appendChild(img);
                historyEl.appendChild(wrap);
            });

            sectionEl.style.display = 'block';
        });
    }

    function openCrop(src) {
        cropImage.src = src;
        cModal.style.display = 'flex';

        if (cropper) {
            cropper.destroy();
            cropper = null;
        }

        setTimeout(function() {
            cropper = new Cropper(cropImage, {
                aspectRatio: 1,
                viewMode: 1,
                dragMode: 'move',
                autoCropArea: 0.8,
                cropBoxResizable: true,
                cropBoxMovable: true
            });
        }, 150);
    }

    document.getElementById('pref-pfp-btn').addEventListener('click', function() {
        openGallery();
    });

    document.getElementById('clbi-gallery-upload-btn').addEventListener('click', function() {
        pfpInput.click();
    });

    document.getElementById('clbi-gallery-close').addEventListener('click', function() {
        gModal.style.display = 'none';
    });

    pfpInput.addEventListener('change', function() {
        var file = this.files[0];
        if (!file) return;

        gModal.style.display = 'none';

        var reader = new FileReader();
        reader.onload = function(e) {
            openCrop(e.target.result);
        };
        reader.readAsDataURL(file);
    });

    document.getElementById('clbi-crop-cancel').addEventListener('click', function() {
        cModal.style.display = 'none';
        if (cropper) {
            cropper.destroy();
            cropper = null;
        }
        pfpInput.value = '';
    });

    document.getElementById('clbi-crop-confirm').addEventListener('click', function() {
        if (!cropper) return;

        var canvas = cropper.getCroppedCanvas({ width: 256, height: 256 });
        if (!canvas) return;

        canvas.toBlob(function(blob) {
            selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
            document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
            cModal.style.display = 'none';
            cropper.destroy();
            cropper = null;
            document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
        }, 'image/png');
    });

    var emailSaveBtn = document.getElementById('pref-email-save');
    if (emailSaveBtn) {
        emailSaveBtn.addEventListener('click', function() {
            var statusEl = document.getElementById('pref-email-status');
            var newEmail = document.getElementById('pref-new-email').value;
            var password = document.getElementById('pref-email-password').value;

            if (!newEmail || !password) {
                statusEl.textContent = '이메일과 비밀번호를 입력해주세요.';
                return;
            }

            statusEl.textContent = '변경 중...';

            api.postWithToken('csrf', {
                action: 'changeemail',
                email: newEmail,
                password: password
            }).then(function() {
                statusEl.textContent = '✓ 이메일 변경됨';
                document.getElementById('pref-new-email').value = '';
                document.getElementById('pref-email-password').value = '';

                setTimeout(function() {
                    statusEl.textContent = '';
                }, 3000);
            }).fail(function(code, data) {
                var msg = data && data.error && data.error.info ? data.error.info : '변경 실패';
                statusEl.textContent = msg;
            });
        });
    }

    saveBtn.addEventListener('click', function() {
        var statusEl = document.getElementById('pref-status');
        statusEl.textContent = '저장 중...';

        var promises = [];

        if (selectedFile) {
            var username = mw.config.get('wgUserName');
            promises.push(
                api.postWithToken('csrf', {
                    action: 'upload',
                    filename: 'Pfp-' + username + '.png',
                    ignorewarnings: true,
                    file: selectedFile,
                    format: 'json'
                }, {
                    contentType: 'multipart/form-data'
                })
            );
        }

        var fields = ['name', 'discord', 'role', 'bio'];
        if (isAdmin) fields.push('badges');

        for (var i = 0; i < fields.length; i++) {
            var el = document.getElementById('pref-' + fields[i]);
            if (!el) continue;

            promises.push(
                api.postWithToken('csrf', {
                    action: 'options',
                    optionname: 'profile-' + fields[i],
                    optionvalue: el.value
                })
            );
        }

        $.when.apply($, promises)
            .then(function() {
                statusEl.textContent = '✓ 저장됨';
                selectedFile = null;
                document.getElementById('pref-pfp-btn').textContent = '사진 선택';

                setTimeout(function() {
                    statusEl.textContent = '';
                }, 2000);
            })
            .fail(function() {
                statusEl.textContent = '저장 실패';
            });
    });
}

/* =========================================
   Banner / CRT Page Monitor thumbnail slices
   - base 이미지는 틀에 들어간 파일 문법 그대로 사용
   - slice 레이어에는 300px MediaWiki 썸네일만 삽입
   ========================================= */

(function ($, mw) {
    var thumbCache = {};

    function parseSliceWidth(value) {
        var parsed = parseInt(value, 10);

        if (!isFinite(parsed) || parsed < 120) {
            return 300;
        }

        return parsed;
    }

    function getImageSrc(img) {
        return img ? (img.currentSrc || img.getAttribute('src') || img.src || '') : '';
    }

    function getFileNameFromSrc(src) {
        var a;
        var parts;
        var fileName;

        if (!src) return '';

        a = document.createElement('a');
        a.href = src;

        parts = (a.pathname || '').split('/').filter(function (part) {
            return !!part;
        });

        if (!parts.length) return '';

        fileName = parts.pop();

        /*
         * MediaWiki thumb URL 예시:
         * /images/thumb/a/ab/File.png/1000px-File.png
         * /images/thumb/a/ab/File.svg/1000px-File.svg.png
         *
         * 이 경우 실제 파일명은 마지막 조각이 아니라 그 앞 조각이다.
         */
        if (/^\d+px-/.test(fileName) && parts.length) {
            fileName = parts.pop();
        }

        fileName = fileName.replace(/^\d+px-/, '');

        try {
            fileName = decodeURIComponent(fileName);
        } catch (e) {}

        return fileName;
    }

    function resolveThumbUrl(img, width, callback) {
        var src = getImageSrc(img);
        var fileName = getFileNameFromSrc(src);
        var cacheKey;
        var entry;

        if (!src) return;

        if (!fileName || !mw || !mw.loader) {
            callback(src);
            return;
        }

        cacheKey = fileName + '|' + width;
        entry = thumbCache[cacheKey];

        if (entry) {
            if (entry.resolved) {
                callback(entry.url || src);
            } else {
                entry.callbacks.push(callback);
            }
            return;
        }

        entry = {
            resolved: false,
            url: '',
            callbacks: [callback]
        };

        thumbCache[cacheKey] = entry;

        function finish(url) {
            var callbacks = entry.callbacks.slice();
            var i;

            entry.resolved = true;
            entry.url = url || src;
            entry.callbacks = [];

            for (i = 0; i < callbacks.length; i++) {
                callbacks[i](entry.url);
            }
        }

        mw.loader.using('mediawiki.api').done(function () {
            var api = new mw.Api();

            api.get({
                action: 'query',
                titles: 'File:' + fileName,
                prop: 'imageinfo',
                iiprop: 'url',
                iiurlwidth: width,
                formatversion: 2
            }).done(function (data) {
                var page;
                var info;

                if (
                    data &&
                    data.query &&
                    data.query.pages &&
                    data.query.pages.length
                ) {
                    page = data.query.pages[0];

                    if (
                        page &&
                        page.imageinfo &&
                        page.imageinfo.length
                    ) {
                        info = page.imageinfo[0];
                    }
                }

                finish((info && (info.thumburl || info.url)) || src);
            }).fail(function () {
                finish(src);
            });
        }).fail(function () {
            finish(src);
        });
    }

    function applySliceImages(frame, thumbUrl) {
        var slices;
        var i;
        var img;

        if (!frame || !thumbUrl) return;

        slices = frame.querySelectorAll('.crt-page-monitor-slice');

        for (i = 0; i < slices.length; i++) {
            slices[i].innerHTML = '';

            img = document.createElement('img');
            img.className = 'crt-page-monitor-slice-img';
            img.src = thumbUrl;
            img.alt = '';
            img.decoding = 'async';
            img.loading = 'eager';
            img.setAttribute('aria-hidden', 'true');

            slices[i].appendChild(img);
        }

        frame.setAttribute('data-crt-slices-ready', '1');
    }

    function initBannerFrame(frame) {
        var baseImg;
        var width;

        if (!frame) return;
        if (frame.getAttribute('data-crt-slices-ready') === '1') return;

        baseImg = frame.querySelector('.crt-page-monitor-image-base img');

        if (!baseImg) return;

        width = parseSliceWidth(frame.getAttribute('data-crt-slice-width'));

        resolveThumbUrl(baseImg, width, function (thumbUrl) {
            if (!frame || !frame.parentNode) return;
            applySliceImages(frame, thumbUrl);
        });
    }

    function initBannerFrames(root) {
        var scope = root && root.querySelectorAll ? root : document;
        var frames = scope.querySelectorAll('.crt-page-monitor-frame');
        var i;

        for (i = 0; i < frames.length; i++) {
            initBannerFrame(frames[i]);
        }
    }

    $(function () {
        initBannerFrames(document);
    });

    if (mw && mw.hook) {
        mw.hook('wikipage.content').add(function ($content) {
            initBannerFrames($content && $content[0] ? $content[0] : document);
        });
    }
})(jQuery, window.mw);

/* =========================================
   Doc Tab System — Q/E 단축키 탭 전환
   글리치 플리커 + RGB split + 방향 슬라이드
   ========================================= */

(function () {
    'use strict';

var keydownBound = false;

    function initDocTabs() {
        var tabBars = document.querySelectorAll('.doc-tab-bar');
        if (!tabBars.length) return;

        tabBars.forEach(function (bar) {
            if (bar.getAttribute('data-tabs-init')) return;
            bar.setAttribute('data-tabs-init', '1');

            var tabs = Array.from(bar.querySelectorAll('.doc-tab'));
            if (!tabs.length) return;

            var panel = bar.closest('.doc-panel');
            var display = panel ? panel.querySelector('.doc-display') : null;
            if (!display) display = document.getElementById('doc-main-display');
            if (!display) return;

            tabs.forEach(function (tab, i) {
                tab.addEventListener('click', function () {
                    var currentIdx = tabs.findIndex(function (t) {
                        return t.classList.contains('active');
                    });
                    if (currentIdx === i) return;
                    switchTab(tabs, display, i, i > currentIdx ? 1 : -1);
                });
            });

            var initIdx = tabs.findIndex(function (t) { return t.classList.contains('active'); });
            if (initIdx !== -1) {
                var initRef = tabs[initIdx].dataset.ref;
                var initEl = initRef ? document.getElementById(initRef) : null;
                display.innerHTML = initEl ? initEl.innerHTML : (tabs[initIdx].dataset.content || '');
            }
        });

        if (!keydownBound) {
            keydownBound = true;
            document.addEventListener('keydown', function (e) {
                var tag = document.activeElement.tagName;
                if (tag === 'INPUT' || tag === 'TEXTAREA') return;

                var bar = document.querySelector('.doc-tab-bar');
                if (!bar) return;
                var tabs = Array.from(bar.querySelectorAll('.doc-tab'));

                var panel = bar.closest('.doc-panel');
                var display = panel ? panel.querySelector('.doc-display') : null;
                if (!display) display = document.getElementById('doc-main-display');
                if (!display) return;

                var activeIdx = tabs.findIndex(function (t) {
                    return t.classList.contains('active');
                });
                if (activeIdx === -1) return;

                if (e.key === 'q' || e.key === 'Q') {
                    e.preventDefault();
                    var prev = (activeIdx - 1 + tabs.length) % tabs.length;
                    if (prev !== activeIdx) switchTab(tabs, display, prev, -1);
                } else if (e.key === 'e' || e.key === 'E') {
                    e.preventDefault();
                    var next = (activeIdx + 1) % tabs.length;
                    if (next !== activeIdx) switchTab(tabs, display, next, 1);
                }
            });
        }
    }

    var isAnimating = false;

    function switchTab(tabs, display, nextIdx, dir) {
        if (isAnimating) return;
        isAnimating = true;

        tabs.forEach(function (t) { t.classList.remove('active'); });
        tabs[nextIdx].classList.add('active');

        var ref = tabs[nextIdx].dataset.ref;
        var nextContent;
        if (ref) {
            var refEl = document.getElementById(ref);
            nextContent = refEl ? refEl.innerHTML : '';
        } else {
            nextContent = tabs[nextIdx].dataset.content || '';
        }

        glitchOut(display, dir, function () {
            display.innerHTML = nextContent;
            glitchIn(display, dir, function () {
                isAnimating = false;
            });
        });
    }

    function glitchOut(el, dir, cb) {
        var duration = 160;
        var start = null;
        var slideX = dir * 16;

        function step(ts) {
            if (!start) start = ts;
            var p = Math.min((ts - start) / duration, 1);
            var ease = p * p;

            var tx = slideX * ease;
            var skew = dir * ease * 1.0;
            var opacity = 1 - ease;
            var rgb = ease * 5;

            el.style.transform = 'translateX(' + tx + 'px) skewX(' + skew + 'deg)';
            el.style.opacity = opacity;
            el.style.filter =
                'drop-shadow(' + (-rgb) + 'px 0 0 rgba(80,160,255,0.75)) ' +
                'drop-shadow(' + rgb + 'px 0 0 rgba(255,55,90,0.65)) ' +
                'brightness(' + (1 + ease * 0.25) + ')';

            if (p < 1) {
                requestAnimationFrame(step);
            } else {
                el.style.opacity = '0';
                cb();
            }
        }
        requestAnimationFrame(step);
    }

    function glitchIn(el, dir, cb) {
        var duration = 200;
        var start = null;
        var startX = -dir * 16;

        el.style.transform = 'translateX(' + startX + 'px) skewX(' + (-dir * 1.0) + 'deg)';
        el.style.opacity = '0';

        function step(ts) {
            if (!start) start = ts;
            var p = Math.min((ts - start) / duration, 1);
            var ease = 1 - Math.pow(1 - p, 3);

            var tx = startX * (1 - ease);
            var skew = -dir * 1.0 * (1 - ease);
            var opacity = ease;
            var rgb = (1 - ease) * 3;
            var brightness = 1 + (1 - ease) * 0.35;

            el.style.transform = 'translateX(' + tx + 'px) skewX(' + skew + 'deg)';
            el.style.opacity = opacity;
            el.style.filter =
                'drop-shadow(' + (-rgb) + 'px 0 0 rgba(80,160,255,0.65)) ' +
                'drop-shadow(' + rgb + 'px 0 0 rgba(255,55,90,0.55)) ' +
                'brightness(' + brightness + ')';

            if (p < 1) {
                requestAnimationFrame(step);
            } else {
                el.style.transform = '';
                el.style.opacity = '';
                el.style.filter = '';
                cb();
            }
        }
        requestAnimationFrame(step);
    }

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initDocTabs);
} else {
    initDocTabs();
}

if (typeof mw !== 'undefined' && mw.hook) {
    mw.hook('wikipage.content').add(function () {
        initDocTabs();
    });
}

})();

/* =========================================
   Doc Section Switch — 좌측 섹션 전환
   ========================================= */

$(document).on('click', '.doc-nav-item[data-section]', function () {
    var name = $(this).attr('data-section');
    var display = document.getElementById('doc-main-display');
    var titleEl = document.getElementById('doc-center-title');
    var tabBar = document.getElementById('doc-tab-bar-text');

    if (!display) return;

    $('.doc-nav-item[data-section]').removeClass('active');
    $('.doc-nav-item[data-section="' + name + '"]').addClass('active');

    if (name === 'text') {
        if (titleEl) titleEl.textContent = '개요';
        if (tabBar) $(tabBar).show();
        var activeTab = tabBar ? tabBar.querySelector('.doc-tab.active') : null;
        if (!activeTab && tabBar) activeTab = tabBar.querySelector('.doc-tab');
        if (activeTab) {
            var ref = activeTab.dataset.ref;
            var refEl = ref ? document.getElementById(ref) : null;
            display.innerHTML = refEl ? refEl.innerHTML : (activeTab.dataset.content || '');
        }
    } else {
        if (titleEl) titleEl.textContent = name === 'factions' ? '세력' : name === 'people' ? '인물' : name;
        if (tabBar) $(tabBar).hide();
        var refEl = document.getElementById('doc-content-' + name);
        display.innerHTML = refEl ? refEl.innerHTML : '';
    }
});