/*
  Create user/res/css/custom.css
  in project folder to override this file.
*/

/* ============================================================
   치지직 곡받이 커스터마이즈
   ============================================================ */

/* 전체 폰트를 Noto Sans 계열로 통일 (한글: Noto Sans KR) */
body {
  font-family: "Noto Sans KR", "Noto Sans", system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

/* Leader 버튼 숨김 (재생 제어는 admin이 자동 리더로 담당) */
#leader_btn { display: none !important; }

/* ===== 넓은 PC 화면 여백 =====
   관리자: 방송 중 오른쪽 하단에 캠·채팅 오버레이를 두는 경우가 많아
   왼쪽보다 오른쪽 여백을 크게 비대칭으로 둠.
   뷰어: 순수 신청 UI라 데스크탑에서도 굳이 넓을 필요 없어 고정 폭으로 제한.
   실제로 스크롤되는 건 body가 아니라 안쪽 #video(overflow:auto)라서, 여백을
   body에 주면 스크롤바가 창 끝이 아니라 여백 경계에 붙어버림. 그래서 #video
   자체는 풀 폭으로 두고(스크롤바는 항상 창 끝) 그 안쪽에 여백을 줌.
   뷰어는 #player가 아예 display:none이라 #playlist에만 주면 됨. */
@media (min-width: 1600px) {
  body.admin-mode #video {
    /* 하단 8vh: 우승 후보란이 맨 아래라서, 끝까지 스크롤하면 마지막 카드가
       브라우저 바닥에 딱 붙어버렸다. 후보 카드 한 줄 높이(약 82px)만큼
       비워 두면 목록이 끝났다는 게 눈에 들어온다. */
    padding: 3% 24% 8vh 3%;
  }
  /* 신청곡 대기열 + 재생 기록을 오른쪽 빈 여백에 고정 사이드바로.
     하단 39%(61vh 아래)는 방송용 VTuber 모델 자리로 비워둠.
     top은 3vh가 아니라 3vw: 플레이어 쪽 여백이 #video의 padding:3%인데 CSS의
     padding % 는 높이가 아니라 '너비' 기준이라, 같은 3%로 맞추려면 vw여야
     플레이어 상단과 사이드바 상단이 실제로 나란해짐. */
  body.admin-mode #admin-sidebar {
    position: fixed;
    top: 3vw;
    right: 1.5vw;
    /* 우측 여백(right)은 그대로 두고 폭만 20px 넓혀서, 왼쪽 끝이 플레이어 쪽으로
       20px 더 붙게 함 */
    width: calc(21vw + 20px);
    /* 상단 여백(3vw)만큼 빼줘야 실제 아래 끝이 100vh-39vh 위에서 멈춤 -
       그냥 61vh로 두면 시작점이 내려간 만큼 하단 여백이 39%보다 좁아짐.
       max-height가 아니라 height: 아래 리사이즈 손잡이(#admin-sidebar-resize)로
       드래그하면 JS가 이 값을 px로 직접 덮어쓴다 - max-height는 내용이 적을 때
       줄어들어버려서 드래그로 늘린 높이가 유지되지 않는다. */
    height: calc(61vh - 3vw);
    min-height: 220px;
    max-height: calc(100vh - 3vw - 12px);
    /* NEXT SONG은 항상 위에 고정하고 그 아래 목록만 스크롤되게:
       사이드바 자체는 스크롤하지 않고(overflow:hidden) 안쪽 목록 래퍼만 스크롤.
       sticky를 쓰면 반투명 배경 위로 아래 내용이 비쳐 보여서 이 방식이 깔끔함. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 12px;
    border: 1px solid rgba(127, 127, 127, 0.25);
    border-radius: 10px;
    background: rgba(20, 20, 20, 0.55);
    backdrop-filter: blur(4px);
  }
  /* 고정된 NEXT SONG 아래, 실제로 스크롤되는 영역. min-height:0 이 있어야
     flex 자식이 내용 높이만큼 부풀지 않고 남은 공간 안에서 스크롤됨. */
  body.admin-mode #admin-sidebar-scroll {
    overflow-y: auto;
    min-height: 0;
    flex: 1 1 auto;
  }
  body.admin-mode #sidebar-next-wrap { flex: 0 0 auto; }
  /* NEXT SONG+대기열 전체 창 높이를 드래그로 조절하는 손잡이. 고정 사이드바가
     있을 때만(넓은 관리자 화면) 의미가 있어 여기서만 보이게 한다. */
  body.admin-mode #admin-sidebar-resize { display: block; }
  /* 재생/삭제 버튼이 사라지고(제목 클릭/더블클릭으로 대체) 사이드바 폭도
     넓어져서 원래 썸네일+가로 배치 행이 다시 들어갈 여유가 생김.
     body.admin-mode로 명확히 한정: 안 그러면 뷰어 페이지도 넓은 화면에서
     같이 영향받아 폰트가 작아짐 (뷰어 대기열 디자인은 그대로 유지). */
  body.admin-mode #admin-sidebar .queue_entry > header h4 { font-size: 0.92rem; }
  body.admin-mode #admin-sidebar .qe_channel { font-size: 0.8rem; }
  body.admin-mode #admin-sidebar .qe_author { font-size: 0.82rem; }
}
/* 리사이즈 손잡이: 기본은 숨김(위 media query에서만 보임). #admin-sidebar의
   flex column 마지막 자식으로 두면 항상 맨 아래 가로줄로 자리잡는다.
   부모 padding(12px)만큼 음수 마진을 줘서 사이드바 폭 전체를 가로지르게 함. */
#admin-sidebar-resize {
  display: none;
  flex: 0 0 auto;
  height: 14px;
  margin: 4px -12px -12px;
  cursor: ns-resize;
  position: relative;
  touch-action: none;
}
#admin-sidebar-resize::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 36px; height: 4px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.25);
  transition: background-color 0.15s ease;
}
#admin-sidebar-resize:hover::after,
#admin-sidebar-resize.dragging::after { background: rgba(255, 255, 255, 0.6); }
/* 좁은 모바일: 제목 줄에 Add Song이 안 들어가 어중간하게 밀려 내려오므로,
   아예 아래 줄 전체 폭을 쓰는 기본 동작 버튼으로 떨어뜨려 의도된 배치로 보이게 함 */
@media (max-width: 520px) {
  body.remote-mode .playlist-info > .controls { width: 100%; margin-top: 10px; }
  body.remote-mode #showmediaurl {
    width: 100% !important;
    justify-content: center;
  }
}
@media (min-width: 700px) {
  body.remote-mode #playlist {
    max-width: 640px;
    margin: 0 auto;
  }
}

/* 채팅 UI 숨김 (설정 톱니바퀴 + 계정 메뉴는 유지) */
#userlist-wrap,
#messagebuffer,
#chat-inputs-wrapper { display: none !important; }

/* 로컬 파일 업로드 차단 (YouTube 링크만 허용) */
#mediaurl-upload,
#selectLocalVideoBtn { display: none !important; }

/* ===== 곡 추가 패널 정리 (묶음 1) ===== */
/* 선택 입력칸(제목/자막/보이스오버) 항상 제거 */
#mediatitleblock,
#subsurlblock,
#voiceoverblock { display: none !important; }

/* 재생목록 링크 콤마 복사(체인, #getplaylist) 버튼 숨김: 곡받이엔 불필요하고
   혼동을 줌(백업은 별도 backupBtn 사용). 요소는 남으니 복구는 이 줄만 제거.
   '>' 템플릿 삽입 버튼(#insert_template)은 로컬 테스트 편의를 위해 다시 노출. */
#getplaylist { display: none !important; }

/* Queue next 제거 → Add(구 Queue last) 한 버튼으로 통일 */
#queue_next,
#ce_queue_next { display: none !important; }
.queue-buttons #queue_end,
.queue-buttons #ce_queue_end { width: 100%; }

/* '+' 버튼을 'Add Song' 텍스트 버튼으로 (다른 버튼보다 30% 크게, 눈에 잘 띄도록) */
#showmediaurl {
  width: auto !important;
  padding: 7px 18px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  /* 인디고 → 바이올렛 그라데이션 (신청 + 버튼과 동일 계열로 통일) */
  background: linear-gradient(135deg, #5b8cff, #a855f7);
  color: #fff;
  border: none;
}
#showmediaurl:hover { background: linear-gradient(135deg, #4a7bf0, #9333ea); }
#showmediaurl ion-icon { font-size: 1.56rem; color: inherit; }
/* 관리자는 곡 추가를 설정(톱니바퀴) 드로어의 '클립보드에서 곡 추가'로 처리하므로
   헤더의 Add Song 토글과 URL 입력 패널을 쓰지 않음 */
.admin-mode #showmediaurl { display: none !important; }
.add-song-label { font-size: 1.17rem; }

/* 곡 추가: 입력칸과 한 줄에 컴팩트한 + 버튼 */
#addfromurl .display-flex { align-items: stretch; gap: 8px; }
#addfromurl .mediaurl-wrap { flex: 1; }
/* 신청(+) 버튼: Add Song과 같은 인디고→바이올렛 그라데이션으로 통일 */
#queue_end.add-song-submit {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; width: 48px; padding: 0;
  background: linear-gradient(135deg, #5b8cff, #a855f7);
  color: #fff; border: none; border-radius: 10px;
  font-size: 1.5rem; cursor: pointer;
}
#queue_end.add-song-submit ion-icon { font-size: 1.45rem; color: inherit; }
#queue_end.add-song-submit:hover { background: linear-gradient(135deg, #4a7bf0, #9333ea); }
/* 하단의 비어있는 예전 버튼 영역 제거 */
#addfromurl .queue-buttons { display: none !important; }

/* admin도 전체폭. 설정은 톱니바퀴(플레이어 상단) → 우측 드로어 오버레이 */
body.admin-mode { grid-template-columns: 1fr 0 0 !important; }
body.admin-mode .gutter { display: none !important; }
/* 설정은 톱니바퀴 버튼에서 말풍선처럼 떠오름. 실제 좌표(right/bottom)는 열 때
   버튼 위치를 재서 JS가 지정하고(Buttons.positionOptionsPopover), 여기서는
   기본 형태만 잡아둠 - 창 우측 끝에 붙어 있으면 넓은 여백 때문에 버튼에서
   한참 떨어진 곳에서 열리는 느낌이 나서. */
body.admin-mode #chat {
  position: fixed; top: auto; right: 0; bottom: 0;
  width: min(320px, 90vw); height: auto;
  z-index: 900; pointer-events: none;
  background: transparent !important; border: none !important;
}
/* 채팅 헤더 잔재(온라인 수 등) 숨김 */
body.admin-mode #chat > .controls { display: none !important; }
/* 말풍선이 설정 패널 높이에 딱 맞게 줄어들도록, 채팅 쪽 잔재(메시지 버퍼·유저
   목록 등)를 모두 비움. 안 그러면 #chat이 화면 전체 높이로 남아 말풍선이
   위로 넘쳐 톱니바퀴에서 멀리 떨어져 보임. */
body.admin-mode #chat > *:not(#chat-body) { display: none !important; }
body.admin-mode #chat-body > *:not(#optionsPanel) { display: none !important; }
body.admin-mode #chat,
body.admin-mode #chat-body {
  height: auto !important;
  min-height: 0;
  flex: 0 0 auto;
  padding-bottom: 0;
  display: block;
}
/* 설정 패널이 열렸을 때만 클릭 가능(닫히면 플레이어 클릭 통과) */
body.admin-mode #optionsPanel:not(.collapse) { pointer-events: auto; }

/* ============================================================
   리모컨 모드 (비관리자 = 곡 신청 전용 페이지)
   ============================================================ */

#remote-header { display: none; }

body.remote-mode { grid-template-columns: 1fr 0 0 !important; }
.remote-mode #chat,
.remote-mode .gutter { display: none !important; }
.remote-mode #player { display: none !important; }

/* 뷰어에게 불필요한 고급 재생목록 컨트롤 숨김 */
.remote-mode #lockplaylist,
.remote-mode #getplaylist,
.remote-mode #playlist-menu-btn,
.remote-mode #insert_template,
.remote-mode #customembed,
.remote-mode .checkbox-row { display: none !important; }

/* 큐 아이템: 뷰어는 삭제만 가능 (재생/순서변경/잠금 버튼 숨김) */
.remote-mode .qbtn-play,
.remote-mode .qbtn-next,
.remote-mode .qbtn-tmp { display: none !important; }

/* 삭제(휴지통) 버튼은 본인이 신청한 곡에만 표시 (남의 곡엔 숨김) */
.remote-mode .queue_entry:not(.qe-own) .qbtn-delete { display: none !important; }

/* 관리자는 재생/삭제 버튼 대신 제목 클릭(즉시재생)/더블클릭(삭제)으로 조작 -
   버튼은 숨기고 동작은 client 쪽 JS에서 isAdmin() 분기로 처리 (Player.hx). */
.admin-mode #queue .controls { display: none !important; }
.admin-mode .qe_title { cursor: pointer; }

/* ===== 지금 / NEXT SONG 배너 (묶음4-2b) ===== */
/* 현재 재생 곡 / NEXT SONG으로 고정된 곡은 목록에서 제외 (배너로만 표시) */
.queue_active,
.queue_nextup { display: none !important; }

#nowNextBanner { display: flex; flex-direction: column; gap: 8px; padding: 12px 16px; }
.nn-item {
  display: flex; align-items: center; gap: 12px; min-width: 0;
  padding: 10px 14px; border-radius: 12px;
  background: rgba(127, 127, 127, 0.1); border: 1px solid rgba(127, 127, 127, 0.2);
}
.nn-next { background: rgba(91, 140, 255, 0.14); border-color: rgba(91, 140, 255, 0.45); }
/* 라벨 고정폭 → NOW PLAYING / NEXT SONG 아래 썸네일·제목이 정렬됨 */
.nn-label {
  flex: 0 0 auto; width: 112px; font-weight: 700; font-size: 0.78rem; opacity: 0.85;
  white-space: nowrap; display: flex; align-items: center; gap: 4px;
}
.nn-thumb { width: 72px; height: 41px; object-fit: cover; border-radius: 6px; flex: 0 0 auto; }
.nn-meta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.nn-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nn-title a { color: inherit; text-decoration: none; }
.nn-title a:hover { text-decoration: underline; }
/* 관리자 NEXT SONG 제목(.lp-title)은 새 탭 링크가 아니라 즉시 재생 버튼이라
   손가락 커서로 클릭 가능함을 표시 (다른 목록의 .qe_title과 동일한 관례) */
.nn-title a.lp-title { cursor: pointer; }
.nn-sub { font-size: 0.8rem; opacity: 0.7; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nn-sub:empty { display: none; }
/* 관리자 화면은 영상이 '지금'을 보여주므로 배너의 '지금' 부분 숨김 */
.admin-mode .nn-now { display: none !important; }

/* 뷰어의 NOW PLAYING: NEXT SONG(파랑)과 구분되는 초록 하이라이트 +
   'NOW PLAYING' 라벨이 천천히 커졌다 작아지며 지금 재생 중임을 알림. */
.remote-mode .nn-now {
  background: rgba(74, 222, 128, 0.13);
  border-color: rgba(74, 222, 128, 0.5);
}
.remote-mode .nn-now .nn-label {
  animation: nowPlayingPulse 2.4s ease-in-out infinite;
  transform-origin: left center;
}
@keyframes nowPlayingPulse {
  0%, 100% { transform: scale(1); opacity: 0.8; }
  50% { transform: scale(1.07); opacity: 1; }
}
/* 모션 최소화를 켠 사용자에게는 애니메이션을 끄고 하이라이트만 남김 */
@media (prefers-reduced-motion: reduce) {
  .remote-mode .nn-now .nn-label { animation: none; }
}

/* 관리자 사이드바 최상단 NEXT SONG: '신청곡 대기열'처럼 안내 문구가 위에 오고
   그 아래 곡 카드가 붙는 구성. 아래 대기열 행보다 확실히 크게(세로 약 1.5배)
   잡아서 지금 무슨 곡이 다음인지 한눈에 들어오게 함. */
/* 사이드바 NEXT SONG은 관리자 전용. 뷰어는 기존 #nowNextBanner를 그대로 쓰므로
   JS(updateNowNextBanner)에서도 숨기지만, 모드 전환 타이밍에 따라 잠깐이라도
   노출되지 않도록 CSS로도 확실히 막아둠. */
.remote-mode #sidebar-next-wrap { display: none !important; }
/* NEXT SONG을 대기열과 물리적으로 분리된 카드로: 자체 배경/테두리를 둬서
   "같은 틀 안에 상단 고정"처럼 보이지 않게 한다. 넓은 화면(고정 사이드바)이든
   좁은 화면(원래 위치에 흐르는 배치)이든 똑같이 적용 - media query 밖에 둠. */
#sidebar-next-wrap {
  margin-bottom: 16px;
  padding: 10px 12px 12px;
  border-radius: 12px;
  background: rgba(91, 140, 255, 0.08);
  border: 1px solid rgba(91, 140, 255, 0.3);
}
/* NEXT SONG 카드 자체(썸네일+제목)도 대기열 행보다 확실히 크게 - 작은 화면
   에서도 다음 곡이 뭔지 한눈에 들어오도록 media query로 가두지 않는다. */
#sidebar-next-song.nn-item { padding: 14px 16px; gap: 16px; }
#sidebar-next-song .nn-thumb { width: 108px; height: 61px; }
#sidebar-next-song .nn-title { font-size: 1.15rem; }
#sidebar-next-song .nn-sub { font-size: 0.92rem; }
/* 키우는 건 '⏭ NEXT SONG' 문구뿐. 아래 곡 카드는 기본 .nn-item 크기 그대로 둠.
   이모지와 글자는 각각 span으로 분리해 상하 가운데로 맞춤 (폰트마다 이모지
   기준선이 달라 그냥 두면 살짝 떠 보임).
   선택자에 .nominee-title을 함께 적어 특이도를 올림: .nominee-title 규칙이
   이 파일 아래쪽에 있어서, 클래스 하나짜리 선택자로는 크기·여백이 그대로
   덮어써져 확대가 적용되지 않았음. */
.nominee-title.sidebar-next-title {
  padding: 2px 2px 8px;
  font-size: 1.17rem;
  display: flex; align-items: center; gap: 7px;
  line-height: 1;
}
.sidebar-next-title .nn-label-icon { display: inline-flex; align-items: center; line-height: 1; }

/* 좁은 화면(모바일 뷰어): 라벨 텍스트를 빼고 기호만 남겨 제목에 폭을 더 줌 */
@media (max-width: 480px) {
  .nn-label { width: auto; }
  .nn-label-text { display: none; }
  .nn-label-icon { font-size: 1.1rem; }
  .nn-thumb { width: 56px; height: 32px; }
}

/* 재생 중 제목: 큰 제목 + 작은 부제(채널 · 신청자).
   header가 flex row에서 실제로 줄어들 수 있어야 안쪽 텍스트가 말줄임표로
   잘리지, 안 그러면 옆의 ♥️/⭐/Skip 버튼과 겹쳐버림. */
.info header { min-width: 0; }
#currenttitle { display: block; min-width: 0; }
#currenttitle .current-main {
  line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#currenttitle .current-sub {
  font-size: 0.5em; font-weight: 400; opacity: 0.7; margin-top: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#currenttitle .current-channel { font-weight: 600; opacity: 0.9; }
/* 현재 재생 곡 조회수·업로드 날짜 (채널명 오른쪽) */
#currenttitle .current-views,
#currenttitle .current-published { opacity: 0.85; white-space: nowrap; }
/* 채널명은 아바타 때문에 inline-flex라 기준선이 글자와 달라, 나머지가 위로 떠
   보였음. 같은 inline-flex + vertical-align:middle로 맞춰 한 줄에 나란히.
   이 줄에 새 항목을 넣을 때는 여기에도 꼭 같이 넣어야 한다. */
#currenttitle .current-views,
#currenttitle .current-published,
#currenttitle .current-author {
  display: inline-flex; align-items: center; vertical-align: middle;
}
#currenttitle .current-channel-icon {
  width: 1.5em; height: 1.5em; border-radius: 50%; object-fit: cover;
  vertical-align: middle; margin-right: 5px;
}
/* 제목/채널 클릭 링크: 색은 그대로, hover 때만 밑줄 */
#currenttitle a.current-title-link,
#currenttitle a.current-channel-link { color: inherit; text-decoration: none; }
#currenttitle a.current-title-link:hover,
#currenttitle a.current-channel-link:hover { text-decoration: underline; }
#currenttitle a.current-channel-link { display: inline-flex; align-items: center; vertical-align: middle; }

/* 플레이리스트: 제목 아래 채널명 (썸네일 - 제목 - 채널 - 신청자) */
.qe_channel {
  font-size: 0.82em; opacity: 0.7; padding: 1px 0 0 2px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ===== 재생 기록 (접이식, 묶음2) ===== */
#playHistory { border-top: 1px solid var(--border); margin-top: 4px; }
#historyToggle {
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: 12px 16px; background: none; border: none; color: inherit;
  cursor: pointer; font-weight: 700; font-size: 0.95rem; text-align: left;
}
#historyToggle:hover { background: rgba(127, 127, 127, 0.08); }
#historyCount { opacity: 0.6; font-weight: 600; }
.history-chevron { transition: transform 0.15s ease; flex: 0 0 auto; }
#playHistory:not(.collapsed) .history-chevron { transform: rotate(90deg); }
#playHistory.collapsed #historyList { display: none; }
/* 백업 버튼: 뷰어 전용, 재생 기록을 펼쳤을 때만(곡 목록 바로 위) 표시 */
#playHistory #remoteBackupBtn { display: none; margin: 4px 16px 12px; }
body.remote-mode #playHistory:not(.collapsed) #remoteBackupBtn { display: inline-flex; }
#historyList { list-style: none; margin: 0; padding: 0; }
/* 재생 기록 행은 재생목록 행(.queue_entry) 스타일 재사용, 살짝 흐리게 */
.history_entry { opacity: 0.82; }

/* ===== 픽 플래그 (묶음3a) ===== */
/* 영상 하단 ♥️/⭐ 버튼: 미설정 흐리게, 설정되면 강조 */
.pick-btn { font-size: 1.15rem; padding: 3px 9px; line-height: 1; opacity: 0.5; }
.pick-btn:hover { opacity: 0.8; }
.pick-btn.active { opacity: 1; background: rgba(91, 140, 255, 0.2); }
/* 재생기록: Added by 왼쪽 플래그 이모지 */
.pick-badge { margin-right: 5px; }

/* ===== 우승 후보 섹션 (묶음3b) ===== */
.nominee-title { font-weight: 700; font-size: 0.9rem; padding: 10px 16px 4px; opacity: 0.85; }
#nomineeList { list-style: none; margin: 0; padding: 0; }
/* 하단 '⭐ 우승 후보' 제목: 80% 더 크게 (0.9rem → 1.35rem → 1.62rem, 마지막
   +20%는 사이드바 카드 도입 후 다시 키워달라는 요청). 사이드바 NEXT
   SONG·대기열 제목은 각자 규칙이 있어 영향 없음. */
#nomineeSection .nominee-title { font-size: 1.62rem; }
/* 팝업 확대보기와 똑같은 카드 느낌을 원래(사이드바 하단) 위치에서도: 안쪽에
   여백을 주고 배경/테두리/둥근 모서리를 둬서 내용이 붕 떠 있지 않고 카드
   안에 담긴 것처럼 보이게 한다. 값은 팝업(아래 body.nominee-zoom 규칙)과
   동일하게 맞춤. */
#nomineeSection {
  box-sizing: border-box;
  padding: 10px 22px 22px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 18px;
  background: #16181f;
}
/* 뷰어(모바일)에서는 이 카드가 화면 폭 끝까지 꽉 차서 오른쪽 둥근 모서리가
   iOS 스크롤바 자리에 딱 붙어 겹쳐 보였다. #nowNextBanner(padding:12px 16px)가
   안의 NOW PLAYING/NEXT SONG 박스를 화면 끝에서 16px 띄우는 것과 같은 값으로,
   카드 자체를 화면 가장자리에서 띄워 스크롤바를 피한다. 관리자 사이드바는
   이미 자체 여백이 있어 문제 없었으므로 뷰어 한정.
   :not(.nominee-zoom) 제외: 팝업은 position:fixed; left:50%로 가운데 정렬되는데,
   margin-left가 있으면 그 옆으로 밀려나 중앙에서 어긋나 버린다. */
body.remote-mode:not(.nominee-zoom) #nomineeSection { margin: 0 16px; }
/* 제목 자체의 기본 좌우 패딩(16px)까지 더해지면 섹션 패딩과 겹쳐 아래 목록
   행보다 제목이 더 안쪽으로 들어가 보인다 - 섹션이 패딩을 주므로 제목 쪽은 뺀다.
   위아래 패딩도 팝업(body.nominee-zoom #nomineeSection .nominee-title)과 같은
   값(6px 0 14px)으로 맞춘다 - 기본값(10px 16px 4px)은 위(섹션 패딩10px+제목
   패딩10px=20px)와 아래(제목 패딩 4px)가 크게 달라서 제목이 위로 쏠려 보였다. */
#nomineeSection .nominee-title { padding: 6px 0 14px; }
/* 일반 대기열 제목: 우승 후보 목록과 헷갈리지 않게 구분선으로 시작을 명확히.
   위쪽 여백을 줄여 살짝 올리고, NEXT SONG 제목과 같은 방식으로 이모지·글자를
   상하 가운데로 맞춤 (이모지를 별도 span으로 빼고 inline-flex 정렬). */
.queue-title {
  border-top: 1px solid rgba(127, 127, 127, 0.25); padding-top: 6px; padding-bottom: 6px;
  margin-top: 4px; font-size: 1.3rem; /* RANDOM 배지가 옆에 붙으면 상대적으로 작아 보여서 살짝 키움 */
  display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  line-height: 1;
}
.queue-title-label { display: inline-flex; align-items: center; gap: 7px; }
.queue-title-label .nn-label-icon { display: inline-flex; align-items: center; line-height: 1; }
/* 관리자 사이드바용: 원래 플레이리스트 하단에 있던 영상 수/재생시간을 여기로
   이전 (아래 .admin-mode .metadata 숨김과 짝) - 대기열 제목 오른쪽에 표시 */
/* margin-right는 아래 행의 'by 신청자'(.qe_author)와 같은 12px - 그래야 영상 수/시간의
   오른쪽 끝이 바로 아래 'by XXX'의 끝 글자와 정확히 같은 선에서 끝남 */
.sidebar-metadata {
  display: flex; align-items: center; font-size: 0.78rem; font-weight: 400; opacity: 0.75;
  margin-right: 12px;
}
.sidebar-metadata > span { display: flex; align-items: center; margin-left: 10px; }
.sidebar-metadata ion-icon { margin-right: 0.25rem; }
/* 원래 위치의 영상 수/재생시간은 양쪽 모두 숨김: 관리자는 사이드바 대기열
   제목 옆에, 뷰어는 '신청곡 대기열' 제목 옆에 이미 같은 값이 나오고 있어 중복. */
.metadata { display: none !important; }
/* 순번 타일: 썸네일 왼쪽에 후보 목록 안에서의 순서를 보여준다. 기존 썸네일
   자리(.qe_thumb, left:12px)를 오른쪽으로 밀어내고 그 앞에 끼워넣는 방식이라
   .nominee_entry에서만 필요한 만큼 padding-left/썸네일 위치를 더 늘려둔다.
   폰트는 일반 UI 산세리프 900(가장 굵은 단계) - 코드용 모노스페이스는 굵게를
   줘도 획이 가늘어서 숫자가 흐릿해 보였다.
   선택자를 .queue_entry.nominee_entry로 (클래스 2개) 쓰는 이유: 그냥
   .nominee_entry만 쓰면 뒤쪽의 기본 .queue_entry(padding-left:98px)와
   우선순위가 완전히 같아서 "나중에 나온 규칙이 이긴다"는 CSS 규칙 때문에
   이 규칙이 조용히 무시됐다 - 썸네일만 순번 타일 자리만큼 밀리고 제목 영역은
   그대로라 썸네일과 제목이 겹쳐 보였음(우승작은 !important라 안 겪음). */
.queue_entry.nominee_entry {
  padding-left: 138px; /* 기존 98px + 순번 타일(32px)+간격(8px) */
  /* 라운딩된 카드처럼 보이게: 기본 목록 행의 아래쪽 구분선 대신 행 사이
     여백을 두고, 각 행 자체를 둥근 모서리 카드로 만든다 (관리자 사이드바
     하단·팝업 확대보기 둘 다 같은 규칙이라 자동으로 같이 적용됨). */
  border-bottom: none !important;
  border-radius: 10px;
  margin-bottom: 6px;
}
.nominee_entry:last-child { margin-bottom: 0; }
.nominee_entry .qe_thumb { left: 52px; }
.qe_rank {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; border-radius: 10px; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans KR", sans-serif;
  font-weight: 900; font-size: 1.15rem; font-variant-numeric: tabular-nums;
  background: linear-gradient(160deg, #5b8cff, #a05aff);
  color: #fff;
  -webkit-text-stroke: 0.4px rgba(255, 255, 255, 0.55);
  box-shadow: 0 3px 8px rgba(160, 90, 255, 0.45);
}
.nominee-winner .qe_rank {
  width: 40px; height: 40px; font-size: 1.5rem; border-radius: 12px;
  -webkit-text-stroke: 0.6px rgba(255, 255, 255, 0.6);
}
/* 후보: 보라 하이라이트. 별표는 이 섹션 안에서는 안 보여도 됨(이미 후보 목록이라 자명) */
.nominee-nominated {
  background: rgba(160, 90, 255, 0.16) !important;
  border-left: 0.25rem solid rgba(160, 90, 255, 0.75) !important;
}
.nominee-nominated .pick-badge { display: none; }
/* 우승자: 빨강 + 확대 + 등장 애니메이션 */
.nominee-winner {
  background: rgba(255, 70, 70, 0.18) !important;
  border-left: 0.25rem solid rgba(255, 70, 70, 0.9) !important;
  overflow: visible !important; /* 확대/애니메이션이 잘리지 않게 (뷰어 우측 잘림 방지) */
  z-index: 2;
  /* 커진 썸네일(114px) + 순번 타일 자리 확보. 뒤쪽 .queue_entry{padding-left:98px}에
     안 밀리게 !important */
  padding-left: 188px !important;
  /* 왕관 배지(4em)가 이 줄을 키워도 옆 제목/썸네일이 위쪽에 붙지 않게 세로 중앙 정렬 */
  align-items: center;
  animation: winnerPop 0.65s cubic-bezier(0.2, 1.5, 0.35, 1);
}
@keyframes winnerPop {
  0%   { transform: scale(0.8) rotate(-2.5deg); box-shadow: 0 0 0 rgba(255, 70, 70, 0); }
  40%  { transform: scale(1.16) rotate(1.5deg);  box-shadow: 0 0 26px rgba(255, 70, 70, 0.8); }
  65%  { transform: scale(0.96) rotate(-0.8deg); box-shadow: 0 0 16px rgba(255, 70, 70, 0.5); }
  100% { transform: scale(1) rotate(0);          box-shadow: 0 0 0 rgba(255, 70, 70, 0); }
}
/* 우승작은 다른 후보보다 확연히 크게: 썸네일·제목·채널 ~1.5배 */
.nominee-winner .qe_thumb { width: 114px; height: 65px; left: 62px; }
.nominee-winner .qe_title { font-size: 1.5em; font-weight: 800; }
.nominee-winner .qe_channel {
  font-size: 1.23em;
  /* 채널명도 제목처럼 말줄임표로: 안 그러면 header가 이름 자리 확보를 위해
     좁아졌을 때 채널명만 잘리지 않고 옆으로 삐져나갔다. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
/* 왕관+이름 위치는 다시 기본값(오른쪽 끝, 다른 우승 후보 행과 동일)으로 -
   한때 제목 옆 30px 지점으로 옮겼었지만 되돌림. header/qe_author 모두 다른
   .queue_entry 행처럼 기본 flex 규칙을 그대로 따른다(별도 오버라이드 없음). */
/* 왕관은 더 이상 제목 옆의 독립된 칸이 아니라 "신청자" 줄의 👤 자리를 대신한다
   (rowHtml 참고) - 왕관 이모지와 이름을 한 덩어리(qe_winner_author)로 묶어서
   같이 움직이고, font-size 하나로 이모지·글자를 동시에 왕관 크기만큼 키운다.
   왕관/이름을 inline-flex + align-items:center로 정렬하는 이유: 이모지와 텍스트를
   그냥 한 줄에 같이 두면 폰트 기준선(baseline)이 서로 달라 위아래가 안 맞았다. */
.nominee-winner .qe_winner_author {
  display: inline-flex;
  align-items: center;
  gap: 0.12em;
  font-size: 3.75rem;
  line-height: 1;
  animation: crownPop 0.65s cubic-bezier(0.2, 1.7, 0.35, 1);
}
/* 뷰어 화면은 이 카드가 훨씬 좁은 폭에 들어가서 관리자용 3.75rem이 지나치게
   크게 보였다 - "원본 크기(신청자 줄 기본 글자 크기)의 20%만 키운" 수준으로 축소.
   em 단위라 부모(.qe_author, body 상속)의 실제 기본 글자 크기를 그대로 따라간다. */
.remote-mode .nominee-winner .qe_winner_author { font-size: 1.2em; }
.nominee-winner .qe_winner_crown {
  line-height: 1;
  /* flex align-items:center는 '글자 상자'만 가운데로 맞춰서, 이모지 폰트 특성상
     상자 안에서 아래로 치우쳐 그려지는 왕관은 그래도 처져 보였다. 광학적으로
     옆 텍스트와 눈높이가 맞도록 살짝 위로 당김. */
  transform: translateY(-10%);
}
.nominee-winner .qe_winner_name { font-weight: 800; line-height: 1; }
/* 우승작 행만 overflow:visible이라(등장 애니메이션이 안 잘리게) 긴 제목이 행
   밖으로 삐져나갔다. 제목 자체는 말줄임표로 자른다. */
.nominee-winner header h4 { overflow: hidden; text-overflow: ellipsis; }
@keyframes crownPop {
  0%   { transform: scale(0) rotate(-45deg); }
  55%  { transform: scale(1.6) rotate(14deg); }
  100% { transform: scale(1) rotate(0); }
}

/* 뷰어가 방금 신청한 곡: 대기열에 스크롤되어 들어오면 잠깐 하이라이트 */
.qe-just-added { animation: queueJustAdded 2s ease-out; }
@keyframes queueJustAdded {
  0%   { background-color: rgba(91, 140, 255, 0.35); }
  100% { background-color: transparent; }
}

/* 목록 행의 ♥️/⭐ 토글 버튼 (관리자). 'by 신청자' 왼쪽에 인라인으로 붙음. */
/* ♥️-⭐ 사이와 ⭐-🎵 사이 간격을 똑같이 10px로 맞춤. 버튼 좌우 패딩이 양쪽에서
   더해지는 탓에, gap을 그냥 두면 버튼끼리(패딩+gap+패딩)가 버튼-텍스트(패딩+
   margin)보다 넓어져서 ♥️-⭐만 벌어져 보였음. gap을 0으로 두고 좌우 패딩과
   margin-right를 같은 값으로 맞추면 두 간격이 정확히 일치함. */
.list-pick-controls { display: inline-flex; gap: 0; align-items: center; margin-right: 5px; }
.list-pick-controls button {
  font-size: 0.95rem; padding: 2px 5px; line-height: 1;
  background: none; border: none; cursor: pointer; opacity: 0.55;
  /* des.css의 전역 button:not(:first-child){margin-left:.5rem}가 ⭐에만 붙어서
     ♥️-⭐ 사이만 8px 더 벌어지고 있었음 - 간격은 위 패딩/margin으로만 잡는다. */
  margin: 0;
}
.list-pick-controls button:hover { opacity: 1; }
/* 지정된 플래그는 또렷하게 - 별도 배지를 없앤 대신 버튼 자체가 상태를 표시 */
.list-pick-controls button.active { opacity: 1; transform: scale(1.12); }
/* 방송 화면에는 불필요한 푸터 크레딧 숨김 (뷰어 페이지에는 그대로 남김) */
.admin-mode #footer { display: none !important; }

/* 재생 대기열 행: 순서변경(↑)·잠금 버튼 숨김, 재생 버튼은 ▶(파랑)로 통일 */
.qbtn-next, .qbtn-tmp { display: none !important; }
.qbtn-play { color: #5b8cff !important; font-size: 0.95rem; }

/* 플레이리스트 항목 왼쪽 유튜브 썸네일 */
.queue_entry {
  padding-left: 98px;
  /* 재생중(파란 바)일 때도 레이아웃이 안 밀리게: 모든 항목에 투명 좌측 보더 */
  border-left: 0.25rem solid transparent;
}
.qe_thumb {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 76px; height: 43px; object-fit: cover; border-radius: 5px;
  background: rgba(127, 127, 127, 0.15); z-index: 1;
}
/* 제목이 남는 공간을 채워 '신청자'가 항상 컨트롤 앞 같은 위치에 오게.
   제목 위 / 채널명 아래로 세로 스택 (메인 영상 제목 header에는 영향 없음).
   flex-basis는 auto가 아니라 0%로: auto면 (실제로는 말줄임표로 잘리는)
   제목의 원래 전체 폭이 줄바꿈 여부 계산에 그대로 들어가서, 제목이 길 때
   "by 신청자"가 같은 줄에 못 들어가고 아래로 밀려버림. */
.queue_entry > header {
  flex: 1 1 0%; min-width: 0;
  flex-direction: column; align-items: flex-start; gap: 2px;
}
/* 제목이 넘치면 줄바꿈 대신 말줄임표로: 안 그러면 좁은 화면에서 줄바꿈되며
   "by 신청자" 줄과 겹쳐서 레이아웃이 깨짐 (NOW/NEXT 배너와 동일한 처리) */
.queue_entry > header h4 { max-width: 100%; white-space: nowrap; }
.qe_author {
  white-space: nowrap; align-self: center; margin-right: 12px;
  display: inline-flex; align-items: center; line-height: 1; /* 왕관/이모지와 텍스트 상하 가운데 */
}
.pick-badge { align-self: center; }

.remote-mode #remote-header {
  display: block;
  padding: 16px 16px 4px;
}
.remote-title { margin: 0 0 12px; font-size: 1.35rem; font-weight: 700; }
.remote-title-en { font-size: 0.8rem; opacity: 0.55; font-weight: 400; margin-left: 6px; }
#remote-nick { font-weight: 700; }
/* 닉네임: 다른 링크 버튼들과 같은 줄에서 같은 높이/정렬로 흐르는 pill (누르면 변경) */
#change-nick-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 16px; border-radius: 10px; cursor: pointer; font-size: 0.9rem;
  border: 1px solid rgba(127, 127, 127, 0.35);
  background: rgba(127, 127, 127, 0.12); color: inherit;
}
/* des.css의 전역 button:not(:first-child){margin-left:.5rem} 때문에 닉네임 버튼만
   8px 밀려서, 줄바꿈되면 위 링크 버튼들과 왼쪽 끝이 안 맞았음. 간격은 .remote-links의
   gap이 이미 담당하므로 마진 제거. */
#change-nick-btn { margin-left: 0; }
#change-nick-btn:hover { background: rgba(127, 127, 127, 0.22); }
#change-nick-btn ion-icon { font-size: 1em; opacity: 0.75; }

/* 뷰어 페이지 하단 링크 버튼 (치지직 채널 / 아카이브) */
.remote-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.remote-link-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 16px; border-radius: 10px;
  background: rgba(127, 127, 127, 0.14); border: 1px solid rgba(127, 127, 127, 0.3);
  color: inherit; text-decoration: none; font-weight: 600; font-size: 0.9rem;
}
.remote-link-btn:hover { background: rgba(127, 127, 127, 0.24); }
/* 치지직 아이콘. 네이버 쪽에서 가져오지 않고 우리 서버에서 직접 준다 -
   광고 차단기나 회선 문제로 아이콘만 사라지는 일이 없게.
   1.11em은 옆 버튼 이모지(ℹ️·👤)의 실제로 보이는 크기(16px)와 맞춘 값. em이라 글꼴
   크기가 바뀌어도 이모지와 같이 따라간다. */
.remote-link-icon { width: 1.11em; height: 1.11em; border-radius: 3px; flex: 0 0 auto; }

/* ============================================================
   커스텀 모달 (기본 브라우저 prompt/alert 대체)
   ============================================================ */

.st-modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: rgba(8, 10, 16, 0.62);
  -webkit-backdrop-filter: blur(5px);
  backdrop-filter: blur(5px);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.st-modal-overlay.open { display: flex; }

.st-modal {
  width: 100%;
  max-width: 400px;
  box-sizing: border-box;
  background: #20242e;
  color: #eef1f6;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 18px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.55);
  padding: 26px 24px 20px;
  animation: st-modal-pop 0.18s ease-out;
}
@keyframes st-modal-pop {
  from { transform: translateY(10px) scale(0.97); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.st-modal-title { margin: 0 0 10px; font-size: 1.2rem; font-weight: 700; }
.st-modal-msg {
  margin: 0 0 18px; font-size: 0.95rem; line-height: 1.55;
  color: #c6ccd8; white-space: pre-line;
}
.st-modal-input {
  width: 100%; box-sizing: border-box;
  padding: 13px 15px; font-size: 1rem;
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 11px;
  background: #14171f; color: #fff; margin-bottom: 20px; outline: none;
}
.st-modal-input:focus {
  border-color: #5b8cff; box-shadow: 0 0 0 3px rgba(91, 140, 255, 0.25);
}
.st-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.st-modal-btn {
  padding: 11px 20px; font-size: 0.95rem; font-weight: 600;
  border: none; border-radius: 11px; cursor: pointer;
}
.st-modal-ok { background: #5b8cff; color: #fff; }
.st-modal-ok:hover { background: #4a7bf0; }
.st-modal-cancel { background: rgba(255, 255, 255, 0.08); color: #cfd6e4; }
.st-modal-cancel:hover { background: rgba(255, 255, 255, 0.15); }
/* 치지직 로그인 버튼: 테두리 기반 디자인으로 대체 로그인 옵션을 제시한다.
   아이콘(치지직 로고)과 함께 버튼의 의도가 명확하게 드러난다.
   선택자를 button.st-modal-chzzk로 둔 이유: des.css의
   `button:not(:first-child) { margin-left: .5rem }` 가 클래스 하나짜리
   선택자보다 우선순위가 높아서, .st-modal-chzzk 만으로는 margin-left를
   못 없앤다. 그 8px 때문에 아래 입력칸보다 버튼만 오른쪽으로 밀려 보였다.
   테두리 두께·모서리도 입력칸(des.css: .125rem / .5rem)에 맞춰 둔다. */
button.st-modal-chzzk {
  width: 100%; box-sizing: border-box;
  padding: 10px 8px; margin: 0 0 16px;
  font-size: 1rem; font-weight: 700;
  border: 0.125rem solid #00ffa3; border-radius: 0.5rem; cursor: pointer;
  background: rgba(0, 255, 163, 0.1); color: #00ffa3;
  display: flex; align-items: center; justify-content: flex-start; gap: 10px;
  transition: all 0.2s ease;
}
button.st-modal-chzzk:hover {
  background: #00ffa3; color: #0d1117;
  box-shadow: 0 4px 12px rgba(0, 255, 163, 0.3);
}
/* 버튼 앞에 치지직 로고 표시 */
.st-modal-chzzk::before {
  content: "";
  flex: 0 0 auto;
  display: inline-block;
  width: 22px; height: 22px;
  background: url('data:image/png;base64,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') center / contain no-repeat;
}
.st-modal-or {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 16px; font-size: 0.82rem; color: #8b93a7;
}
.st-modal-or::before,
.st-modal-or::after {
  content: ""; flex: 1 1 auto; height: 1px;
  background: rgba(255, 255, 255, 0.08);
}

/* 불필요해진 설정 항목 숨김 (묶음7): Player·Chat 카테고리 */
#playerCategory,
#adminMenu { display: none !important; }
/* 위 두 카테고리가 항상 숨겨져 있어 '방송 관리'가 사실상 첫 항목인데,
   :not(:first-child)는 DOM 기준이라 위쪽 구분선이 남아 말풍선 맨 위에 뜬금없는
   선이 하나 그어짐 - 제거. */
#broadcastMenu { border-top: none; padding-top: 0; }

/* admin 스킵 버튼 (플레이어 하단, 묶음6) */
.admin-skip-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font-weight: 600; padding: 4px 12px;
}

/* 플레이어 우측 하단 컨트롤(♥️/⭐/Skip/새로고침/설정) 30% 확대.
   FullScreen 버튼은 삭제했으니 남은 다섯 개만 대상. */
.video-info .controls button { padding: 0.65rem; }
.video-info .controls button:not(:first-child) { margin-left: 0.65rem; }
.video-info .controls ion-icon { font-size: 1.56rem; }
.video-info .controls .pick-btn { font-size: 1.5rem; padding: 4px 12px; }
.video-info .controls .admin-skip-btn { padding: 5px 16px; font-size: 1.3rem; gap: 7px; }

/* ===== 방송 관리 설정 UI (admin) ===== */
#adminAddSongBtn, #backupBtn { width: 100%; text-align: left; font-weight: 600; }
/* 제한 행을 한 줄로: [체크 라벨] ...... [작은칸] (단위) */
.broadcast-limit-row {
  display: flex; align-items: center; gap: 8px; padding: 6px 10px; list-style: none;
}
.broadcast-limit-row label {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
}
.broadcast-limit-row input[type="number"] {
  width: 52px; margin-left: auto; padding: 5px 7px; border-radius: 7px;
  border: 1px solid rgba(127, 127, 127, 0.4); background: rgba(127, 127, 127, 0.1); color: inherit;
}
.broadcast-limit-row select {
  margin-left: auto; padding: 5px 8px; border-radius: 7px;
  border: 1px solid rgba(127, 127, 127, 0.4); background: rgba(127, 127, 127, 0.1); color: inherit;
}

/* 설정 말풍선(admin): 내용 높이로 → 하단 빈공간·불필요 스크롤바 제거 + 닫기 버튼.
   position:static 으로 되돌려야 #chat(고정 위치)이 내용 높이를 잡을 수 있음. */
body.admin-mode #optionsPanel {
  position: static; height: auto; overflow-y: auto;
  border-radius: 12px;
  border: 1px solid rgba(127, 127, 127, 0.28);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}
/* 말풍선 꼬리: 톱니바퀴 버튼을 향해 아래쪽을 가리킴 (열려 있을 때만) */
body.admin-mode #chat.popover-open::after {
  content: "";
  position: absolute; bottom: -9px; right: 20px;
  border: 9px solid transparent; border-bottom: 0;
  border-top-color: var(--background-chat);
  filter: drop-shadow(0 3px 2px rgba(0, 0, 0, 0.35));
  pointer-events: none;
}
/* ===== 설정 전용 창 (OBS 캡처 회피용 별도 브라우저 창) =====
   메인 화면에서는 말풍선이라 위치/크기를 잡아뒀지만, 이 창에서는 창 전체를
   그대로 채워야 하므로 그 규칙들을 되돌린다. */
body.settings-window {
  margin: 0;
  padding: 14px 16px 24px;
  background: var(--background-chat, #17171c);
  color: var(--foreground, #e8e8ea);
  overflow-y: auto;
  display: block;
  font-family: "Noto Sans KR", "Noto Sans", system-ui, -apple-system, sans-serif;
}
body.settings-window #optionsPanel {
  position: static; height: auto; max-height: none; width: auto;
  opacity: 1 !important; display: block;
  border: none; border-radius: 0; box-shadow: none; background: transparent;
  padding: 0; margin: 0; overflow: visible;
}
body.settings-window #optionsPanel.collapse { height: auto; }
body.settings-window #optionsList { height: auto !important; overflow: visible; }
body.settings-window #optionsList.collapse { height: auto; }
/* 말풍선 꼬리는 이 창에서 의미 없음 */
body.settings-window #optionsPanel::after { display: none; }

/* ===== 사용자 관리 창 (관리자 전용) ===== */
/* 이름 변경·밴 확인 프롬프트(#st-modal-overlay)가 이 창 위에 떠야 하므로 한 단계 낮게.
   같은 z-index면 DOM 순서상 이 창이 위로 올라와 프롬프트를 클릭할 수 없게 됨. */
#user-admin-overlay { z-index: 9990; }
.user-admin-modal { max-width: 460px; }
#user-admin-list { list-style: none; margin: 0; padding: 0; max-height: 55vh; overflow-y: auto; }
/* 이름 검색: 접속자가 수십 명이면 한 화면에 5~6명씩만 보여 스크롤이 길어진다 */
.ua-search { margin-bottom: 6px; }
.ua-count { margin: 0 0 6px; font-size: 0.78rem; opacity: 0.6; text-align: right; }
.ua-row.ua-hidden { display: none; }
.ua-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 2px; border-bottom: 1px solid rgba(127, 127, 127, 0.18);
}
.ua-row:last-child { border-bottom: none; }
.ua-name { display: flex; align-items: center; gap: 6px; min-width: 0; font-weight: 600; word-break: break-all; }
/* 접속자 앞 아이콘: 직접 입력한 사람은 👤, 치지직으로 로그인한 사람은 치지직
   로고. 둘의 자리 폭을 같게 잡아 이름 시작 위치가 줄마다 흔들리지 않게 한다. */
.ua-icon {
  flex: 0 0 auto; width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.95rem; line-height: 1;
}
.ua-icon-chzzk { border-radius: 4px; object-fit: contain; }
.ua-tag {
  font-size: 0.66rem; font-weight: 700; padding: 2px 6px; border-radius: 5px;
  border: 1px solid currentColor; white-space: nowrap;
}
.ua-admin { color: #7fa4ff; }
.ua-banned { color: var(--error); }
.ua-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.ua-actions button {
  padding: 5px 10px; border-radius: 7px; cursor: pointer; font-size: 0.78rem; font-weight: 600;
  border: 1px solid rgba(127, 127, 127, 0.35); background: rgba(127, 127, 127, 0.12); color: inherit;
  margin: 0;
}
.ua-actions button:hover { background: rgba(127, 127, 127, 0.24); }
.ua-actions .ua-ban, .ua-actions .ua-kick { color: #ff8f8f; border-color: rgba(255, 143, 143, 0.4); }
.ua-actions .ua-unban { color: #7fe0a0; border-color: rgba(127, 224, 160, 0.4); }
#user-admin-empty:empty { display: none; }

/* 마지막 로그인/로그아웃 항목: 위쪽 구분선 없이 앞 항목과 이어지게 */
.options-category.no-divider { border-top: none !important; padding-top: 0 !important; }

/* 말풍선 안에 스크롤 영역이 둘(패널·목록)이라 목록 쪽이 마지막 항목의 아래
   여백(0.5rem)만큼 넘쳐서 필요 없는 스크롤바가 생겼음. 높이 제한과 스크롤은
   바깥 패널 하나만 담당하게 하고, 마지막 여백도 제거. */
body.admin-mode #optionsList { overflow: visible; height: auto; }
body.admin-mode #optionsList .options-category:last-child { margin-bottom: 0; }

/* 제목·버튼도 체크박스 행(.broadcast-limit-row의 padding-left:10px)과 같은
   위치에서 시작하도록 왼쪽 여백을 맞춤 - 기본값은 0이라 이모지/글자가 체크박스보다
   왼쪽으로 튀어나와 보였음. */
body.admin-mode #optionsList h4,
body.admin-mode #optionsList li button { padding-left: 10px; }

/* 접속자 수: Playlist 헤더 옆에 작은 배지로 표시 (관리자·뷰어 공통) */
.playlist-online-count {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 0.78rem; font-weight: 600; line-height: 1;
  padding: 4px 9px; border-radius: 999px;
  background: rgba(91, 140, 255, 0.16); color: #7fa4ff;
  white-space: nowrap;
}
.playlist-online-count::before { content: "●"; font-size: 0.6em; color: #5bff8c; }

/* 곡 신청 받는 중/마감 상태 배지: 테두리 박스 + 텍스트로 표시.
   #lockplaylist(관리자, 클릭해서 토글)와 #playlist-status-badge(뷰어, 읽기전용)
   둘 다 같은 스타일 공유. 뷰어는 접속자 수 배지 오른쪽에, 관리자는 기존
   자물쇠 자리에 그대로 표시. */
#lockplaylist,
.playlist-status-badge {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 10px; border-radius: 7px; border: 1.5px solid currentColor;
  background: transparent; font-size: 0.78rem; font-weight: 700;
  line-height: 1.3; white-space: nowrap;
}
#lockplaylist.success,
.playlist-status-badge.success { color: var(--success); }
#lockplaylist.danger,
.playlist-status-badge.danger { color: var(--error); }
.playlist-status-badge { display: none; }
.remote-mode #playlist-status-badge { display: inline-flex; }

/* 랜덤 재생 표시: 마감/받는 중 배지와 같은 테두리 박스 형태의 파란색 배지.
   켜져 있을 때만(.is-on) 보이고, 위치는 모드별로 다름 - 뷰어·관리자 모두
   '대기열' 제목 오른쪽(#random-badge-sidebar)에 표시. */
/* 크기는 '받는 중/마감' 배지와 동일하게 (.playlist-status-badge 값 그대로 상속) */
.random-badge { color: #5b8cff; margin-left: 2px; font-size: 0.702rem; } /* 기본 0.78rem에서 10% 축소 */
/* 대기열 제목 옆 배지만 살짝 내린다.

   Noto Sans KR의 한글은 글자가 줄상자 안에서 아래쪽에 앉는데, flex의
   align-items:center는 글자가 아니라 '상자'를 기준으로 가운데를 맞춘다.
   그래서 옆의 '대기열'은 낮게 앉고 배지만 위로 뜬 것처럼 보였다(실측 0.83px).

   헤더 쪽 배지(#random-badge)에는 적용하지 않는다. 거기는 옆이 한글이 아니라
   '마감' 알약 버튼이고 둘이 이미 맞아 있어서, 같이 내리면 그쪽이 어긋난다.

   ※ 값은 실제 Noto Sans KR를 띄워 픽셀을 재서 구했다(0.09~0.11em 구간이 최소).
     대체 폰트로 재면 전혀 다른 값이 나오니, 손볼 일이 생기면 폰트부터 확인할 것. */
#random-badge-sidebar {
  position: relative;
  top: 0.1em;
}
/* RANDOM+ : 일반 RANDOM(파란색)과 한눈에 갈라지도록 무지개색.
   글자는 background-clip으로 그라데이션을 입히고(-webkit- 접두사가 있어야
   사파리/구형 크롬에서도 먹는다), 테두리는 글자색을 따라가는 currentColor라
   투명해져 버리므로 border-image로 같은 그라데이션을 따로 그린다.
   border-image를 쓰면 border-radius가 무시되어 모서리가 각지므로, 배경
   그라데이션 두 장(안쪽 단색 + 바깥 무지개)을 겹쳐 둥근 테두리를 만든다. */
.random-badge.is-plus {
  color: transparent;
  background-image: linear-gradient(90deg,
    #ff5f6d, #ffb347, #ffe259, #4ade80, #38bdf8, #a78bfa, #f472b6);
  -webkit-background-clip: text;
  background-clip: text;
  border-color: transparent;
  position: relative;
}
/* 둥근 무지개 테두리: 배지와 같은 모양의 층을 하나 덧대고 마스크로 가운데를
   파내 테두리 굵기만 남긴다. 글자(z-index 위)는 그대로 보인다. */
.random-badge.is-plus::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: linear-gradient(90deg,
    #ff5f6d, #ffb347, #ffe259, #4ade80, #38bdf8, #a78bfa, #f472b6);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}
.remote-mode #random-badge-sidebar.is-on,
.admin-mode #random-badge-sidebar.is-on,
/* 좁은 관리자 화면에서는 사이드바가 고정되지 않고 플레이어 한참 아래로 흘러가서
   대기열 옆 배지가 눈에 안 띔 - 그때는 헤더 쪽 배지도 같이 띄운다. */
.admin-mode #random-badge.is-on { display: inline-flex; }
@media (min-width: 1600px) {
  /* 사이드바가 우측에 고정돼 항상 보이므로 헤더 배지는 중복 - 숨김 */
  .admin-mode #random-badge.is-on { display: none; }
}

/* RANDOM 배지 말풍선 툴팁: PC는 마우스오버, 모바일은 탭으로 뜬다 (Main.hx
   showRandomTooltip 참고). getBoundingClientRect 기준 좌표라 position:fixed. */
.random-tooltip {
  position: fixed;
  z-index: 10050;
  transform: translate(-50%, -4px);
  background: rgba(20, 20, 26, 0.95);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 8px;
  /* RANDOM+ 설명은 한 줄에 담기지 않는다. 좁은 화면(휴대폰)에서 한 줄로
     붙잡아 두면 배지 중앙 기준으로 펼쳐지다가 화면 밖으로 잘려 나가므로,
     폭을 화면 안으로 제한하고 줄바꿈을 허용한다. */
  max-width: min(280px, calc(100vw - 24px));
  white-space: normal;
  text-align: center;
  line-height: 1.45;
  pointer-events: none;
  opacity: 0;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.random-tooltip.show { opacity: 1; transform: translate(-50%, 0); }
.random-tooltip::before {
  content: "";
  position: absolute;
  left: 50%; top: -5px;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-bottom-color: rgba(20, 20, 26, 0.95);
}

/* 플레이리스트 제목: 관리자만 클릭해서 수정 가능 → 그때만 클릭 가능 표시 */
body.admin-mode #playlist-heading { cursor: pointer; }
body.admin-mode #playlist-heading:hover { text-decoration: underline dotted; text-underline-offset: 4px; }
/* 제목이 길면 2줄까지는 허용하고 그 이상은 말줄임표로. flex-basis를 auto로
   두면 (실제로는 잘리는) 원래 전체 텍스트 폭이 그대로 header의 공간을
   차지해버려서 옆 배지/버튼과 겹치니, 남는 공간만큼만 차지하고 그 안에서
   줄바꿈되게 함. */
#playlist-heading {
  flex: 1 1 auto; min-width: 0;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; word-break: break-word;
  /* header에서 white-space:nowrap이 상속돼서 명시적으로 풀어줘야 줄바꿈됨 */
  white-space: normal;
}

/* ♥️/⭐ 버튼: 유튜브 좋아요처럼 누르면 통통 튀고 이모지가 떠오르는 연출 */
.pick-pop { animation: pick-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes pick-pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.4); }
  60% { transform: scale(0.92); }
  100% { transform: scale(1); }
}
.pick-particle {
  position: fixed; pointer-events: none; z-index: 9999;
  font-size: 1.1rem; left: 0; top: 0;
  animation: pick-particle-fly 0.85s ease-out forwards;
}
@keyframes pick-particle-fly {
  0% { opacity: 1; transform: translate(-50%, -50%) scale(0.6); }
  20% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.85); }
}

/* 확인 버튼 없이 잠깐 떴다 사라지는 안내 (예: 백업 복사됨) — 화면 정중앙 */
.st-toast {
  position: fixed; left: 50%; top: 50%; z-index: 10000;
  transform: translate(-50%, -50%) scale(0.96);
  max-width: 90vw; padding: 16px 26px; border-radius: 12px;
  background: rgba(28, 28, 32, 0.95); color: #fff;
  font-size: 0.95rem; font-weight: 600; text-align: center; white-space: pre-line;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  opacity: 0; pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.st-toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }

/* ===== 우승 후보 확대 보기 =====
   후보란은 화면 맨 아래라 스크롤을 내려야만 보였다. 제목을 누르면 목록을
   그대로 화면 가운데로 띄운다. 별도 브라우저 창이 아니라 같은 창의 오버레이라
   OBS 캡처에 그대로 잡히고, 그래서 우승자 발표 연출로도 쓸 수 있다.
   DOM은 원래 자리에 그대로 두고 CSS로만 띄운다 - 옮기면 안에 걸어둔 클릭
   핸들러들의 부모가 바뀌어 이벤트 순서가 꼬일 수 있어서. */
#nominee-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  /* 확인 프롬프트(10000)·사용자 관리(9990)보다는 아래. 후보 목록 위로 안내창이
     떠야 하므로 이 순서가 지켜져야 함. */
  z-index: 9970;
  background: rgba(6, 7, 12, 0.72);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: nomineeFadeIn 0.22s ease-out;
}
body.nominee-zoom #nominee-backdrop { display: block; }

/* 우승자 선정 시 후보 행이 커지면서(썸네일/제목/왕관 확대) 목록 높이가 순간
   늘었다가 애니메이션이 끝날 때 다시 줄어드는데, 그 사이에 이 스크롤이
   auto라서 스크롤바가 잠깐 나타났다 사라지는 게 눈에 띄었다. renderNominees가
   애니메이션 재생 중에만 붙이는 클래스로, 그동안만 스크롤을 잠가 스크롤바
   깜빡임을 막는다 (내용이 진짜 넘칠 때의 평소 스크롤 동작은 그대로 유지). */
#nomineeSection.nominee-animating { overflow-y: hidden !important; }

body.nominee-zoom #nomineeSection {
  position: fixed;
  left: 50%;
  top: 50%;
  z-index: 9971;
  width: min(1000px, 88vw);
  max-height: 86vh;
  overflow-y: auto;
  box-sizing: border-box;
  padding: 10px 22px 22px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 18px;
  background: #16181f;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
  transform: translate(-50%, -50%);
  animation: nomineeZoomIn 0.32s cubic-bezier(0.2, 1.3, 0.35, 1);
}
/* 확대 상태에서는 제목도 같이 커져야 목록과 균형이 맞음 */
body.nominee-zoom #nomineeSection .nominee-title {
  font-size: 2rem;
  opacity: 1;
  padding: 6px 0 14px;
  text-align: center;
}
/* 제목이 여는 버튼이라는 걸 알 수 있게 */
#nomineeSection .nominee-title {
  cursor: pointer;
  user-select: none;
}
#nomineeSection .nominee-title:hover { opacity: 1; }
@keyframes nomineeFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes nomineeZoomIn {
  0%   { transform: translate(-50%, -50%) scale(0.84); opacity: 0; }
  100% { transform: translate(-50%, -50%) scale(1);    opacity: 1; }
}

/* URL 입력칸: 안내 문구(placeholder)만 가운데, 실제로 입력한 URL은 왼쪽 정렬.
   ::placeholder에 text-align을 주는 건 브라우저별로 안 먹어서, 칸 전체를 가운데로
   두고 '값이 들어온 순간'(:not(:placeholder-shown))에 왼쪽으로 되돌린다.
   긴 URL은 왼쪽 정렬이라야 앞부분이 보인다. */
#mediaurl { text-align: center; }
#mediaurl:not(:placeholder-shown) { text-align: left; }
