/* v3 컴포넌트 스타일 — playerHeader 와 compactStrip */

/* ─────────────────────────────────────────────────────────────────────
   큰 플레이어 헤더 .__v3c_pheader
   ────────────────────────────────────────────────────────────────────── */

.__v3c_pheader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--v3-space-7);
  padding: var(--v3-space-8) var(--v3-space-9);
  background-color: var(--v3-bg-surface);
  border-radius: var(--v3-radius-modal);
  margin-bottom: var(--v3-space-8);
}

.__v3c_pheader_msg {
  justify-content: center;
  color: var(--v3-text-muted);
}

.__v3c_pheader_msg p {
  margin: 0;
}

.__v3c_pheader_info {
  min-width: 0;
}

.__v3c_pheader_idline {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 7px;
}

.__v3c_pheader_name {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--v3-text-heading);
}

.__v3c_pheader_id {
  font-size: 12px;
  color: var(--v3-text-dim);
  font-family: var(--v3-font-mono);
}

.__v3c_pheader_ranks {
  display: flex;
  gap: var(--v3-space-7);
  margin-top: 5px;
  font-size: 13px;
  color: var(--v3-text-muted);
}

/* 🔴 라벨(SP/DP)과 단 값 사이의 간격. v1 은 HTML 문자열에 공백 한 칸을 넣어 벌렸는데,
   v3 는 DOM 으로 조립해 공백 노드가 없다 — gap 이 없으면 「SP皆伝」 으로 붙는다. */
.__v3c_pheader_rank {
  display: inline-flex;
  align-items: baseline;
  gap: var(--v3-space-3);
}

.__v3c_pheader_ranks b {
  font-weight: 700;
}

.__v3c_pheader_ranklabel {
  font-size: 10px;
  color: var(--v3-text-disabled);
}

/* 활성 모드 강조 */
.__v3c_pheader_rank[data-v3c-active="true"] .__v3c_pheader_ranklabel {
  font-weight: 700;
  color: var(--v3-accent);
  text-shadow: 0 0 8px var(--v3-accent);
}

.__v3c_pheader_star {
  flex-shrink: 0;
  text-align: right;
}

.__v3c_pheader_rating {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.15;
  color: var(--v3-text-heading);
}

.__v3c_pheader_rstar {
  font-size: 11px;
  margin-top: var(--v3-space-1);
  color: var(--v3-text-dim);
}

/* ─────────────────────────────────────────────────────────────────────
   단(段) 색 — 5종
   ────────────────────────────────────────────────────────────────────── */

.__v3c_rank_white {
  color: var(--v3-rank-white);
}

.__v3c_rank_blue {
  color: var(--v3-rank-blue);
}

.__v3c_rank_red {
  color: var(--v3-rank-red);
}

.__v3c_rank_chuden,
.__v3c_rank_kaiden {
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  background-size: 200% 100%;
  animation: __v3c_rank_shimmer 6s var(--v3-ease-linear) infinite;
}

.__v3c_rank_chuden {
  background-image: linear-gradient(
    90deg,
    var(--v3-rank-chuden-base) 0%,
    var(--v3-rank-chuden-base) 40%,
    var(--v3-rank-chuden-peak) 50%,
    var(--v3-rank-chuden-base) 60%,
    var(--v3-rank-chuden-base) 100%
  );
}

.__v3c_rank_kaiden {
  background-image: linear-gradient(
    90deg,
    var(--v3-rank-kaiden-base) 0%,
    var(--v3-rank-kaiden-base) 40%,
    var(--v3-rank-kaiden-peak) 50%,
    var(--v3-rank-kaiden-base) 60%,
    var(--v3-rank-kaiden-base) 100%
  );
}

@keyframes __v3c_rank_shimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .__v3c_rank_chuden,
  .__v3c_rank_kaiden {
    animation: none;
    background-position: 0 0;
  }
}

/* ─────────────────────────────────────────────────────────────────────
   Compact Strip .__v3c_strip (겉 요소)
   ────────────────────────────────────────────────────────────────────── */

.__v3c_strip {
  position: sticky;
  /* 🔴 실제 top 은 v3/shared/sticky-top.js 가 인라인 style 로 덮어쓴다(= 자기 앞 줄들의 합, 여기서는 헤더 높이).
     이 var() 는 그 모듈이 돌지 않았을 때의 폴백이고, 값이 총합이라 조금 아래에 붙는다 —
     헤더 뒤로 숨는 것보다 낫기 때문에 0 이 아니라 총합을 폴백으로 둔다. */
  top: var(--v3-sticky-top, 0px);
  z-index: var(--v3-z-stickybar); /* 🔴 폴더 summary(z 3)보다 위 — 되돌리면 Scoring 폴더 제목이 이 줄을 덮는다(세션 110) */
  background-color: var(--v3-bg-panel);
  border-bottom: 1px solid var(--v3-border-default);
  /* 🔴 **좌우 여백은 여기 두지 않는다 — `.__v3c_strip_inner` 가 혼자 갖는다**(2026-09-19 세션 51).
     사연: 둘 다 가지면 **좁은 폭에서 이중으로** 먹는다. 넓은 폭에서는 inner 가 960px 로 가운데 정렬돼
     겉 패딩이 안 보이지만, 좁은 폭에서는 inner 가 전폭이라 겉 패딩이 그대로 더해진다.
     ⇒ 실측: PC 는 맞는데 553px 에서 스트립 글자 24 vs 탭 글자 12 로 **12px 어긋났다.**
     ⚠️ 아래 좁은 폭 블록(`:387`)의 좌우 오버라이드도 같은 이유로 없앴다. */
  padding: var(--v3-space-4) 0;
}

.__v3c_strip[hidden] {
  display: none;
}

.__v3c_strip_inner {
  display: flex;
  align-items: baseline;
  gap: var(--v3-space-8);
  max-width: var(--v3-content-max);
  margin: 0 auto;
  font-size: 13px;
  /* 🔴 **탭 글자와 세로를 맞춘다**(2026-09-19 세션 51, 사용자 지시: 「왼쪽으로 튀어나왔다」).
     사연: 이 줄은 본문 박스와 **같은 폭·같은 중앙정렬**이라 내용이 박스 왼쪽 끝에서 시작했는데,
     바로 아래 PlayData 탭줄은 full-bleed 라 **탭 자신의 좌우 패딩(`--v3-space-7` = 12px)** 만큼
     안쪽에서 글자가 시작한다. ⇒ 실측(PC 1440px) 스트립 내용 240 vs 탭 글자 252 — **12px 어긋났다.**
     ⚠️ `--v3-space-7` 이어야 한다 — 탭이 쓰는 것과 **같은 토큰**이다. 값을 따로 적지 마라.
     ⚠️ `box-sizing: border-box`(`styles.css:6`)라 `max-width` 는 그대로 960px 다. */
  padding-inline: var(--v3-space-7);
}

.__v3c_strip_mode {
  font-size: 10px;
  color: var(--v3-text-disabled);
}

/* 위 __v3c_pheader_rank 와 같은 이유로 gap 이 필요하다 — DOM 조립에는 공백 노드가 없다. */
.__v3c_strip_dan {
  display: inline-flex;
  align-items: baseline;
  gap: var(--v3-space-3);
}

.__v3c_strip_dan b {
  font-weight: 700;
}

.__v3c_strip_star {
  font-weight: 700;
  color: var(--v3-text-heading);
}

.__v3c_strip_rstar {
  font-size: 11px;
  color: var(--v3-text-dim);
}

/* ─────────────────────────────────────────────────────────────────────
   Persona .__v3c_persona — 성향 리포트 (정본 §5.3)
   값의 출처는 v1 user/tabs/report.js 의 injectReportCss() 인라인 CSS 다.
   색은 전부 기존 토큰과 hex 가 일치한다(#ff6b9d=accent · #ff9bce=accent-hover ·
   #868e96=text-dim · #fff=text-strong · #495057=text-faint).
   ────────────────────────────────────────────────────────────────────── */

.__v3c_persona {
  padding: var(--v3-space-6) var(--v3-space-7);
  background-color: var(--v3-bg-surface);
  border-radius: var(--v3-radius-modal);
  margin-bottom: var(--v3-space-8);
  font-size: 13px;
  line-height: 1.55;
}

.__v3c_persona_topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--v3-space-5);
  margin-bottom: var(--v3-space-6);
}

.__v3c_persona_langsel {
  display: flex;
  align-items: center;
  gap: var(--v3-space-4);
  font-size: 13px;
}

.__v3c_persona_langlbl {
  color: var(--v3-text-dim);
}

/* 버튼이지만 v1 과 같은 「글자만」 모양이라 기본 버튼 스타일을 전부 지운다. */
.__v3c_persona_lang {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--v3-text-dim);
  cursor: pointer;
}

.__v3c_persona_lang[data-v3c-active="true"] {
  font-weight: 700;
  color: var(--v3-text-strong);
}

.__v3c_persona_sep {
  color: var(--v3-text-faint);
}

.__v3c_mdlang { display: flex; align-items: center; gap: var(--v3-space-4); font-size: 13px; margin-bottom: var(--v3-space-6); }
.__v3c_mdlang_lbl, .__v3c_mdlang_sep { color: var(--v3-text-dim); }
.__v3c_mdlang_btn { padding: 0; border: 0; background: none; font: inherit; color: var(--v3-text-dim); cursor: pointer; }
.__v3c_mdlang_btn[data-v3c-active="true"] { font-weight: 700; color: var(--v3-text-strong); }

/* 캡처 버튼 — Persona topbar 우측. 정본 §5.3.2.
   v1 .__uprofile_report_capture 는 카드 배경(어두움)보다 밝은 #2d2d2d 였는데,
   v3 Persona 카드가 이미 --v3-bg-surface(#2d2d2d) 라 같은 값을 쓰면 배경에 묻는다 —
   「배경보다 한 단계 밝게」라는 관계만 계승해 한 칸 올렸다. 새 색은 없다. */
.__v3c_persona_capture {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--v3-space-4) var(--v3-space-5);
  border: 1px solid var(--v3-border-strong);
  border-radius: var(--v3-radius-xl);
  background: var(--v3-bg-surface-raised);
  color: var(--v3-text-main);
  cursor: pointer;
}

.__v3c_persona_capture:hover {
  background: var(--v3-bg-surface-muted);
}

.__v3c_persona_capture:disabled {
  opacity: .5;
  cursor: default;
}

.__v3c_persona_head {
  margin: var(--v3-space-1) 0 var(--v3-space-6);
  padding: var(--v3-space-5) var(--v3-space-6);
  border-left: 3px solid var(--v3-accent);
  border-radius: 0 var(--v3-radius-xl) var(--v3-radius-xl) 0;
  background: var(--v3-bg-accent-soft);
  font-size: 15px;
  font-weight: 700;
}

.__v3c_persona_sec {
  margin: var(--v3-space-7) 0 var(--v3-space-1);
  font-size: 13.5px;
  font-weight: 700;
}

.__v3c_persona_li {
  margin: var(--v3-space-1) 0 var(--v3-space-1) var(--v3-space-3);
}

/* → 로 시작하는 줄과 🧩 배치 블록의 따옴표 문장만 강조한다.
   🔴 📝 요약 블록의 따옴표 문장은 강조하지 않는다(파서가 quote=false 로 준다). */
.__v3c_persona_li.__v3c_is_arrow,
.__v3c_persona_li.__v3c_is_quote {
  color: var(--v3-accent-hover);
}

.__v3c_persona_tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--v3-space-1) var(--v3-space-5);
  margin-top: var(--v3-space-6);
}

.__v3c_persona_tag {
  font-size: 12px;
  color: var(--v3-accent-hover);
  white-space: nowrap;
}

/* v1 은 opacity:.55 였다. #e9ecef 의 55% 는 --v3-text-dim(#868e96) 과 거의 같아 색으로 치환했다(G-5). */
.__v3c_persona_meta {
  margin-top: var(--v3-space-6);
  font-size: 11px;
  color: var(--v3-text-dim);
}

.__v3c_persona_empty {
  padding: var(--v3-space-10) var(--v3-space-7);
  color: var(--v3-text-muted);
  line-height: 1.6;
}

.__v3c_persona_empty p {
  margin: 0;
}

/* ─────────────────────────────────────────────────────────────────────
   모바일 반응형
   ────────────────────────────────────────────────────────────────────── */

@media (max-width: 600px) {
  .__v3c_pheader {
    padding: var(--v3-space-7);
    gap: var(--v3-space-5);
  }

  .__v3c_pheader_name {
    font-size: 16px;
  }

  .__v3c_pheader_rating {
    font-size: 19px;
  }

  .__v3c_strip_inner {
    gap: var(--v3-space-6);
  }

  /* 🔴 **스트립 좌우 오버라이드는 없앴다**(2026-09-19 세션 51) — 좌우 여백의 주인은
     `.__v3c_strip_inner` 하나다. 여기에 되살리면 좁은 폭에서 **이중으로** 먹어
     스트립 글자가 탭 글자보다 오른쪽으로 밀린다. */

  /* 🔴 :not() 으로 빈 상태를 뺀다. 빼지 않으면 같은 특정도(0,1,0)에 소스 순서가 뒤라
     위의 .__v3c_persona_empty 세로 여백(18px)이 모바일에서만 12px 로 덮인다. */
  .__v3c_persona:not(.__v3c_persona_empty) {
    padding: var(--v3-space-7);
  }
}

/* ─────────────────────────────────────────────────────────────────────
   토스트 .__v3sh_toast — 셸 소유(화면 무관)라 __v3sh_ 접두사다. 정본 §5.3.2 · §1.2 C6
   값은 v1 styles.css #__chap_toast 실측. 20px 좌우 padding 은 v3 간격 토큰에 없어
   가장 가까운 18px(--v3-space-10) 로 맞췄다 — 토큰을 새로 만들지 않는다.
   ────────────────────────────────────────────────────────────────────── */

.__v3sh_toast {
  position: fixed;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  z-index: var(--v3-z-toast);
  padding: var(--v3-space-6) var(--v3-space-10);
  border: 1px solid var(--v3-toast-ok-border);
  border-radius: var(--v3-radius-xl);
  background: var(--v3-toast-ok-bg);
  color: var(--v3-toast-ok-text);
  box-shadow: var(--v3-shadow-toast);
  font-size: 14px;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--v3-duration-dialog) var(--v3-ease-default),
              transform var(--v3-duration-dialog) var(--v3-ease-default);
}

.__v3sh_toast[data-v3sh-error="true"] {
  border-color: var(--v3-state-danger);
  background: var(--v3-toast-err-bg);
  color: var(--v3-toast-err-text);
}

.__v3sh_toast[data-v3sh-show="true"] {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .__v3sh_toast {
    transition: none;
  }
}

/* ─────────────────────────────────────────────────────────────────────
   노트레이더 .__v3c_nr — 정본 §5.4.1 / 크기·간격은 v1 ohsorryRender.js:369~393
   실측, 구조색만 v3 토큰으로 갈아탔다(N-8c)
   ────────────────────────────────────────────────────────────────────── */

.__v3c_nr {
  display: flex;
  gap: var(--v3-space-4);
  padding: var(--v3-space-5);
  background-color: var(--v3-bg-surface);
  border-radius: var(--v3-radius-modal);
  margin-bottom: var(--v3-space-8);
}

.__v3c_nr_box {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--v3-space-1);
  padding: var(--v3-space-3) var(--v3-space-4);
}

.__v3c_nr_svg {
  display: block;
  overflow: visible;
}

.__v3c_nr_header {
  text-align: center;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 1.5px;
  line-height: 1.1;
}

.__v3c_nr_detail {
  width: 100%;
  margin-top: var(--v3-space-4);
  padding-top: 5px;
  border-top: 1px dashed var(--v3-border-default);
}

.__v3c_nr_stats {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-size: 11px;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
}

.__v3c_nr_stat {
  display: flex;
  justify-content: space-between;
  gap: var(--v3-space-4);
}

.__v3c_nr_total {
  display: flex;
  justify-content: space-between;
  padding-top: var(--v3-space-2);
  margin-top: var(--v3-space-1);
  border-top: 1px solid var(--v3-border-subtle);
  font-size: 10.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.__v3c_nr_label {
  font-weight: 700;
}

.__v3c_nr_value {
  color: var(--v3-text-main);
}

.__v3c_nr_total .__v3c_nr_label {
  color: var(--v3-text-muted);
  font-weight: 600;
}

/* 축별 라벨 색 */
.__v3c_nr_stat[data-v3c-cat="NOTES"]   .__v3c_nr_label { color: var(--v3-radar-notes); }
.__v3c_nr_stat[data-v3c-cat="CHORD"]   .__v3c_nr_label { color: var(--v3-radar-chord); }
.__v3c_nr_stat[data-v3c-cat="PEAK"]    .__v3c_nr_label { color: var(--v3-radar-peak); }
.__v3c_nr_stat[data-v3c-cat="CHARGE"]  .__v3c_nr_label { color: var(--v3-radar-charge); }
.__v3c_nr_stat[data-v3c-cat="SCRATCH"] .__v3c_nr_label { color: var(--v3-radar-scratch); }
.__v3c_nr_stat[data-v3c-cat="SOF-LAN"] .__v3c_nr_label { color: var(--v3-radar-soflan); }

/* ──────────────────────────────────────────────────────────────────────
   Analysis .__v3c_analysis — 정본 §5.4.2
   🔴 패널 **안쪽** 클래스는 v3 가 아니라 외부 모듈(gist-modules/analysisRender.js)이
      문자열로 박아서 낸다. 그래서 __v3c_ 접두사가 아니다 — §1.2 G-2 의 예외(A-6).
      이름을 바꿀 수 없으므로 v3 가 그 이름으로 선언을 갖는다.
   🔴 다만 **전역에 풀지 않고 `.__v3c_analysis` 안으로 스코프를 조였다.** G-2 예외는
      「이름을 바꿀 수 없다」는 사실에 준 것이지 전역 선언을 허가한 것이 아니다 —
      이렇게 두면 예외의 실질 범위가 이 패널 안으로 한정되고 이름은 그대로다.
      (모듈이 내는 노드는 전부 panel 의 후손이라 매칭에 영향이 없다.)
   값은 v1 styles.css:1688 · 1692~1712 · 1815~1820 을 옮긴 것이고,
   색 2개만 v3 토큰으로 갈아탔다 — 둘 다 **v1 과 값이 같아 새 색은 0개**다
   (#232830 = --v3-bg-surface-hover · #868e96 = --v3-text-dim).
   ────────────────────────────────────────────────────────────────────── */

.__v3c_analysis {
  padding: var(--v3-space-5);
  background-color: var(--v3-bg-surface);
  border-radius: var(--v3-radius-modal);
  margin-bottom: var(--v3-space-8);
}

.__v3c_analysis .__uprofile_pat_row:hover {
  background: var(--v3-bg-surface-hover);
}

/* 분석 상세(강/약점 이유 등) — 줄바꿈 강제.
   🔴 !important 는 v1 에서 옮겨온 것이고 반드시 있어야 한다 — 모듈이 이유 박스 div 에
      인라인 nowrap 을 박아서 내므로 그것을 이겨야 한다(v1 styles.css:1690~1691 주석). */
.__v3c_analysis .__uprofile_pat_detail,
.__v3c_analysis .__uprofile_pat_detail div {
  white-space: normal !important;
  overflow-wrap: anywhere;
}

/* 막대그래프 — 모바일에서 화면 밖으로 나가지 않게 */
.__v3c_analysis .__uprofile_pat_chart {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  overflow: hidden;
}

.__v3c_analysis .__uprofile_pat_chart > div {
  min-width: 0;  /* flex item 강제 줄이기 (text nowrap 무시) */
}

.__v3c_analysis .__uprofile_pat_chart > div > div:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 빈 상태 · 로드 실패 문구 */
.__v3c_analysis .__uprofile_tabempty {
  color: var(--v3-text-dim);
  text-align: center;
  padding: 40px 12px;
  font-size: 13px;
}

@media (max-width: 480px) {
  .__v3c_analysis .__uprofile_pat_chart > div > div:last-child {
    font-size: 9px !important;  /* 라벨 작게 (NOTES / SOF-LAN 등 긴 글자) */
  }
}

/* ─────────────────────────────────────────────────────────────────────
   PlayData 하위 메뉴 탭줄 .__v3c_pdtabs (정본 §6.1 · S3-1)
   🔴 top 은 v3/shared/sticky-top.js 가 인라인으로 덮어쓴다. var() 는 폴백이다.
   ────────────────────────────────────────────────────────────────────── */

.__v3c_pdtabs {
  position: sticky;
  top: var(--v3-sticky-top, 0px);
  z-index: var(--v3-z-stickybar); /* 🔴 폴더 summary(z 3)보다 위 — 되돌리면 Scoring 폴더 제목이 이 줄을 덮는다(세션 110) */
  display: flex;
  /* 🔴 높이 32px(테두리 포함) — 상단 메뉴줄·Compact Strip 과 맞춘다(2026-09-27 세션 106, 사용자 지시). */
  box-sizing: border-box;
  height: 32px;
  align-items: stretch;
  gap: var(--v3-space-2);
  overflow-x: auto;
  background-color: var(--v3-bg-panel);
  border-bottom: 1px solid var(--v3-border-default);

  /* 🔴 **full-bleed — 메인의 패딩 밖으로 빼 상단·좌우에 붙인다**
     (2026-09-19 세션 51, 사용자 실기 지시: 「탭이 메인 패딩 때문에 중간에 떠 있다」).
     `.__v3sh_main`(`v3/styles.css`)이 `padding: var(--v3-space-9)` 라 탭줄이 사방 16px 안쪽에 떠 있었다.
     🔴 **이 값은 `.__v3sh_main` 의 padding 과 반드시 같은 토큰이어야 한다** — 한쪽만 바꾸면
     탭줄이 어긋난 채로 뜨는데 **에러도 빨간 테스트도 없다.** 그래서 `playdata.css.test.mjs` 가
     두 규칙이 같은 토큰을 쓰는지 소스로 대조한다. 토큰을 바꾸려거든 **양쪽을 같이** 바꿔라.
     ⚠️ `margin-bottom` 은 건드리지 않는다 — 아래 `__v3c_pdsubtabs`·`__v3c_pdfilter` 와의 간격은
     그대로 둔다(사용자가 지시한 것은 **상단과 좌우**뿐이다).
     ⚠️ 탭 글자가 가장자리에 딱 붙지는 않는다 — `.__v3c_pdtab` 자신의 좌우 패딩
     (`--v3-space-7` = 12px)이 남는다. 그것까지 없애면 글자가 화면 모서리에 닿는다. */
  margin-top: calc(-1 * var(--v3-main-pad));
  margin-inline: calc(-1 * var(--v3-main-pad));
}

.__v3c_pdtab {
  flex: 0 0 auto;
  /* 위아래 패딩 0 — 줄 높이(32px)를 채우고 글자는 flex 로 세로 가운데. 강조선(border-bottom)은 줄 바닥에 붙는다(세션 106). */
  display: flex;
  align-items: center;
  padding: 0 var(--v3-space-7);
  font-size: 13px;
  font-weight: 600;
  color: var(--v3-text-dim);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

.__v3c_pdtab:hover {
  color: var(--v3-text-muted);
}

.__v3g_admin_tier_toggle[aria-pressed='true'],
.__v3c_pdtab[aria-current='page'],
.__v3c_pdtab[role='tab'][aria-selected='true'] {
  color: var(--v3-accent);
  border-bottom-color: var(--v3-accent);
}

.__v3g_admin_tier_status {
  margin: 8px 0;
  padding: 8px 12px;
  font-size: 13px;
  line-height: 1.5;
  border-left: 3px solid var(--v3-accent);
  border-radius: 4px;
  color: var(--v3-text-main);
  white-space: normal;
  overflow-wrap: anywhere;
}

.__v3c_pdtab[role='tab']:focus-visible {
  outline: 2px solid var(--v3-accent);
  outline-offset: -2px;
}

.__v3c_pdbody {
  padding-top: var(--v3-space-9);

  /* 🔴 **full-bleed — 위 탭줄(`.__v3c_pdtabs`)과 좌우를 맞춘다**
     (2026-09-21 세션 71, 사용자 실기 지시: 「렌더도 탭이랑 똑같이 맞춰줘」).
     세션 51 이 탭줄·서브탭만 메인 패딩 밖으로 뺐고 **본문은 16px 안쪽에 남아 있었다** —
     탭줄 L=240/R=1200 인데 본문은 L=256/R=1184 로 사방이 어긋나 보였다(실측, 1440px).
     🔴 **이 값은 `.__v3sh_main`(`v3/styles.css`)의 padding 과 반드시 같은 토큰이어야 한다** —
     한쪽만 바꾸면 본문이 어긋난 채로 뜨는데 **에러도 빨간 테스트도 없다.**
     `playdata.css.test.mjs` 가 탭줄에 대해 이미 그 대조를 하고 있고, 이 규칙도 같이 잰다.
     ⚠️ 이 래퍼 하나로 충분하다 — Stats 패널·폴더 목록이 전부 이 안에 들어 있다.
     ⚠️ 탭과 달리 **자식이 자기 좌우 패딩을 갖지 않아** 곡행·폴더가 가장자리에 그대로 닿는다.
        그것이 탭줄과 「같은 선」이고 사용자가 지시한 것이다. */
  margin-inline: calc(-1 * var(--v3-main-pad));
}

.__v3c_pdmsg {
  margin: 0;
  padding: var(--v3-space-12) 0;
  color: var(--v3-text-dim);
  font-size: 13px;
  text-align: center;
}

/* ===== C1 곡행 — S3-2b ===== */

/* 폴더 안 row 영역 — 🔴 컨테이너는 **세로 스택**이고 7트랙 grid 는 **행**이 갖는다
   (정본 §1.2.1 「행 구조」, 2026-09-15 개정). 컨테이너 쿼리는 **조상만** 질의하므로
   이 요소 **자신**의 `display`·트랙은 `@container v3row` 로 되돌릴 수 없다(G-7 부칙) —
   그래서 브레이크포인트에서 바뀌어야 하는 것은 전부 자손이 소유한다.
   컬럼: [lamp색박스 | LV | 곡명 | NOTES | LAMP텍스트 | RATE바 | SCORE+grade] */
.__v3c_tbody {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--v3-bg-row-gap);
  border-top: 1px solid var(--v3-border-subtle);
  container: v3row / inline-size;
  /* 🔴 트랙 폭은 여기서 **선언만** 하고 행이 소비한다(`grid-template-columns: var(--v3c-tracks)`).
     변형 셀렉터에 행을 이어 붙이면 (0,3,0)이 되어 2줄 블록의 (0,2,0)을 **이겨 버리고**
     bpLayout 유저만 2줄 전환이 죽는다 — 화면 변화 0 이라 또 안 보인다.
     🔴 유연 트랙이 `1fr` 이 아니라 `minmax(0, 1fr)` 인 것도 계약이다. 이유는 행별 grid 에서
     `1fr`(=`minmax(auto,1fr)`)의 최소값이 **그 행 자신의** 곡명 min-content 라 트랙이 행마다 따로 커질
     수 있기 때문이다. ⚠️ **지금 이 코드에서는 실제로 안 깨진다** — 곡명 셀에 `overflow: hidden` 이
     있어(아래 `.__v3c_cell.__v3c_title`) grid item 의 자동 최소 크기가 **0** 이 되고, 그래서 `1fr` 로도
     트랙이 이미 내용 독립이다. 🔴 **그런데도 명시하는 이유는 그 암묵 의존을 없애기 위해서다** —
     누가 `overflow` 한 줄을 지우면 정렬이 **조용히** 행별로 깨진다(정본 §1.2.1 「행 구조」). */
  --v3c-tracks: 8px 32px minmax(0, 1fr) 45px 100px 130px 95px;
}

/* bpLayout(BP 보유 INF 유저) — 꼬리 4셀 순서가 [노트수,램프,레이트,EX] → [램프,레이트,EX,BP] 로 바뀌는 표 전체 스위치 */
.__v3c_tbody.__v3c_bplayout {
  --v3c-tracks: 8px 32px minmax(0, 1fr) 100px 130px 95px 45px;
}

/* 🔴 Recent 행 — **10열 2행**. PC·모바일이 같은 모양이다(사용자 확정 2026-09-23).
   종전은 6열 1행(PC)/2행(모바일)이라 두 화면의 모양이 서로 달랐다.
     col1 램프 · col2 레벨(12/12.3) · col3~7 곡명(row1)
     row2 → col4 램프 prev(NP→) · col5 현재 램프 · col6 DJ prev(B→) · col7 rate 바
     col8 ★ ↔ EX prev · col9 BP prev ↔ EX 현재 · col10 BP 현재 ↔ 등급차
     (🔴 2026-09-25 세션 90 — 램프 prev 를 제 열로 갈라 9열 → 10열. 종전 col4 46px = 28px + 22px)
   🔴 **오른쪽 셋을 열로 가른 이유** — 한 칸에 몰아넣으면(종전 `__v3c_num` 방식) prev 유무와
   자릿수에 따라 현재값의 가로 위치가 **행마다 흔들린다.** 열이 갈려야 세로가 맞는다. */
.__v3c_tbody.__v3c_rctbody {
  /* 6번째(DJ prev `AA→`) 26px → 34px — 오른쪽 정렬이라 넓힌 만큼 램프 글자와의 간격이 된다(2026-09-26 사용자 지적 · v1 과 같은 값, RCX-7). */
  --v3c-tracks: 8px 32px minmax(0, 1fr) 28px 22px 34px 82px 45px 40px 45px;
}

/* 🔴 `bplayout` 분기는 더 이상 트랙을 바꾸지 않는다 — BP 는 이제 col9·col10 에 **항상** 자리를
   가지고, 값이 없으면 그 칸이 빌 뿐이다. 종전엔 BP 열을 붙였다 뗐다 해서 곡명 폭이 흔들렸다. */

/* ── Recent 2행 배치 ─────────────────────────────────────────────── */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow {
  grid-template-rows: auto auto;
  height: auto;
  column-gap: 4px;
  /* 🔴 **행 배경을 행에 준다** — 2행 모드에서 열 사이 `column-gap` 을 칠하는 것은 셀 배경이 아니라
     행 배경이다. 없으면 그 틈으로 tbody 색이 비쳐 **격자선처럼** 보인다(`__v3c_ostbody` 와 같은 처방).
     🔴 종전엔 이 규칙이 **모바일 컨테이너 쿼리 안에만** 있어서 PC 에서만 격자선이 보였다. */
  background: var(--v3-bg-row);
}
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow:hover,
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow.__v3c_highlight { background: var(--v3-bg-surface); }
/* 🔴 **셀에도 건다** — 셀마다 자기 배경이 있어서 행 배경만 바꾸면 **칸마다 hover 색이 갈린다**
   (2026-09-24 사용자 지적). 램프 색 셀은 `--v3c-cell-bg` 를 그대로 유지한다. */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow:hover > .__v3c_cell,
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow.__v3c_highlight > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-bg-surface));
}
/* ⚠️ 단 **9열은 hover 에서도 투명을 유지한다** — 불투명해지면 그 칸이 10열 쪽으로 넘긴 글자 뒤를
   가려 버린다(넘침 허용의 전제가 깨진다). 행 배경이 그대로 비친다. */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow:hover > .__v3c_cell.__v3c_rcbpprev,
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow:hover > .__v3c_cell.__v3c_rcexcur,
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow.__v3c_highlight > .__v3c_cell.__v3c_rcbpprev,
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow.__v3c_highlight > .__v3c_cell.__v3c_rcexcur {
  background: transparent;
}
/* 🔴 일반행용 `min-height: 40px` 를 리셋한다 — 2행이면 줄마다 40px = **80px** 가 된다.
   🔴 이것도 종전엔 모바일 컨테이너 안에만 있어서 **PC 에서만 행이 커졌다.** */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell {
  padding-top: 0;
  padding-bottom: 0;
  min-height: 0;
}
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_lamp { grid-column: 1; grid-row: 1 / 3; margin: 0; }
/* 🔴 Recent 레벨 칸 — 2행 모드라 게임레벨/zasa 를 **세로로** 쌓는다.
   ⚠️ **기본 `.__v3c_cell.__v3c_level` 은 `font-size: 20px` 에 flex 가로 방향이다.**
   zasa 를 `display:block` 으로 켜기만 하면 `12` 와 `12.4` 가 **가로로 붙어 `1212.4` 로 보이고
   20px 로 거대해진다** — 2026-09-24 v1 프로덕션 실사고이고 v3 도 **같은 구조였다.**
   세로 쌓기가 **컨테이너 쿼리 안에만** 있었던 것이 원인이다. `RS-18` 이 이 자리를 잠근다. */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_level {
  grid-column: 2;
  grid-row: 1 / 3;
  flex-direction: column;
  line-height: 1;
  justify-content: center;
  align-items: center;
  gap: 2px;
  padding: 0 4px !important;
}
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_level .__v3c_level_game { font-size: 18px; font-weight: 600; }
/* min-height — 아이폰 사파리가 line-clamp 요소의 intrinsic 높이를 grid auto 트랙에서 0 으로
   계산해 곡명이 사라지던 버그 방어(`__v3c_ostbody` 와 같은 처방). 1.25em = 한 줄 line-height. */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_title { grid-column: 3 / 8; grid-row: 1; min-height: 1.25em; align-self: center; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rclampprev { grid-column: 4; grid-row: 2; justify-content: flex-end; padding-left: 0; padding-right: 0; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_lamptext { grid-column: 5; grid-row: 2; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcdjprev { grid-column: 6; grid-row: 2; justify-content: flex-end; padding-left: 0; padding-right: 2px; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_ratebar { grid-column: 7; grid-row: 2; padding-left: 0; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcstar { grid-column: 8; grid-row: 1; justify-content: flex-start; padding-left: 0; padding-right: 0; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcbpprev { grid-column: 9; grid-row: 1; justify-content: flex-start; padding-left: 0; padding-right: 0; white-space: nowrap; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcbpcur { grid-column: 10; grid-row: 1; justify-content: flex-end; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcexprev { grid-column: 8; grid-row: 2; justify-content: flex-end; padding-right: 0; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcexcur { grid-column: 9; grid-row: 2; justify-content: flex-start; padding-left: 0; padding-right: 0; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_rcgrade { grid-column: 10; grid-row: 2; justify-content: flex-end; }
/* zasa★ 위치 — 🔴 Recent 는 **레벨 칸 아래줄**만 쓴다. 곡명 앞 span 은 DOM 에는 있지만 숨긴다
   (v1 `headCells` 와 같은 구조를 유지해야 오라클 동등성이 성립한다). */
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_title > .__v3c_zasa { display: none; }
.__v3c_tbody.__v3c_rctbody .__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_level .__v3c_level_zasa {
  display: block;
  font-size: 10.5px;
  font-weight: 400;
  color: var(--v3-text-dim);
}

/* Recent DBR 토글 — 곡명 앞 zasa 자리에 DBR 난이도(13.01)가 와서 20px 로는 잘린다.
   v1 styles.css:2622(`.__rc_dbr`). 🔴 컨테이너 변형 4종 중 하나이고 **생산자는 S3-9** 다
   (정본 §1.2.1 「컨테이너 클래스」). 어느 테스트도 이 누락을 못 잡아 회귀 검토가 찾았다. */
.__v3c_tbody.__v3c_rcdbr .__v3c_zasa { width: 25px; }

/* 🔴 행이 grid 컨테이너다. `display: contents` 가 **아니다** — 그것은 컨테이너가 grid 일 때의
   구조였고 G-7 부칙과 양립하지 않았다(§1.2.1 「행 구조」 2026-09-15 개정).
   v1 도 추천곡 폴더에서 같은 전환을 이미 했다(styles.css:2642-2650). */
.__v3c_tr {
  display: grid;
  grid-template-columns: var(--v3c-tracks);
}

.__v3c_tr > .__v3c_cell {
  padding: 6px 8px;
  box-sizing: border-box;
  min-height: 40px;
  /* 램프 색은 --v3c-cell-bg 로 들어온다(D-2). 없으면 일반 행 배경. */
  background: var(--v3c-cell-bg, var(--v3-bg-row));
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  cursor: pointer;
  color: var(--v3-text-main);
}

/* 🔴 hover·highlight 도 `--v3c-cell-bg` 를 **먼저** 본다.
   이 규칙은 (0,3,0)이라 위 기본 규칙(0,2,0)을 이기는데, 폴백 없이 쓰면
   **램프 셀 6종(NP·AC·EC·NC·HC·EX)의 색이 hover 에서 통째로 회색이 된다.**
   blink 3종(F·FC·PFC)은 `animation` 이라 살아남아 「9종 중 6종만 죽는」 비일관이 된다.
   v1 은 이것을 램프 색의 `!important` 로 지키고 있었다(styles.css:2237-2246) —
   `!important` 를 뺐으면 그 자리에 대체 방어가 있어야 한다. */
.__v3c_tr:hover > .__v3c_cell,
.__v3c_tr.__v3c_highlight > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-bg-surface));
}

/* 🔴 라이벌 승패 배경(4차 S4-4a · 정본 RV-9). 클래스는 chart-row.js 가 hasRival 일 때 rivalCmpScore 로만 붙인다
   (Lamp 폴더도 색 기준은 스코어 — v1 user/tabs/playdata.js:528-529). 값은 v1 styles.css:1318-1325 의 화면값이고
   색은 토큰으로만 쓴다(tokens.css 의 --v3-state-rival-*).
   🔴 `--v3c-cell-bg` 를 **먼저** 본다 — v1 은 램프 색을 `!important` 로 지켰다(styles.css:2237-2246).
   폴백 없이 쓰면 이 규칙(0,3,0)이 램프 셀 색을 덮어 **라이벌 행의 램프 칸이 통째로 승패색이 된다.**
   ⚠️ 이 블록을 위 hover 규칙보다 **앞으로** 옮기지 마라 — 같은 특정도(0,3,0)인 highlight 규칙에 진다. */
.__v3c_tr.__v3c_rival_win > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-state-rival-win-bg));
  box-shadow: 0 1px 0 var(--v3-state-rival-win-bg), 0 -1px 0 var(--v3-state-rival-win-bg);
}
.__v3c_tr.__v3c_rival_lose > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-state-rival-lose-bg));
  box-shadow: 0 1px 0 var(--v3-state-rival-lose-bg), 0 -1px 0 var(--v3-state-rival-lose-bg);
}
.__v3c_tr.__v3c_rival_win:hover > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-state-rival-win-hover-bg));
  box-shadow: 0 1px 0 var(--v3-state-rival-win-hover-bg), 0 -1px 0 var(--v3-state-rival-win-hover-bg);
}
.__v3c_tr.__v3c_rival_lose:hover > .__v3c_cell {
  background: var(--v3c-cell-bg, var(--v3-state-rival-lose-hover-bg));
  box-shadow: 0 1px 0 var(--v3-state-rival-lose-hover-bg), 0 -1px 0 var(--v3-state-rival-lose-hover-bg);
}

.__v3c_tr > .__v3c_cell.__v3c_num {
  justify-content: flex-start;
  padding-left: 12px;
}

.__v3c_tr > .__v3c_cell.__v3c_notes {
  justify-content: flex-end;
  padding-right: 6px;
  overflow: visible;
}

/* BP 셀(bpLayout) — "BP" 라벨과 숫자를 별도 span 으로 감싸 그리드(cell) 안에서 라벨은 왼쪽정렬,
   숫자는 오른쪽정렬 */
.__v3c_tr > .__v3c_cell.__v3c_notes.__v3c_bp .__v3c_bp_wrap {
  display: flex;
  flex: 1;
  min-width: 0;
  justify-content: space-between;
  align-items: center;
  gap: 4px;
}

.__v3c_tr > .__v3c_cell.__v3c_notes.__v3c_bp .__v3c_bp_label {
  flex: 0 0 auto;
  color: var(--v3-text-disabled);
  font-size: 0.85em;
}

.__v3c_tr > .__v3c_cell.__v3c_notes.__v3c_bp .__v3c_notes_val {
  color: var(--v3-text-disabled);
  text-align: right;
}

.__v3c_notes_star {
  display: none;
}

.__v3c_tr > .__v3c_cell.__v3c_notes.__v3c_notes_empty {
  justify-content: center;
  padding-right: 0;
}

/* 배치 뱃지 — 기본 숨김 */
.__v3c_layout_val { display: none; }

/* 배치 라벨 뱃지 — 스타일만(노출 스위치는 S3-6) */
/* 유저가 입력한 배치(세션 126) — 로직 추천(핑크)보다 우선, 노랑 */
.__v3c_layout_badge.__v3c_layout_user { color: var(--v3-state-warning-strong); --v3c-layout-user-color: var(--v3-state-warning-strong); }
.__v3c_layout_badge {
  color: var(--v3-accent);
  font-size: 9px;
  font-weight: 700;
  padding: 0 3px;
  margin-right: 4px;
  border: 1px solid var(--v3-accent);
  border-radius: 2px;
  line-height: 1.2;
  white-space: nowrap;
  position: relative;
  z-index: 1;
}

/* lamp 색박스 — 좁은 컬럼, gap(1px) 침범해서 색이 row 끊김 없이 이어짐 */
.__v3c_cell.__v3c_lamp {
  margin: -1px 0;
  padding: 0 !important;
  cursor: help;
}

/* 램프 색 — 커스텀 프로퍼티로 셀렉터 특정도 싸움을 피함(D-2) */
.__v3c_lamp-NP { --v3c-cell-bg: var(--v3-lamp-np); }
.__v3c_lamp-AC { --v3c-cell-bg: var(--v3-lamp-ac); }
.__v3c_lamp-EC { --v3c-cell-bg: var(--v3-lamp-ec); }
.__v3c_lamp-NC { --v3c-cell-bg: var(--v3-lamp-nc); }
.__v3c_lamp-EX { --v3c-cell-bg: var(--v3-lamp-ex); }

/* HC — 색 + 좌우 테두리 */
.__v3c_lamp-HC {
  --v3c-cell-bg: var(--v3-lamp-hc);
  border-left: 1px solid var(--v3-border-light);
  border-right: 1px solid var(--v3-border-light);
}

/* F/FC/PFC — blink 애니메이션(duration은 토큰) */
.__v3c_lamp-F { animation: __v3c_lampBlinkF var(--v3-lamp-blink-f) linear infinite; }
.__v3c_lamp-FC { animation: __v3c_lampBlinkFC var(--v3-lamp-blink-fc) linear infinite; }
.__v3c_lamp-PFC { animation: __v3c_lampBlinkFC var(--v3-lamp-blink-pfc) linear infinite; }

@keyframes __v3c_lampBlinkF {
  0%, 100% { background: var(--v3-lamp-f-dark); }
  50%      { background: var(--v3-lamp-f-off); }
}

@keyframes __v3c_lampBlinkFC {
  0%, 100% { background: var(--v3-lamp-nc); }
  33%      { background: var(--v3-lamp-hc); box-shadow: inset 0 0 0 1px var(--v3-border-light); }
  66%      { background: var(--v3-lamp-ex); }
}

@media (prefers-reduced-motion: reduce) {
  .__v3c_lamp-F {
    animation: none;
    --v3c-cell-bg: var(--v3-lamp-f-dark);
  }

  .__v3c_lamp-FC,
  .__v3c_lamp-PFC {
    animation: none;
    --v3c-cell-bg: linear-gradient(to bottom,
      var(--v3-lamp-nc) 0 33.33%,
      var(--v3-lamp-hc) 33.33% 66.66%,
      var(--v3-lamp-ex) 66.66% 100%);
  }
}

/* 폴더 summary 의 lamp 색박스 */
.__v3c_summarylamp {
  align-self: stretch;
  width: 8px;
  margin-right: 10px;
  flex-shrink: 0;
  background: var(--v3c-cell-bg, transparent);
}

/* LV 셀 */
.__v3c_cell.__v3c_level {
  padding: 2px 6px !important;
  font-size: 20px;
  line-height: 1;
  font-weight: 700;
  justify-content: center;
}

/* 곡명 셀 */
.__v3c_cell.__v3c_title {
  display: flex;
  align-items: center;
  overflow: hidden;
  font-size: 14px;
  padding-left: 0;
}

/* 곡명 위 시리즈 태그와 곡명을 감싼 박스 */
.__v3c_cell.__v3c_title > .__v3c_headtitlebox {
  flex: 1 1 auto;
}

/* 곡명 텍스트 — 한 줄 고정, 넘치면 오른쪽 말줄임(줄바꿈 없음) */
.__v3c_cell.__v3c_title .__v3c_songname {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  line-height: 1.25;
}

/* 곡명 앞 zasa 레벨 */
.__v3c_zasa {
  display: inline-block;
  width: 20px;
  margin-right: 3px;
  text-align: right;
  color: var(--v3-text-dim);
  font-size: 10.5px;
  font-weight: 400;
}

/* PC 한 줄 모드 — level cell 안의 zasa span 은 숨김 */
.__v3c_level_zasa { display: none; }

/* 램프텍스트 셀 */
.__v3c_cell.__v3c_lamptext {
  font-size: 11.5px;
  letter-spacing: 0.3px;
  justify-content: center;
  font-weight: 700;
  /* 🔴 배치 배지(아래)와 출처 뱃지(2줄 모드)의 **기준점**이다 — 둘 다 `position: absolute` 다.
     ⚠️ 종전에는 `@container` 블록 안에만 `position: relative` 가 있었다(2줄 모드용).
     PC 에서도 배지를 띄우게 되면서 **기본 규칙으로 올렸다**(2026-09-19 세션 51). 지우지 마라. */
  position: relative;
}

/* 🔴 **배치 배지는 flow 밖이다 — 램프 글자의 가운데 정렬을 건드리지 않는다**
   (2026-09-19 세션 51, 사용자 지시: 「램프 텍스트 정렬은 기존대로, 배지는 왼쪽으로 띄워서」).
   ⚠️ inline 으로 두면 배지+글자가 **한 덩어리로 가운데 정렬**돼 램프 글자가 오른쪽으로 밀린다.
   실측(PC 1440px): inline 일 때 글자 x=870, 이 규칙으로 되돌리면 칸 가운데로 돌아온다.
   🔴 PC 는 램프칸이 100px 라 `left: 0` 으로 왼쪽 끝에 붙여도 글자와 안 겹친다.
   2줄 모드는 칸이 **45px** 라 겹치므로 아래 `@container` 블록이 칸 **밖**으로 내보낸다. */
.__v3c_layoutmode .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_lamptext .__v3c_layout_badge {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  z-index: 2;
}

/* RATE 막대 */
.__v3c_cell.__v3c_ratebar {
  padding: 0 !important;
  position: relative;
  overflow: hidden;
}

.__v3c_rate_bg {
  position: absolute; inset: 0;
  background: var(--v3-bg-row);
}

/* 라이벌 승패 행은 rate 칸 바탕도 승패색이다(v1 styles.css:1322-1325). */
.__v3c_tr.__v3c_rival_win .__v3c_rate_bg { background: var(--v3-state-rival-win-bg); }
.__v3c_tr.__v3c_rival_lose .__v3c_rate_bg { background: var(--v3-state-rival-lose-bg); }
.__v3c_tr.__v3c_rival_win:hover .__v3c_rate_bg { background: var(--v3-state-rival-win-hover-bg); }
.__v3c_tr.__v3c_rival_lose:hover .__v3c_rate_bg { background: var(--v3-state-rival-lose-hover-bg); }

.__v3c_rate_fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: linear-gradient(to right, var(--v3-rate-fill-start), var(--v3-rate-fill-end));
}

.__v3c_rate_cut {
  position: absolute; top: 0; bottom: 0;
  width: 1px; background: var(--v3-rate-cut); z-index: 1;
}

.__v3c_rate_text {
  position: relative; z-index: 2;
  padding-left: 6px;
  font-size: 11.5px;
  color: var(--v3-text-main);
  text-shadow: 0 0 2px var(--v3-rate-text-shadow);
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
}

.__v3c_rate_letter {
  font-weight: 700;
  color: var(--v3-text-rate-letter);
  text-shadow:
    -1px -1px 0 var(--v3-rate-letter-outline),
     1px -1px 0 var(--v3-rate-letter-outline),
    -1px  1px 0 var(--v3-rate-letter-outline),
     1px  1px 0 var(--v3-rate-letter-outline);
}

/* 등급 글자 색 — PD_LETTER_COLOR(playdataTable.js:27)가 정본. D·E·F·'-' 는 색을 주지 않고
   .__v3c_rate_letter 의 기본색을 상속한다(chart-row.js 가 애초에 클래스를 안 붙인다). */
.__v3c_rate_letter.__v3c_rate_letter-AAA { color: var(--v3-lamp-ex); }
.__v3c_rate_letter.__v3c_rate_letter-AA  { color: var(--v3-lamp-ex); }
.__v3c_rate_letter.__v3c_rate_letter-A   { color: var(--v3-lamp-ex); }
.__v3c_rate_letter.__v3c_rate_letter-B   { color: var(--v3-lamp-nc); }
.__v3c_rate_letter.__v3c_rate_letter-C   { color: var(--v3-grade-c); }

.__v3c_rate_pct { font-size: 10.5px; color: var(--v3-text-muted); }

.__v3c_empty { color: var(--v3-text-empty); padding-left: 6px; }

/* ⚠️ v1 의 `.__pd_cell.__pd_empty`(styles.css:2361)는 **계승하지 않는다** — `__pd_empty` 는
   v1 에서도 항상 **span** 에만 붙고(chartTable.js:55·:114·:233·:292) 셀에 붙는 경로가 0건이라
   v1 에서도 죽은 규칙이다. T-9(셀렉터가 실제 DOM 과 매칭되는가)가 찾았다. */

/* 난이도 색 — v1 은 인라인 style 이었다(chartTable.js:34·:117). C4 가 A계열로 통일했다.
   레벨 셀은 항상, 곡명 셀은 LEG 일 때만 색이 붙는다(chart-row.js 가 그렇게 낸다). */
.__v3c_tr > .__v3c_cell.__v3c_diff-normal      { color: var(--v3-diff-normal); }
.__v3c_tr > .__v3c_cell.__v3c_diff-hyper       { color: var(--v3-diff-hyper); }
.__v3c_tr > .__v3c_cell.__v3c_diff-another     { color: var(--v3-diff-another); }
.__v3c_tr > .__v3c_cell.__v3c_diff-leggendaria { color: var(--v3-diff-leggendaria); }
.__v3c_tr > .__v3c_cell.__v3c_diff-fallback    { color: var(--v3-diff-fallback); }

/* 램프 텍스트 색 — 배지색과 다른 표다(helpers.js:54). 정본 §1.2.3 의 매핑 그대로. */
.__v3c_tr > .__v3c_cell.__v3c_lamptext-NP  { color: var(--v3-text-faint); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-F   { color: var(--v3-diff-another); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-AC  { color: var(--v3-lamp-ac); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-EC  { color: var(--v3-lamp-ec); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-NC  { color: var(--v3-lamp-nc); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-HC  { color: var(--v3-text-main); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-EX  { color: var(--v3-lamp-ex); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-FC  { color: var(--v3-state-info); }
.__v3c_tr > .__v3c_cell.__v3c_lamptext-PFC { color: var(--v3-state-info); }

/* R-STAR 축 도달 표시 */
.__v3c_rstar_axis_met { color: var(--v3-lamp-ex); }

/* 🔴 종전 `__v3c_rcbp` · `__v3c_rcdjchange` · `__v3c_rcexchange` 규칙은 10열 개편(2026-09-23)으로
   **그 셀들이 없어져 죽은 CSS 가 되었으므로 지웠다**(T-3 가 잡았다).
   BP 는 `__v3c_rcbpprev`/`__v3c_rcbpcur`, DJ 는 `__v3c_rcdjprev`,
   EX 는 `__v3c_rcexprev`/`__v3c_rcexcur` 로 갈렸다. */

/* 현재 램프 셀(`EX`) — 종전 `__v3c_rclampchange` 의 스타일을 그대로 가져왔다. 🔴 2026-09-25 부터 **현재 램프만** 낸다 —
   prev(`NP→`)는 바로 아래 `__v3c_rclampprev` 칸이다. 종전 문안: 「램프 전이 셀(`AC→EC`)」.
   ⚠️ Recent 행에만 건다 — `__v3c_lamptext` 는 Recs 행도 쓰는 클래스다. */
.__v3c_recrow > .__v3c_cell.__v3c_lamptext {
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
/* 램프 prev 셀(`NP→`) — col4. **오른쪽 붙임**이라 화살표가 col5 현재 램프에 붙어 보인다(DJ prev `B→` 와 같은 처방).
   ⚠️ 28px 이다 — `PFC→`·`EXH→` 가 넘치는지는 브라우저로 안 봤다(세션 90). */
.__v3c_recrow > .__v3c_cell.__v3c_rclampprev {
  justify-content: flex-end;
  padding-left: 0;
  padding-right: 0;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

/* SCORE cell 옆 — 다음 DJ Level 컷까지 부족분 (예: "AAA-13"). 작고 회색, 괄호 X. */
.__v3c_grade {
  color: var(--v3-text-disabled);
  font-size: 10.5px;
  font-weight: 400;
  margin-left: 6px;
  white-space: nowrap;
}

/* 🔴 종전 `__v3c_rcexchange > __v3c_grade` 의 absolute 규칙은 지웠다 — 10열 개편으로
   등급차가 **자기 열(col10 — 세션 90 전은 col9)** 을 갖게 되어 띄울 이유가 사라졌다. */

/* Recent row 클릭 가능 표시 */
.__v3c_recrow { cursor: pointer; }

/* 합본/라이벌 플레이데이터 — 출처 뱃지(AC/INF) + 라이벌 DJNAME */
.__v3c_tr > .__v3c_cell.__v3c_num { position: relative; }

.__v3c_srcwrap {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  pointer-events: none;
}

.__v3c_srcwrap_pc {
  position: absolute;
  top: -1px;
  left: 10px;
  z-index: 1;
}

.__v3c_srcwrap_mo { display: none; }

.__v3c_srcbadge {
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  padding: 1px 3px;
  border-radius: 3px;
  letter-spacing: 0.3px;
}

/* 출처 뱃지 색 — v1 styles.css:1290-1291. 🔴 글자색과 배경이 **다른 토큰**이다
   (배경 2개는 S3-2b 가 신설했다 — 정본 PD-7 재정정). */
.__v3c_src-ac  { color: var(--v3-source-ac);  background: var(--v3-source-ac-bg); }
.__v3c_src-inf { color: var(--v3-source-inf); background: var(--v3-source-inf-bg); }

.__v3c_rivalname {
  font-size: 9px;
  color: var(--v3-text-secondary);
  white-space: nowrap;
  max-width: 88px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 시리즈 태그 컨테이너 */
.__v3c_seriesline {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

/* 곡명 위 시리즈 태그 — 회색 작은 글자 */
.__v3c_series {
  font-size: 10px;
  color: var(--v3-text-tag);
  font-weight: 400;
  white-space: nowrap;
  line-height: 1.1;
}

/* 시리즈 특성 아이콘 */
.__v3c_seriesfeat {
  display: none;
}

/* Recommend 곡명 (추천곡 폴더 전용) */
.__v3c_recsongname {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Recommend titlebox 호출(추천곡 폴더) */
.__v3c_titlebox {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  line-height: 1.15;
  vertical-align: middle;
}

/* ── Recs 행 num 칸 · est 목표 램프 태그 · 해시태그 notes — S3-14c.
   🔴 v1 이 **무스코프**로 쓰는 것만 여기 있다(`styles.css:2702-2704`·`:2713`·`:2715-2721`·`:2768`).
   조상 스코프가 필요한 나머지는 **파일 끝 「Recs 폴더」 절**에 있다 — 옮기지 마라.
   등장 클래스의 생산자가 전부 `chart-row.js` 라 T-3 이 떨어뜨리지 않는 것이 여기 있는 근거다. */

/* v1 :2702-2704 — Recs 행 num 칸은 좌우 패딩을 지우고 EX(43px)·등급차(46px) 고정폭으로 재정렬한다.
   🔴 `padding-left: 0` 되돌림이 계약이다 — 빠지면 Recs 행 num 셀이 12px 밀린다(T-19 가 지킨다).
   ⚠️ v1 도 `.__pd_recrow` 무스코프라 Recent 행에도 같이 걸린다. **그대로 계승한 것**이다. */
.__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_num { padding-left: 0; padding-right: 0; }
.__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_num .__v3c_num_val { flex: 0 0 43px; text-align: right; margin-right: 4px; }
.__v3c_tr.__v3c_recrow > .__v3c_cell.__v3c_num .__v3c_grade { flex: 0 0 46px; text-align: left; margin-left: 0; }

/* est축 사다리 목표 램프 태그(#HC목표) — v1 :2713·:2715-2721. 한 폴더에 EC/HC/EXH 가 섞여 나오므로
   행마다 표시한다. PC = 해시태그 칸 맨 앞 / 모바일 = 시리즈줄 다음. 두 자리에 같은 값을 심고 한쪽씩 숨긴다.
   🔴 6색은 **램프 배지와 다른 팔레트**다(`--v3-recstage-*`, tokens.css). 값이 같아 보여도 합치지 마라.
   🔴 6색 전부가 있어야 한다 — T-2 가 열린 접두 면제를 걷었고 T-20 이 6종을 센다. */
.__v3c_estgoal { font-size: 10px; font-weight: 700; color: var(--v3-text-muted); white-space: nowrap; flex-shrink: 0; }
.__v3c_estgoal_ec { color: var(--v3-recstage-ec); }
.__v3c_estgoal_hc { color: var(--v3-recstage-hc); }
.__v3c_estgoal_exh { color: var(--v3-recstage-exh); }
.__v3c_estgoal_cl { color: var(--v3-recstage-cl); }
.__v3c_estgoal_ex { color: var(--v3-recstage-ex); }
.__v3c_estgoal_fc { color: var(--v3-recstage-fc); }
.__v3c_estgoal_mb { display: none; }

/* 추천 해시태그를 담는 notes 칸 — v1 :2768.
   🔴 v1 의 `!important` 둘(`justify-content`·`padding-right`)은 **계승하지 않는다.** v1 이 그것을 쓴
   이유는 `:2755` `.__dbr_folders … .__pd_notes{justify-content:flex-end!important}` (0,4,0) 를
   (0,1,0) 이 이겨야 했기 때문인데 **v3 에는 그 경쟁자가 없다**. 계승하면 S3-12b 가 나중에
   이길 수 없는 벽을 미리 세우는 것이다. */
.__v3c_notestags { justify-content: flex-start; gap: 5px; font-size: 10px; color: var(--v3-accent-hover); overflow: visible; white-space: nowrap; padding-right: 2px; }

/* ── 2줄 전환 (S3-2b-2) — 컨테이너 폭 ≤600px 에서 한 줄 grid 를 두 줄 grid 로 break.
   첫 줄: lamp / LV (각각 2 row span) + 곡명 (column 3 부터)
   둘째 줄: NOTES / LAMP텍스트 / RATE 바 / SCORE
   🔴 v1 은 무명 `@container (max-width:600px)`(styles.css:2422) 이고 v3 는 이름을 준다(G-7).
   ⚠️ v1 블록 안의 라이벌 승패(:2427-2430, 4차) · 랭킹 모달 강조(:2434-2438, 7차) ·
      summary 높이 47px(:2446-2448, S3-3) · 배치 뱃지 모바일 absolute(:2496-2503, S3-6) 는
      **이 조각 몫이 아니라 일부러 뺀 것**이다. 「빠졌다」고 채우지 마라. */
@container v3row (max-width: 600px) {
  /* 🔴 여기에 `.__v3c_tbody` 규칙을 쓰지 마라 — 컨테이너 자신은 자기 쿼리의 대상이 아니라서
     **영원히 매칭되지 않는 죽은 규칙**이 된다(정본 §1.2.1 G-7 부칙). 실제로 이 자리에 있었고
     회귀 검토가 잡았다. PC 의 tbody 가 이미 flex column + gap 이라 2줄에서 바꿀 것도 없다.
     T-18 이 이 자리를 기계로 막는다. */

  /* 🔴 행은 PC 에서도 이미 grid 다(§1.2.1 「행 구조」). 2줄에서 더해지는 것은 **두 번째 줄**과
     **column-gap**, 그리고 **행 배경**이다 — 2줄은 셀 사이 column-gap 을 셀 배경이 아니라
     **행 배경**이 칠하기 때문이다(v1 :2422).
     ⚠️ 여기서 `--v3c-cell-bg` 를 보지 않는다 — 램프 색 클래스는 **셀**에 붙지 행에 안 붙는다. */
  .__v3c_tr {
    display: grid;
    grid-template-rows: auto auto;
    column-gap: 6px;
    background: var(--v3-bg-row);
  }
  .__v3c_tr:hover,
  .__v3c_tr.__v3c_highlight { background: var(--v3-bg-surface); }
  /* 라이벌 승패 — 2줄 모드는 셀 사이 column-gap 을 행 배경이 칠한다(v1 styles.css:2514-2517). */
  .__v3c_tr.__v3c_rival_win { background: var(--v3-state-rival-win-bg); }
  .__v3c_tr.__v3c_rival_lose { background: var(--v3-state-rival-lose-bg); }
  .__v3c_tr.__v3c_rival_win:hover { background: var(--v3-state-rival-win-hover-bg); }
  .__v3c_tr.__v3c_rival_lose:hover { background: var(--v3-state-rival-lose-hover-bg); }
  /* 두 줄 모드 — cell 의 위아래 padding 제거(좌우는 유지). PC min-height 리셋. */
  .__v3c_tr > .__v3c_cell {
    padding-top: 0;
    padding-bottom: 0;
    min-height: 0;
  }
  .__v3c_tr > .__v3c_cell.__v3c_lamp { grid-column: 1; grid-row: 1 / 3; margin: 0; }
  /* level cell — 1번째 줄 gameLevel, 2번째 줄 zasaLevel. flex column + 가운데 정렬.
     `padding: 0 !important` 는 레이아웃 리셋이라 §1.2.3 의 「램프 색 !important 미계승」과 무관하다. */
  .__v3c_tr > .__v3c_cell.__v3c_level {
    grid-column: 2;
    grid-row: 1 / 3;
    padding: 0 !important;
    flex-direction: column;
    line-height: 1;
    justify-content: center;
    align-items: center;
    gap: 2px;
  }
  .__v3c_tr > .__v3c_cell.__v3c_level .__v3c_level_game { font-size: 18px; font-weight: 600; }
  .__v3c_tr > .__v3c_cell.__v3c_level .__v3c_level_zasa {
    display: block;
    font-size: 10.5px;
    font-weight: 400;
    color: var(--v3-text-dim);
  }
  /* 곡명 앞 zasa span 은 숨김(level cell 2번째 줄로 옮겨 갔다). */
  .__v3c_tr > .__v3c_cell.__v3c_title .__v3c_zasa { display: none; }
  .__v3c_tr > .__v3c_cell.__v3c_title { grid-column: 3 / -1; grid-row: 1; }

  /* PlayData row — 7 컬럼. 첫째 줄: lamp / level / title / NOTES(col7). 둘째 줄: LAMP텍스트 / RATE / SCORE.
     모바일 viewport(320~412px) 안에 들어가도록 합을 줄인다: 8+36+1fr+45+50+60+93.
     minmax(0, 1fr) — title 이 자식 컨텐츠 길이까지 안 양보해 viewport 를 넘기는 것을 막는다. */
  .__v3c_tr:not(.__v3c_recrow) {
    grid-template-columns: 8px 36px minmax(0, 1fr) 45px 50px 60px 93px;
    column-gap: 4px;
  }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_title {
    grid-column: 3 / 7;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes {
    grid-column: 7;
    grid-row: 1;
    padding-left: 0 !important;
    padding-right: 0;
    justify-content: flex-start;
    position: relative;
  }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes .__v3c_notes_val { flex: 0 0 43px; text-align: right; margin-right: 4px; }
  /* 첫째 줄 우측 노트수가 뭔지 인식이 어려워 'notes' 라벨을 grade 자리(46px 좌측정렬)에. (빈 cell '-' 제외) */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes:not(.__v3c_notes_empty)::after {
    content: 'notes';
    flex: 0 0 46px;
    text-align: left;
    font-size: 0.78em;
    color: var(--v3-text-disabled);
  }
  /* BP 셀(bpLayout) — 숫자와 라벨이 두 박스로 갈라져 보이던 것("4"|"miss")을 PC 와 같이 "BP 4" 한 덩어리로.
     라벨 박스(::after) 자체를 없애고 숫자 span 폭도 그만큼 넓힌다. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_bp::after { content: none; }
  /* Scoring 배지 모드에는 노트수가 없다. 라벨 공간은 유지해 배지 정렬을 보존한다. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_layout::after { visibility: hidden; }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_bp .__v3c_notes_val { flex: 0 0 auto; }
  /* ★값(zasaLevel) — 2줄 모드에서만 notes 칸을 좌(★)/우(노트수 또는 BP)로 반분한다. PC 는 위 __v3c_notes_star{display:none}. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_split { justify-content: space-between; gap: 4px; }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_split .__v3c_notes_star {
    display: inline;
    flex: 1;
    min-width: max-content;
    text-align: right;
    white-space: nowrap;
    color: var(--v3-text-disabled);
    font-size: 0.85em;
  }
  /* 🔴 BP 없는 유저(AC/미재업로드 INF) — 이 ★ 는 level 칸(__v3c_level_zasa)에 같은 값이 이미 나와 **중복**이라
     2줄 모드에서도 다시 숨긴다. BP 있는 유저는 notes 칸이 BP 로 대체돼 ★ 가 유일 노출처라 그대로 둔다.
     이 한 줄이 빠지면 bp 없는 유저에게 ★ 가 두 번 보인다(v1 :2528-2532). */
  .__v3c_tbody:not(.__v3c_bplayout) .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_split .__v3c_notes_star {
    display: none;
  }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_split > .__v3c_notes_val,
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_notes.__v3c_notes_split > .__v3c_bp_wrap {
    flex: 1;
    min-width: 0;
  }
  /* 🔴 lamptext — grid 좌표와 출처 뱃지의 기준점이 **한 몸**이다(정본 §6.2.3 각주 ⓔ).
     모바일 뱃지가 이 셀 기준 `right:100%` 로 뜨므로 좌표를 옮기면 뱃지도 따라 옮겨야 한다. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_lamptext {
    grid-column: 4;
    grid-row: 2;
    position: relative;
    overflow: visible;
  }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_ratebar { grid-column: 5 / 7; grid-row: 2; }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_num {
    grid-column: 7;
    grid-row: 2;
    padding-left: 0 !important;
    padding-right: 0;
    justify-content: flex-start;
  }
  /* EX스코어(43px 우측정렬) / 등급차(46px 좌측정렬) — 가운데 경계에서 만나 행마다 각각 정렬된다. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_num .__v3c_num_val { flex: 0 0 43px; text-align: right; margin-right: 4px; }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_num .__v3c_grade   { flex: 0 0 46px; text-align: left; margin-left: 0; }

  /* 🔴 출처 뱃지 PC↔모바일 스왑 — v1 은 `@media (max-width:820px)`(styles.css:1300-1312) 였고
     S3-2b-2 가 여기로 옮겼다(정본 §6.2.3 각주 ⓔ). 그 규칙의 전제가 뷰포트가 아니라 **2줄 레이아웃**이기 때문이다
     — v1 주석이 「LAMP텍스트 셀(col4) 왼쪽 빈칸(col3/row2)에 띄움」이라 적는데 col3/row2 는 2줄 grid 전용 좌표다.
     🔴 `@media` 로 되돌리면 전환점이 820/600 으로 갈려 양쪽 띠에서 깨진다. T-6·T-15 가 잡는다. */
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_num .__v3c_srcwrap_pc { display: none; }
  .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_lamptext .__v3c_srcwrap_mo {
    display: inline-flex;
    position: absolute;
    right: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-right: 4px;
    z-index: 2;
  }

  /* 🔴 **2줄 모드에서는 램프칸 *밖* 왼쪽으로 내보낸다**(2026-09-19 세션 51).
     그 폭에서는 램프칸이 **45px** 뿐이라 `left: 0` 으로는 램프 글자와 겹친다.
     칸 왼쪽(둘째 줄의 빈 자리)으로 빼면 겹치지 않는다 — v1 도 같은 자리에 띄운다
     (`styles.css:2494-2503` 「LAMP텍스트 셀 왼쪽 빈칸」). */
  .__v3c_layoutmode .__v3c_tr:not(.__v3c_recrow) > .__v3c_cell.__v3c_lamptext .__v3c_layout_badge {
    left: auto;
    right: 100%;
    margin-right: 4px;
  }

  /* 🔴 **종전 notes 칸용 모바일 absolute 규칙은 없앴다**(2026-09-19 세션 51).
     종전 셀렉터는 `… .__v3c_notes .__v3c_layout_badge` 였고 목적은 「노트수를 안 밀리게
     flow 밖으로 뺀다」(v1 `styles.css:2494-2503`)였다. 배지가 **notes 칸을 떠나 램프텍스트 칸으로**
     옮겨가면서(사용자 지시) 그 셀렉터는 **아무것도 안 가리키는 죽은 규칙**이 됐다.
     🔴 **T-19 컨테이너 쿼리 린트가 그것을 잡았다** — 미매칭 셀렉터 수가 13 → 14 로 늘어
     `F-8` 이 빨개졌다. 죽은 CSS 를 남기면 다음 사람이 「이미 처리돼 있다」고 읽는다.
     ⚠️ **램프칸용으로 되살리지 마라** — 램프텍스트는 2줄 모드에서 **둘째 줄**이고
     `right: 100%` 로 띄우면 같은 줄 왼쪽 셀과 겹친다. 지금은 **inline 으로 둔다**(램프 글자만 조금 민다). */

  /* 🔴 Recent row 는 **여기서 아무것도 재정의하지 않는다** — PC·모바일이 **같은 격자**다
     (사용자 지정 2026-09-23: 「따로 그리지 말고 모바일 기준으로 통일」).
     기준 트랙은 위 `.__v3c_tbody.__v3c_rctbody` **한 곳뿐**이고, 그 값이 곧 모바일 값이다.
     ⚠️ 종전에 있던 `__v3c_rclampchange`·`__v3c_rcdjchange`·`__v3c_rcexchange`·`__v3c_rcbp` 배치 규칙
     넷은 그 클래스가 10열 개편으로 없어져 **죽은 CSS 가 되었으므로 지웠다**(T-19 린트가 잡는다). */
}

/* ===== C1 곡행 끝 ===== */

/* ===== C5 폴더 — S3-3a ===== */

/* 폴더 박스 — v1 styles.css:2046-2054 이식.
   🔴 `container` 를 **폴더 자신**에 준다(정본 §1.2.5 「폴더가 스스로 컨테이너다」).
      summary 는 그 **자식**이라 아래 `@container v3folder` 가 산다.
      **폴더 목록 래퍼에 두면 안 된다** — ⓐ 그 조상이 없는 자리(DBR·7차 모달)에서 전환이 통째로 죽고
      ⓑ 래퍼를 만드는 것은 S3-5 라 PD-4(단방향 의존)가 깨지며
      ⓒ 래퍼 폭은 폴더 border 1px×2 만큼 넓어 `v3row` 와 전환점이 2px 어긋난다.
   🔴 **G-7 부칙이 이 컨테이너에도 걸린다** — `.__v3c_folder` **자신**의 레이아웃은
      `@container v3folder` 로 못 바꾼다(컨테이너 쿼리는 조상만 질의한다). T-19 가 기계로 지킨다.
   ⚠️ **v1 의 `margin: 0 !important`(`:2038`·`:2040`)와 summary 의 `padding … !important`(`:2063`)는
      계승하지 않는다.** v1 주석이 스스로 「오소리 본체 panel CSS(<style> 안 details·summary 룰)의
      상속 차단」이라 밝히는데 **v3 셸에는 그 <style> 이 없다**(§7 Stats 는 PD-9 대로 신규 구현이라
      ohsorryRender 를 끌어오지 않는다). §1.2.3 의 램프 색 `!important` 미계승과는 별개 판단이다. */
.__v3c_folder {
  background: var(--v3-bg-folder);
  border: 1px solid var(--v3-border-subtle);
  border-top-width: 0;
  container: v3folder / inline-size;
}

/* 폴더끼리 붙을 때 인접 border 가 2px 로 굵어지는 것을 막는다 — 위 `border-top-width: 0` 과 한 쌍이다.
   ⚠️ v1(`:2041`)과 같은 셀렉터지만 **전제가 다를 수 있다** — v1 은 폴더가 `.__uprofile_pdfolders` 의
   직속 자식이고 그 앞에 다른 요소가 없다. v3 래퍼(S3-5)가 폴더 앞에 다른 요소를 두면
   `:first-child` 가 **아무 폴더에도 안 걸려 첫 폴더 위 border 가 사라진다.** 래퍼를 만들 때 확인할 것. */
.__v3c_folder:first-child {
  border-top-width: 1px;
}

.__v3c_folder[open] {
  background: var(--v3-bg-surface-hover);
}

/* 열린 폴더 summary — 셸 sticky 줄들 아래에 붙는다.
   🔴 **`sticky-top.js` 의 `register()` 에 넣지 않는다.** 그 스택은 **셸** sticky 줄의 합이고
      등록 순서가 곧 화면 순서라(`:27-33` 이 각 줄의 top 을 누적 배치한다) 폴더를 끼우면
      **셸 줄 전체의 top 이 밀린다.** 폴더는 `--v3-sticky-top` 을 **읽기만** 한다.
   `-3px` 은 서브픽셀 갭 보완(v1 `:2049` 계승 — 필터와 색이 다른 경우의 안전망).
   🔴 **z-index 3 의 근거는 v1 주석의 두 문장이다.**
      v1 `:2044` 은 먼저 「RATE cell 의 자식(rate_text z:2) 보다 위」를 적고 **그 다음에**
      「tabs(4)/header(5)/filter(6) 보다는 아래」를 적는다. **두 문장 다 v3 에서 살아 있는 요구다.**
      🔴 **종전 이 자리의 「`container-type` 이 폴더를 스택 컨텍스트로 만들어 summary 가 셸 줄 위로 못 뜬다」는
      거짓이었다**(2026-09-27 세션 110 헤드리스 실측 — Scoring 에서 summary 가 셸 줄을 덮은 지점 PC 4건·모바일 3건, 수정 후 0건).
      ⚠️ `contain` 의 computed 값(`none`)을 근거로 쓰지 마라 — `container-type` 은 `contain` 프로퍼티를 바꾸지 않아 그 값으로는 컨테인먼트 여부를 잴 수 없다. 근거는 겹침 실측이다.
      Scoring 처럼 열린 폴더가 여럿이면 앞 폴더 끝에서 밀려 올라간 summary 가 **같은 z 3** 이던 strip·탭줄 **위에**
      그려졌다(`docs/v3/probes/run-recs-sticky.mjs`). ⇒ 셸 sticky 줄은 `--v3-z-stickybar`(4)로 **한 층 위**다 —
      v1 의 「tabs 4 / filter 6 보다 아래」를 v3 토큰으로 되살린 것이다. **이 summary 를 4 이상으로 올리지 마라.** */
.__v3c_folder[open] > .__v3c_summary {
  position: sticky;
  top: calc(var(--v3-sticky-top, 0px) - 3px);
  z-index: var(--v3-z-sticky);
  background: var(--v3-bg-surface-hover);
}

/* 🔴 브라우저 기본 `scrollIntoView` 의 착지 위치 — v1 `styles.css:2065-2068` 이식.
   v1 은 CSS 변수 3개 합(header+tabs+filter)이었고 v3 는 `--v3-sticky-top` 단일 변수다.
   ⚠️ **미계승이 아니라 이식이다** — S3-7 의 `scrollRowIntoView` 는 v1 `folderScroll.js:135` 에
   `scrollIntoView` **폴백**을 갖고 있고 이 줄이 바로 그 폴백의 착지 지점을 정한다.
   빠뜨리면 폴백 경로에서 폴더가 sticky 줄 **뒤로 숨는다**(회귀 검토가 잡았다).
   `scroll-margin` 은 요소 자신의 속성이라 스크롤 컨테이너가 카드든 창이든 동일하게 작동한다.
   ⚠️ v1 에는 같은 속성이 **하나 더** 있다 — `styles.css:2792` 의 `.__dbr_folders .__uprofile_pdfolder`.
   그것은 **조상 스코프**라 정본 §6.2.3 각주 ⓐ 대로 **S3-12b** 몫이다. */
.__v3c_summary {
  scroll-margin-top: var(--v3-sticky-top, 0px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  height: 40px;
  padding: 0 10px 0 0;
  cursor: pointer;
  font-size: 13px;
  color: var(--v3-text-main);
  user-select: none;
  list-style: none;
}

.__v3c_summary::-webkit-details-marker {
  display: none;
}

.__v3c_summary::marker {
  content: '';
}

.__v3c_summary:hover {
  background: var(--v3-bg-surface-hover);
}

.__v3c_folderlabel {
  flex: 1;
  font-weight: 600;
  font-size: 15px;
}

.__v3c_foldercount {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--v3-text-dim);
  font-variant-numeric: tabular-nums;
}

.__v3c_foldercount b {
  color: var(--v3-accent-hover);
  font-weight: 700;
}

/* 2줄 모드 — 모바일 터치 영역 확보로 summary 를 40px → 47px (v1 `:2445-2448` 주석 그대로).
   🔴 **이 규칙을 `@container v3row` 안에 쓰면 죽은 규칙이다** — `v3row` 의 컨테이너는
      `.__v3c_tbody` 이고 summary 는 그 **자손이 아니라 형제**다. 직전 조각(S3-2b-2)이
      같은 종류의 함정에 물렸고(컨테이너가 자기 자신을 질의), 그래서 G-7 부칙과 T-19 가 생겼다.
   ⚠️ v1 은 이 규칙을 `.__uprofile_pdfolders`(폴더 목록 래퍼) 컨테이너 안에 뒀다 — v3 는 폴더 자신이다. */
@container v3folder (max-width: 600px) {
  .__v3c_summary {
    height: 47px;
  }
}

/* ===== C5 폴더 끝 ===== */

/* ═══════════════════════════════════════════════════════════════════════
   폴더 목록 래퍼 .__v3c_folderlist — S3-5
   v1 `.__uprofile_pdfolders`(styles.css:2038-2042) 이식.
   ═══════════════════════════════════════════════════════════════════════ */

/* 🔴 v1 의 `container-type: inline-size` 는 **가져오지 않는다.** 정본 §1.2.5 가 컨테이너를
      폴더 자신(`.__v3c_folder` = `v3folder`)으로 확정하고 래퍼에 두는 것을 **명시 기각**했다
      (조상이 없는 자리에서 전환이 죽고 · 전환점이 `v3row` 와 2px 어긋난다).
   🔴 `flex-direction: column` 은 장식이 아니다 — `.__v3c_folder` 가 `container-type: inline-size`
      (= `contain: inline-size`)라 **폭이 내용과 무관**해지므로, 래퍼를 row 방향으로 두면
      폴더가 0 으로 접힌다(정본 §1.2.5 「폴더 목록 래퍼의 방향」).
   🔴 래퍼 안에는 폴더만 넣는다 — `.__v3c_folder:first-child`(위 `:1352`)가 첫 폴더의 위 border 를
      되살리는데, 앞에 다른 요소가 있으면 그 규칙이 아무 폴더에도 안 걸린다. */
.__v3c_folderlist {
  display: flex;
  flex-direction: column;
}

/* 시리즈 폴더는 폴더 라벨이 이미 그 시리즈라 곡행의 시리즈 태그가 중복이다 — 숨긴다.
   v1 `.__pd_seriesfolders .__dbr_series { display: none }`(styles.css:2290) 이식.
   🔴 v1 은 축마다 **다른 래퍼 클래스**(`__pd_seriesfolders`/`__pd_lvlfolders`/`__pd_zasafolders`)로
      갈랐지만 v3 는 래퍼가 하나이고 축이 `data-axis` 다 — 정본 PD-15 의 「배제 사슬 대신 data-axis」.
   ⚠️ Level·Zasa 축에서는 이 태그가 **보여야 한다**(v1 도 그 축에서는 안 숨긴다). */
.__v3c_folderlist[data-axis='series'] .__v3c_series {
  display: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   PlayData 필터바 .__v3c_pdfilter — S3-6a
   v1 `.__uprofile_pdfilter`(styles.css:1890-1903) + 토글 버튼(:1969-2039) 이식.
   🔴 diff·sort 버튼은 마크업·스타일만 — 순환·롱프레스는 S3-6b 다. 검색창 CSS 는 S3-7 이 아래 절에 깔았다.
   🔴 새 토큰 0개 — 아래 색 15종 중 14종은 기존 토큰과 값이 같아 재사용한다(PD-7 이 처음으로 실측 참).
   ⚠️ `.__v3c_pickmenu` 의 그림자만 v1 `0 6px 18px rgba(0,0,0,.5)` 와 다르다 — 기존 토큰
   `--v3-shadow-popover`(`0 4px 12px rgba(0,0,0,0.4)`)로 근사한다(회귀 검토 지적, 새 토큰을
   만들지 않기로 판단했다).
   ═══════════════════════════════════════════════════════════════════════ */

.__v3c_pdfilter {
  display: flex;
  gap: var(--v3-space-4);
  /* 🔴 **full-bleed — 위 탭줄(`.__v3c_pdtabs`)과 좌우를 맞춘다**
     (2026-09-21 세션 71, 사용자 실기 지시: 「검색창과 버튼이 있는 행은 좌우 넓이를 탭과 같이 맞춰라」).
     🔴 **값은 `.__v3sh_main` 의 padding 과 같은 토큰이어야 한다** — `.__v3c_pdbody` 의 같은 주석을 봐라.
     ⚠️ `margin: 0` 을 쓰면 안 된다 — 이 규칙을 지우는 것과 같고, 그래도 **에러가 안 난다.**
     ⚠️ 좌우 padding 은 그대로 0 이다 — 검색창 왼쪽과 맨 오른쪽 버튼이 탭줄 가장자리에 맞는다. */
  margin-block: 0;
  margin-inline: calc(-1 * var(--v3-main-pad));
  /* 🔴 높이 32px — 검색창 28px(`.__v3c_searchbox`) + 위아래 2px(2026-09-27 세션 106, 사용자 지시 · 종전 8px 로 44px).
     검색창 높이를 바꾸면 이 줄 높이도 같이 바뀐다. */
  padding: var(--v3-space-1) 0;
  font-size: 12px;
  position: sticky;
  /* 🔴 실제 top 은 sticky-top.js 가 인라인으로 덮어쓴다. var() 는 폴백이다(.__v3c_strip 과 같은 패턴). */
  top: var(--v3-sticky-top, 0px);
  z-index: var(--v3-z-stickybar); /* 🔴 폴더 summary(z 3)보다 위 — 되돌리면 Scoring 폴더 제목이 이 줄을 덮는다(세션 110) */
  /* v1 은 rgb(31,31,31) — 위 두 sticky 줄(strip·탭줄)과 같은 --v3-bg-panel(#1f1f1f)이다. */
  background: var(--v3-bg-panel);
}

.__v3c_pdfilter[hidden] { display: none; }

.__v3c_pdtoggle {
  background: var(--v3-bg-search);
  border: 1px solid var(--v3-border-subtle);
  color: var(--v3-text-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: var(--v3-radius-md);
  /* 모바일 롱프레스(S3-6c) 충돌 방지 + 글자 선택 방지(v1 :1994-1998)는 토글 전체에 걸린다. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
/* 🔴 touch-action:none 은 diff·sort 두 개에만 건다 — v1 은 `.__pd_foldermodebtn, .__pd_difffilter,
   .__pd_sortbtn` 셋에만 주고(styles.css:2001) 롱프레스가 없는 배치 버튼은 일부러 뺐다.
   전체에 걸면 배치 버튼 위에서 손가락을 끌 때 페이지 스크롤이 막혀 v1 대비 퇴행이다(회귀 검토 지적). */
.__v3c_difffilter, .__v3c_sortbtn { touch-action: none; }
.__v3c_pdtoggle:hover { background: var(--v3-border-subtle); color: var(--v3-text-strong); }
.__v3c_pdtoggle.active {
  background: transparent;
  border-color: var(--v3-accent-hover);
  color: var(--v3-accent-hover);
  font-weight: 700;
}

.__v3c_difffilter { margin-left: auto; box-sizing: border-box; text-align: center; }

/* series 축이 아니면 diff 필터는 의미가 없다 — 숨기고 sortbtn 이 margin-left:auto 를 이어받는다(v1 :1981-1986). */
.__v3c_pdfilter[data-axis="level"] .__v3c_difffilter,
.__v3c_pdfilter[data-axis="zasa"] .__v3c_difffilter,
.__v3c_pdfilter[data-axis="rstar"] .__v3c_difffilter { display: none; }
.__v3c_pdfilter[data-axis="level"] .__v3c_sortbtn,
.__v3c_pdfilter[data-axis="zasa"] .__v3c_sortbtn,
.__v3c_pdfilter[data-axis="rstar"] .__v3c_sortbtn { margin-left: auto; }

/* 배치 토글 — OFF 여도 또렷하게(다른 토글은 always active 라 무관, v1 :2020-2025). */
.__v3c_layoutbtn { background: transparent; border-color: var(--v3-border-strong); color: var(--v3-text-main); font-weight: 600; }
/* 「배치 ON/OFF」 는 좁은 폭에서도 한 줄 — 필터바·Recs 서브탭 두 자리 모두. 글자가 접히지 않게 줄어들지도 않는다. */
.__v3c_layoutbtn, .__v3c_recslayoutbtn { white-space: nowrap; flex-shrink: 0; }

/* 롱프레스 선택 목록 — S3-6b/6c 가 소비한다. 스타일만 지금 깐다(v1 :1999-2018). */
.__v3c_pickmenu {
  position: fixed;
  z-index: var(--v3-z-toast);
  min-width: 132px;
  padding: 4px;
  background: var(--v3-bg-surface);
  border: 1px solid var(--v3-border-strong);
  border-radius: var(--v3-radius-xl);
  box-shadow: var(--v3-shadow-popover);
}
.__v3c_pickitem {
  padding: 8px 12px;
  font-size: 13px;
  color: var(--v3-text-secondary);
  border-radius: var(--v3-radius-md);
  white-space: nowrap;
  cursor: pointer;
}
.__v3c_pickitem.active { color: var(--v3-text-strong); font-weight: 700; }
.__v3c_pickitem.hover { background: var(--v3-accent); color: var(--v3-text-strong); }

/* 배치 뱃지 노출 스위치(PC) — 데이터는 S3-14, 클래스 토글은 여기(v1 :2200-2201). */
.__v3c_layoutmode .__v3c_layout_val { display: inline-block; }

/* ═══════════════════════════════════════════════════════════════════════
   PlayData 검색 .__v3c_searchwrap · 드롭다운 — S3-7
   v1 `styles.css:1905-1969` 이식. 토큰 매핑은 정본 §6.2.3 S3-7 의 PD-7 칸이 박아 뒀다.
   🔴 그림자는 `--v3-shadow-popover` 를 **재사용한다 — 신설하지 마라.** v1 은 `0 4px 12px rgba(0,0,0,.5)`
   (`styles.css:1942`)이고 토큰은 `0 4px 12px rgba(0,0,0,0.4)` 로 **기하가 같고 알파만 다르다.**
   S3-6a 가 피커 메뉴에서 **이미 같은 판정을 내렸고**(위 `:1444-1447`), 검색 드롭다운과 피커는
   **같은 필터바에 나란히 뜨는 두 팝오버**라 여기서 신설하면 **한 줄의 두 메뉴가 서로 달라진다.**
   대가는 알파 0.5 → 0.4 이탈 하나다.
   🔴 `z-index` 는 `--v3-z-content` 다 — v1 의 `10`(`styles.css:1935`)을 옮기지 마라. 그 값은 v1 필터바(z:6)
   기준이고, v3 는 필터바가 `--v3-z-sticky` 로 **스택 컨텍스트를 만들어**(`:1459`) 그 안에서만 겨루면 된다.
   ═══════════════════════════════════════════════════════════════════════ */

.__v3c_searchwrap {
  position: relative;
  flex: 1 1 120px;
  min-width: 100px;
  max-width: 240px;
}

.__v3c_searchbox {
  width: 100%;
  /* 🔴 27px + 12px 이었다 — iOS 가 포커스 때 페이지를 확대했다(`v3/styles.css` 「iOS 자동 확대 방지」).
     16px 미만으로 되돌리지 마라. 높이는 1px 만 늘려 27 → 28px 로 잡는다:
     28 - 4*2(padding) - 1*2(border) = 18px 이고 `line-height:1` 이 줄상자를 16px 로 묶어 여유 2px 이 남는다.
     ⚠️ 형제 토글 버튼(`.__v3c_pdtoggle`)은 높이를 안 주고 `.__v3c_pdfilter` 의 flex `align-items:stretch`
     로 이 값에 맞춰 늘어난다 — 그래서 한 줄이 그대로 정렬된 채 1px 만 자란다. */
  height: 28px;
  box-sizing: border-box;
  font-family: inherit;
  font-size: 16px;
  line-height: 1;
  padding: 4px 10px;
  outline: none;
  background: var(--v3-bg-search);
  border: 1px solid var(--v3-border-subtle);
  color: var(--v3-text-main);
  border-radius: var(--v3-radius-md);
}

.__v3c_searchbox:focus {
  border-color: var(--v3-accent);
}

.__v3c_searchdrop {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  display: none;
  max-height: 155px;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background: var(--v3-bg-search);
  border: 1px solid var(--v3-border-subtle);
  border-radius: var(--v3-radius-md);
  z-index: var(--v3-z-content);
  box-shadow: var(--v3-shadow-popover);
  /* 5 항목 높이 = 31×5. JS 는 전건을 렌더한다 */
}

.__v3c_searchdrop::-webkit-scrollbar {
  display: none;
}

.__v3c_searchitem {
  padding: 6px 10px;
  font-size: 13px;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--v3-text-main);
  border-bottom: 1px solid var(--v3-border-subtle);
}

.__v3c_searchitem:last-child {
  border-bottom: none;
}

.__v3c_searchitem:hover {
  background: var(--v3-bg-search-item-hover);
  color: var(--v3-accent);
}

.__v3c_searchempty {
  padding: 6px 10px;
  font-size: 12px;
  color: var(--v3-text-disabled);
  font-style: italic;
}

.__v3c_searchbadge {
  color: var(--v3-text-search-badge);
  font-size: 11px;
}

/* ===== Recent — S3-9b ===== */

/* 절 1 — DJ 레터 색표 */
.__v3c_djletter-AAA,
.__v3c_djletter-AA  { color: var(--v3-lamp-ex); }        /* #dcaf45 */
.__v3c_djletter-A   { color: var(--v3-grade-c); }         /* #52a447 */
.__v3c_djletter-B   { color: var(--v3-state-info); }      /* #74c0fc */
.__v3c_djletter-C,
.__v3c_djletter-D   { color: var(--v3-text-dim); }        /* v1 #888 → #868e96 스냅 */
.__v3c_djletter-E,
.__v3c_djletter-F   { color: var(--v3-state-danger); }    /* #ff6b6b */
.__v3c_djletter-na  { color: var(--v3-text-disabled); }   /* #6c757d — 표에 없는 값·'-' */

/* 절 2 — 램프 텍스트 색의 span 스코프 변형 */
/* 🔴 Recent 램프 전이(`AC→EC`)의 **글자별** 색. 위 `.__v3c_tr > .__v3c_cell.__v3c_lamptext-*` 는
   **셀 자체**에 램프 클래스가 붙는 구조(`chartRow`·`chartRecRow`)를 위한 것이고, Recent 는 한 칸에
   **두 램프**가 들어가므로 span 마다 색이 필요하다.
   🔴 종전 셀렉터는 `.__v3c_cell.__v3c_rclampchange` 였는데 **그 셀 클래스가 10열 개편으로 없어졌다.**
   그대로 뒀으면 램프 색이 통째로 죽은 채 초록 테스트만 남는다(2026-09-23 세션 80).
   🔴 세션 90 에 prev(`NP→`)가 `__v3c_rclampprev` 칸으로 갈라지며 그 칸엔 색 규칙이 없어 prev 램프가 전부
   기본 글자색(흰색)으로 보였다 — 두 칸 모두에 건다(세션 106). */
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-NP ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-NP  { color: var(--v3-text-faint); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-F  ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-F   { color: var(--v3-diff-another); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-AC ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-AC  { color: var(--v3-lamp-ac); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-EC ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-EC  { color: var(--v3-lamp-ec); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-NC ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-NC  { color: var(--v3-lamp-nc); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-HC ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-HC  { color: var(--v3-text-main); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-EX ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-EX  { color: var(--v3-lamp-ex); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-FC ,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-FC  { color: var(--v3-state-info); }
.__v3c_cell.__v3c_lamptext > .__v3c_lamptext-PFC,
.__v3c_cell.__v3c_rclampprev > .__v3c_lamptext-PFC { color: var(--v3-state-info); }

/* ── Recent 10열 개편 — 새 칸들의 글자 스타일 (2026-09-23 세션 80) ───────────── */

/* DJ prev(`B→`) — 🔴 rate 바 안 글자와 **같은 크기·굵기**로 맞춘다(사용자 지정).
   색은 `__v3c_djletter-*` 가 등급별로 준다. */
.__v3c_cell.__v3c_rcdjprev {
  font-size: 11.5px;
  font-weight: 700;
}

/* ★ 수치 — 🔴 어두운 색(사용자 지정). 종전 규칙은 `.__v3c_numtop .__v3c_recdiff` 라
   10열 개편으로 `__v3c_numtop` 래퍼가 없어지면서 안 걸렸다. */
.__v3c_cell.__v3c_rcstar .__v3c_recdiff {
  font-size: 10px;
  color: var(--v3-text-disabled);
}

/* BP 라벨 — 🔴 어두운 색(사용자 지정). 숫자(`__v3c_excur`)는 그대로 둔다. */
.__v3c_cell.__v3c_rcbpprev .__v3c_rcbp_label {
  color: var(--v3-text-disabled);
}

/* 🔴 prev 화살표(`→`)는 **전부 어두운 색**(사용자 지정 2026-09-23). 값 글자의 색을 따라가지 않는다.
   그래서 `recent-cells.js` 가 화살표를 **별도 span** 으로 뺐다 — 값 문자열에 이어 붙이면 색을 못 준다. */
.__v3c_cell .__v3c_arrow {
  color: var(--v3-text-disabled);
}

/* 🔴 DJ prev **글자는 등급색 그대로**다(`__v3c_djletter-*`). 회색이 되는 것은 **화살표뿐**이고
   그것은 위 `.__v3c_arrow` 가 맡는다. ⚠️ 2026-09-23 에 글자까지 회색으로 했다가 정정했다 —
   여기에 `color` 를 다시 넣지 마라. */

/* BP prev 칸 — 🔴 `BP 164 →` 가 **줄바꿈되지 않게** 한다(사용자 지정). 칸이 좁아도 한 줄로 둔다. */
.__v3c_cell.__v3c_rcbpprev {
  white-space: nowrap;
}

/* 🔴 9열은 **넘쳐도 잘리지 않는다**(사용자 지정 2026-09-23).
   ⚠️ 원인은 `overflow` 가 아니라 **다음 셀(10열)의 배경**이었다 — 셀마다 배경이 있어서 나중에 그려지는
   10열이 넘친 글자를 덮었다. 그래서 ⓐ 배경을 **투명**으로 두고(행 배경이 뒤를 받친다)
   ⓑ `z-index` 로 위에 올린다. 글자만 넘치고 배경은 아무것도 가리지 않는다. */
.__v3c_cell.__v3c_rcbpprev,
.__v3c_cell.__v3c_rcexcur {
  overflow: visible;
  white-space: nowrap;
  background: transparent;
  position: relative;
  z-index: 1;
}

/* 절 3 — prev / 현재값 / BP 라벨 */
.__v3c_recprev {
  color: var(--v3-text-disabled);
  font-size: 10.5px;
  margin-right: 4px;
  font-weight: 400;
}

/* 🔴 종전 `__v3c_rcexchange > __v3c_recprev` 의 absolute 규칙도 같은 이유로 지웠다 —
   prev 는 이제 col8(EX)·col9(BP) 에 자기 열이 있다(세션 90 10열 개편 뒤 번호). */

/* BP 현재값만 밝게 — 라벨·prev 는 회색(종전 `__v3c_rcbp` 계약 그대로). */
.__v3c_cell.__v3c_rcbpcur .__v3c_excur { color: var(--v3-text-main); }

/* 절 4 — 날짜 헤더 · 네비 (v1 styles.css:1639-1672 이식, 커밋 43f192c 기준) */
/* 🔴 grid `1fr auto 1fr` 이 계약이다 — v1 자기 주석(:1638)이 「nav 묶음은 정확히 중앙, 곡수는 우측 끝」이라
      못박았다. flex + space-between 으로 바꾸면 nav 가 중앙에서 벗어나고 에러가 없다.
   ⚠️ v1 에 없는 것을 더하지 마라 — 배경·테두리·padding 은 v1 헤더에 **없다**. */
.__v3c_rcheader {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px;
  margin: 4px 0 8px;
  font-size: 12px;
  color: var(--v3-text-muted);          /* v1 #adb5bd */
}

/* 🔴 `grid-column: 2` 가 중앙 배치의 전부다. 빼면 1열로 밀리고 에러가 없다. */
.__v3c_rcheader_nav {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* v1 은 이 둘에 CSS 규칙이 0건이고 인라인 style 로 처리한다(user/tabs/recent.js:258·:261).
   v3 는 G-8 취지대로 클래스로 뽑는다 — 내용은 그 인라인 그대로이고 **색은 하나도 없다**. */
.__v3c_rcheader_right {
  grid-column: 3;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* 🔴 `grid-column: 3` 은 `_right` 안에 중첩돼 있는 동안 무효다(grid item 이 아니다).
      v1 그대로 남긴다 — 「죽은 규칙」으로 지우지도, 그것에 의존하지도 마라. */
.__v3c_rcheader_count {
  grid-column: 3;
  text-align: right;
  color: var(--v3-text-dim);            /* v1 #868e96 */
}

/* 이전/다음 날짜 nav **버튼**(< / >). 🔴 컨테이너가 아니라 버튼이다 — v1 은
   `<button class="__uprofile_rcnav">` 이고 데이터가 없으면 그 버튼 자체가 없다(v1 :1659 자기 주석). */
.__v3c_rcnav {
  background: transparent;
  border: none;
  color: var(--v3-text-muted);          /* v1 #adb5bd */
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 8px;
  border-radius: 3px;
}
/* ⚠️ `--v3-border-subtle`(#2d323c) 을 배경으로 쓴다 — 이름은 border 지만 **값이 v1 과 같고**
      신설 토큰 0개가 이 조각의 계약이다(정본 §6.2.3 S3-9b). */
.__v3c_rcnav:hover {
  background: var(--v3-border-subtle);  /* v1 #2d323c */
  color: var(--v3-text-strong);         /* v1 #fff */
}

/* ===== Recent 끝 ===== */

/* ===== Stats — S3-10b ===== */
/* U-2 확정(2026-09-17, 사용자 결정): Stats 막대·표의 색을 **C3 토큰으로 통일**한다.
   v1 은 색을 인라인 hex 로 박았지만(`ohsorryRender.js:1088`·`:1104`) v3 는 토큰만 본다.
   ⇒ v1 대비 HARD 가 빨강→흰색, CLEAR 가 하늘→파랑으로 눈에 띄게 바뀐다. 의도된 변경이다.

   🔴 blink 3종(F·FC·PFC)은 **정적 대표색으로 내린다** — 막대는 면적이라 깜빡이면 못 읽는다.
      정적 대체를 새로 정하지 않았다. `prefers-reduced-motion` 이 쓰는 것과 **같은 값**을 쓴다
      — 단 FC 는 예외: 2026-09-30 사용자 결정으로 CL 파랑 → HC 흰색 150deg 그라데이션(아래 .__v3c_statslamp-FC).
      (아래 `--v3c-stats-bg` 정의를 C3 의 reduced-motion 블록과 나란히 두고 비교하라).

   🔴 색 클래스 하나가 **두 맥락**에서 쓰인다 — 표 라벨은 글자색, 범례 칩·세그먼트는 배경이다.
      그래서 클래스는 커스텀 프로퍼티만 싣고 소비는 맥락이 한다(`--v3c-cell-bg` 와 같은 기제).
      `--v3c-stats-bg` 는 그라디언트일 수 있고 `--v3c-stats-fg` 는 **반드시 단색**이다. */

.__v3c_stats {
  container: v3stats / inline-size;
  margin: 0 0 10px;
  border: 1px solid var(--v3-border-subtle);
  border-radius: var(--v3-radius-md);
  background: var(--v3-bg-panel);
}

.__v3c_statssummary {
  cursor: pointer;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 700;
  color: var(--v3-text-strong);
  list-style: none;
}
.__v3c_statssummary::-webkit-details-marker { display: none; }
.__v3c_statssummary::after {
  content: '▼';
  float: right;
  font-size: 10px;
  color: var(--v3-text-dim);
}
.__v3c_stats[open] > .__v3c_statssummary::after { content: '▲'; }

.__v3c_statsbody { padding: 0 12px 12px; }

/* 뷰 하나(그래프/표). 전환은 `hidden` 속성 토글이고 CSS 는 그것만 존중한다. */
.__v3c_statspane[hidden] { display: none; }

/* ── 표 ── */
.__v3c_statstablewrap {
  overflow-x: auto;
}

.__v3c_statstable {
  width: auto;
  min-width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.__v3c_statsth {
  text-align: center;
  padding: 4px 6px;
  font-size: 11px;
  font-weight: 700;
  color: var(--v3-text-dim);
  border-bottom: 1px solid var(--v3-border-subtle);
  white-space: nowrap;
}
.__v3c_statslabel {
  padding: 3px 6px;
  font-weight: 600;
  /* 🔴 그라디언트를 못 쓰는 자리라 fg(단색)를 본다. 정의가 없으면 본문색으로 떨어진다. */
  color: var(--v3c-stats-fg, var(--v3-text-muted));
}
.__v3c_statsrowlabel {
  text-align: left;
  padding: 3px 6px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--v3-text-muted);
}
.__v3c_statsnum {
  padding: 3px 6px;
  text-align: right;
  color: var(--v3-text-muted);
  white-space: nowrap;
}

/* ── 막대 ── */
.__v3c_statsgroup { margin-top: 10px; }

.__v3c_stats[data-selected-kind="lamp"] .__v3c_statspane [data-kind="dj"],
.__v3c_stats[data-selected-kind="dj"] .__v3c_statspane [data-kind="lamp"] { display: none; }

.__v3c_statslegend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-bottom: 6px;
  font-size: 10px;
  color: var(--v3-text-dim);
  min-width: 0;
}
.__v3c_statslegendrow {
  display: grid;
  grid-template-columns: 42px 26px minmax(0, 1fr);
  gap: 6px;
}
.__v3c_statslegendrow > .__v3c_statslegend { grid-column: 3; }
.__v3c_statslegitem {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.__v3c_statschip {
  display: inline-block;
  width: 5px;
  height: 9px;
  border-radius: 1px;
  background: var(--v3c-stats-bg, transparent);
}

.__v3c_statsbarrow {
  display: grid;
  grid-template-columns: 42px 26px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  margin-bottom: 3px;
  font-size: 11px;
  line-height: 1;
}
/* 🔴 `hidden` 은 `display:flex` 에 지므로 명시로 덮는다 — 안 덮으면 0 곡 행이 그대로 보인다.
   v1 은 인라인 `display:none` 이라 이 문제가 없었다(`ohsorryRender.js:1108`). */
.__v3c_statsbarrow[hidden] { display: none; }
.__v3c_statsbarrow > [data-kind] { min-width: 0; }

.__v3c_statsbarlabel {
  flex-shrink: 0;
  width: 42px;
  color: var(--v3-text-dim);
  font-variant-numeric: tabular-nums;
}
.__v3c_statsbartotal {
  flex-shrink: 0;
  width: 26px;
  text-align: right;
  color: var(--v3-text-muted);
  font-variant-numeric: tabular-nums;
}
.__v3c_statsbartrack {
  min-width: 0;
  height: 12px;
  display: flex;
  border-radius: 2px;
  overflow: hidden;
  background: var(--v3-bg-search);
}
.__v3c_statstable .__v3c_statsrowmeta {
  display: inline-grid;
  grid-template-columns: 42px 26px;
  width: 68px;
  align-items: center;
}
.__v3c_statstable .__v3c_statsbarlabel { text-align: left; color: var(--v3-text-muted); }
@container v3stats (min-width: calc(600px - 2px)) {
  .__v3c_stats .__v3c_statskinds { display: none; }
  .__v3c_stats .__v3c_statslegendrow,
  .__v3c_stats .__v3c_statsbarrow {
    grid-template-columns: 42px 26px minmax(0, 1fr) minmax(0, 1fr);
  }
  /* 좁은 화면 숨김 규칙(.__v3c_stats[data-selected-kind] .__v3c_statspane [data-kind])보다 특이성이 높아야 복원된다. */
  .__v3c_stats[data-selected-kind] .__v3c_statspane .__v3c_statslegend[data-kind],
  .__v3c_stats[data-selected-kind] .__v3c_statspane .__v3c_statsbartrack[data-kind] { display: flex; }
  .__v3c_stats .__v3c_statslegend[data-kind="lamp"],
  .__v3c_stats .__v3c_statsbartrack[data-kind="lamp"] { grid-column: 3; }
  .__v3c_stats .__v3c_statslegend[data-kind="dj"],
  .__v3c_stats .__v3c_statsbartrack[data-kind="dj"] { grid-column: 4; }
  .__v3c_stats .__v3c_statstable { width: 100%; table-layout: fixed; }
  .__v3c_stats .__v3c_statstable col.__v3c_statslevelcol { width: 80px; }
  .__v3c_stats .__v3c_statstable col[data-kind="lamp"] { width: calc((100% - 80px) / 16); }
  .__v3c_stats .__v3c_statstable col[data-kind="dj"] { width: calc((100% - 80px) / 8); }
  .__v3c_stats[data-selected-kind="lamp"] .__v3c_statspane colgroup:has(> col[data-kind="dj"]),
  .__v3c_stats[data-selected-kind="dj"] .__v3c_statspane colgroup:has(> col[data-kind="lamp"]) { display: table-column-group; }
  .__v3c_stats[data-selected-kind="lamp"] .__v3c_statspane thead th[data-kind="dj"],
  .__v3c_stats[data-selected-kind="dj"] .__v3c_statspane thead th[data-kind="lamp"] { display: table-cell; }
  .__v3c_stats[data-selected-kind="lamp"] .__v3c_statspane tbody td[data-kind="dj"],
  .__v3c_stats[data-selected-kind="dj"] .__v3c_statspane tbody td[data-kind="lamp"] { display: table-cell; }
  .__v3c_stats[data-selected-kind="lamp"] .__v3c_statspane col[data-kind="dj"],
  .__v3c_stats[data-selected-kind="dj"] .__v3c_statspane col[data-kind="lamp"] { display: table-column; }
  .__v3c_stats[data-selected-kind] .__v3c_statspane .__v3c_statstable [data-kind="dj"][data-dj="none"] { display: none; }
  .__v3c_stats .__v3c_statstable th[data-kind="lamp"][data-lamp="FC"],
  .__v3c_stats .__v3c_statstable td[data-kind="lamp"][data-lamp="FC"] { border-right: 1px dotted var(--v3-border-subtle); }
}
.__v3c_statsseg {
  height: 100%;
  background: var(--v3c-stats-bg, transparent);
}
/* 세그먼트도 같다 — flex 아이템이라 `hidden` 만으로는 안 사라진다. */
.__v3c_statsseg[hidden] { display: none; }

/* ── 색 — 램프 9종. C3(`--v3-lamp-*`)만 본다 ── */
.__v3c_statslamp-NP  { --v3c-stats-bg: var(--v3-lamp-np);  --v3c-stats-fg: var(--v3-text-dim); }
.__v3c_statslamp-AC  { --v3c-stats-bg: var(--v3-lamp-ac);  --v3c-stats-fg: var(--v3-lamp-ac); }
.__v3c_statslamp-EC  { --v3c-stats-bg: var(--v3-lamp-ec);  --v3c-stats-fg: var(--v3-lamp-ec); }
.__v3c_statslamp-NC  { --v3c-stats-bg: var(--v3-lamp-nc);  --v3c-stats-fg: var(--v3-lamp-nc); }
.__v3c_statslamp-HC  { --v3c-stats-bg: var(--v3-lamp-hc);  --v3c-stats-fg: var(--v3-lamp-hc); }
.__v3c_statslamp-EX  { --v3c-stats-bg: var(--v3-lamp-ex);  --v3c-stats-fg: var(--v3-lamp-ex); }

/* 🔴 blink 3종의 정적 대체 — C3 의 `prefers-reduced-motion` 블록과 **같은 값**이다.
   F 는 단색(f-dark), FC/PFC 는 3단 그라디언트(nc → hc → ex).
   ⚠️ 그라디언트는 글자색이 될 수 없으므로 fg 는 3단 중 **nc** 를 대표로 쓴다 —
      v1 의 FC 가 청록(`#00d4dd`)이었고 C3 에서 가장 가까운 것이 nc 다.
      NC(CLEAR)와 같은 색이 되지만 v1 도 HARD 와 FAILED 가 같은 `#dc3545` 였다.
      표에서 구분을 담당하는 것은 색이 아니라 **라벨**이다(정본 §7.2.1 ⑥). */
.__v3c_statslamp-F {
  --v3c-stats-bg: var(--v3-lamp-f-dark);
  --v3c-stats-fg: var(--v3-lamp-f-dark);
}
.__v3c_statslamp-FC {
  --v3c-stats-bg: linear-gradient(150deg, var(--v3-lamp-nc), var(--v3-lamp-hc));
  --v3c-stats-fg: var(--v3-lamp-nc);
}

/* ── 색 — 판정 5종 ──
   🔴 `Bminus` 는 B 이하 전부(B·C·D·E·F)를 접은 키다. 구간의 대표가 B 이므로 B 의 색을 그대로 쓴다.
   🔴 `none`(np)과 **다른 색**이어야 한다 — 같으면 막대에서 구별되지 않는다(`SC-10`).
   🔴 **램프 블록과 이 블록을 합치지 마라.** 오늘은 두 키 집합의 교집합이 0 이라 안 섞이지만,
      S3-10c 직전까지 `F` 가 겹쳤고 축이 늘면 또 겹친다 — 접두 분리가 그 예방이다(`SC-6`). */
.__v3c_statsdj-AAA   { --v3c-stats-bg: var(--v3-lamp-ex);   --v3c-stats-fg: var(--v3-lamp-ex); }
.__v3c_statsdj-AA    { --v3c-stats-bg: var(--v3-lamp-ex);   --v3c-stats-fg: var(--v3-lamp-ex); }
.__v3c_statsdj-A     { --v3c-stats-bg: var(--v3-lamp-ex);   --v3c-stats-fg: var(--v3-lamp-ex); }
.__v3c_statsdj-Bminus { --v3c-stats-bg: var(--v3-lamp-nc);   --v3c-stats-fg: var(--v3-lamp-nc); }
.__v3c_statsdj-none  { --v3c-stats-bg: var(--v3-lamp-np);   --v3c-stats-fg: var(--v3-text-dim); }

/* ===== Stats 끝 ===== */

/* ===== Recs 서브탭 줄 — S3-14b-1 ===== */
/* 🔴 **이 절은 파일 끝에 둔다. 위로 옮기지 마라.**
   2026-09-18 세션 46 이 이것을 `.__v3c_pdtab` 뒤(:628)에 넣었다가
   **그 아래 모든 좌표가 +43 으로 밀려** 문서·제품 주석 30곳의 인용이 무효가 됐고
   회귀 검토를 **여러 라운드** 태웠다. 파일 끝에 두면 아무것도 안 밀린다.
   ⚠️ **여기에 라운드 수를 적지 마라** — 이 레포가 이미 이름 붙인 실패모드다
      (phase-3 「라운드를 세는 문장을 쓰지 마라」). 라운드가 늘 때마다 이 주석이 낡는다.
   ⚠️ 셀렉터가 고유해서 캐스케이드상 위치가 무관하다 — 그래서 끝이 안전하다. */
/* 위 탭줄(`.__v3c_pdtabs`)의 한 단계 아래다 — 알약 꼴로 갈라 층을 구별한다.
   🔴 `[hidden]` 규칙이 **필수다** — `display:flex` 가 HTML `hidden` 속성의 UA `display:none` 을
      이겨서, 이 줄이 없으면 Recs 가 아닌 탭에서도 서브탭 줄이 그대로 보인다.
      `.__v3c_pdfilter[hidden]`(:1464)이 같은 이유로 있는 규칙이고,
      **화면으로만 보이고 테스트는 침묵하는** 종류다(jsdom 은 레이아웃이 없다).
   🔴 sticky 를 걸지 않았다 — `subnavEl` 은 sticky 스택에 **등록돼 있지 않다**(S3-14b-1 이 범위 밖으로 뒀다).
      여기에 `position:sticky` 만 넣으면 `--v3-sticky-top` 오프셋을 아무도 안 세워 탭줄과 겹친다.
      ⇒ **sticky 로 만들려면 CSS 가 아니라 `stickyTop.register` 부터** 해야 한다(S3-14c · H-1). */
.__v3c_pdsubtabs {
  display: flex;
  gap: var(--v3-space-3);
  overflow-x: auto;
  /* 🔴 높이 32px(테두리 포함) — 상단 메뉴줄·Compact Strip 과 맞춘다(2026-09-27 세션 106, 사용자 지시). */
  box-sizing: border-box;
  height: 32px;
  align-items: center;
  padding: 0 var(--v3-space-7);
  background-color: var(--v3-bg-panel);
  border-bottom: 1px solid var(--v3-border-subtle);

  /* 🔴 **full-bleed — 위 탭줄(`.__v3c_pdtabs`)과 시작선을 맞춘다**
     (2026-09-19 세션 51, 사용자 지시). 탭줄만 빼내면 서브탭이 16px 안쪽에 남아
     **두 줄의 왼쪽 끝이 어긋난다.**
     ⚠️ 위쪽과 달리 `margin-top` 은 안 준다 — 서브탭은 탭줄 바로 아래라 상단 패딩과 무관하다.
     ⚠️ 글자 위치는 그대로다 — 자신의 좌우 패딩(`--v3-space-7` = 12px)이
     위 `.__v3c_pdtab` 의 좌우 패딩과 **같은 값**이라 두 줄의 첫 글자가 세로로 맞는다. */
  margin-inline: calc(-1 * var(--v3-main-pad));
}

.__v3c_pdsubtabs[hidden] { display: none; }

.__v3c_pdsubtab {
  flex: 0 0 auto;
  padding: var(--v3-space-3) var(--v3-space-6);
  font-size: 12px;
  font-weight: 600;
  color: var(--v3-text-dim);
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--v3-radius-lg);
}

.__v3c_recslayoutbtn { margin-left: auto; }

.__v3c_pdsubtab:hover {
  color: var(--v3-text-muted);
}

/* 🔴 선택 상태가 **눈에 보여야 한다** — 이 조각의 화면 산출물이 「URL 의 origin 이 반영된다」인데
      그 반영의 유일한 표현이 `aria-current` 라, 이 규칙이 없으면 **테스트로만 보이고 사람 눈에는 안 보인다.**
      2026-09-18 회귀 1차가 잡은 자리다. */
.__v3c_pdsubtab[aria-current='page'] {
  color: var(--v3-accent);
  border-color: var(--v3-accent);
}

/* ===== Recs 서브탭 줄 끝 ===== */

/* ──────────────────────────────────────────────────────────────────────
   탭줄 가로 스크롤 — 스크롤바를 숨기고 「더 있다」를 그림자로 알린다
   (2026-09-19 세션 47 · 사용자 지시)

   🔴 **이 절은 파일 끝에 둔다. 위로 옮기지 마라.**
      중간에 끼우면 그 아래 좌표를 가리키는 인용이 전부 죽는다 — 세션 46 이 같은 일로
      회귀를 여러 라운드 태웠고 그 처방이 「끼울 자리가 자유롭다면 끝에 붙여라」였다.
      여기 셀렉터는 전부 고유해서 캐스케이드상 위치가 무관하다.

   기법 — `background-attachment` 의 `local`/`scroll` 조합이다. **JS 0줄.**
     · `local`  레이어(덮개)는 내용과 **같이 스크롤**한다
     · `scroll` 레이어(그림자)는 요소에 **고정**된다
     ⇒ 끝에 닿으면 덮개가 그림자를 덮어 **그림자가 저절로 사라진다.**
        양끝이 독립이라 「왼쪽에도 더 있다」까지 같이 얻는다.

   ⚠️ **덮개 색이 배경과 같아야 성립한다** — 두 줄 다 `--v3-bg-panel`(불투명)이라 맞는다.
      **배경을 반투명으로 바꾸면 이 절이 조용히 깨진다**(에러 없이 덮개가 비친다).
   ⚠️ **`background-color` 를 덮지 않으려고 `background-image` 로 따로 쓴다** —
      단축 `background` 를 쓰면 위 규칙들이 세운 색이 날아간다.
   ⚠️ **`background-origin: border-box`** — 서브탭 줄에 좌우 padding 이 있어
      기본값(`padding-box`)이면 덮개가 안쪽으로 밀려 끝이 어긋난다.
   ────────────────────────────────────────────────────────────────────── */

.__v3c_pdtabs,
.__v3c_pdsubtabs {
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* 구 Edge */

  background-image:
    linear-gradient(to right, var(--v3-bg-panel), transparent),
    linear-gradient(to left,  var(--v3-bg-panel), transparent),
    radial-gradient(farthest-side at 0 50%,    rgba(0, 0, 0, 0.5), transparent),
    radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, 0.5), transparent);
  background-position: left center, right center, left center, right center;
  background-size: 28px 100%, 28px 100%, 14px 100%, 14px 100%;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
  background-origin: border-box;

  scroll-snap-type: x proximity;
}

/* WebKit·Blink — 스크롤바 자체를 없앤다. 위 두 속성으로는 이 엔진이 안 듣는다. */
.__v3c_pdtabs::-webkit-scrollbar,
.__v3c_pdsubtabs::-webkit-scrollbar { display: none; }

/* 탭 경계에 부드럽게 멈춘다. `proximity` 라 강제가 아니고, 가까울 때만 붙는다. */
.__v3c_pdtab,
.__v3c_pdsubtab { scroll-snap-align: start; }

/* ===== Recs 폴더 — S3-14c ===== */
/* 추천곡·연습곡·SP·DBR·라이벌 폴더가 **공유하는** 스코프다. v1 `.__os_recfolder` 를 계승했고
   v3 에서 그 자리는 tbody 변형 `.__v3c_ostbody` 다(정본 「__v3c_ostbody」 절).
   🔴 **S3-15·S3-12b·4차는 이 CSS 를 재작성하지 마라** — v1 에서 이 클래스를 다는 자리가 아홉이다.
      재작성하면 이미 끝난 조각의 화면이 바뀐다.
   🔴 **이 절은 파일 끝에 둔다.** 위 `@container` 안 `.__v3c_tr.__v3c_recrow`(Recent 6트랙, 무스코프)
      보다 뒤여야 한다(정본 「소스 순서」 절). 셀렉터를 (0,3,0) 이상으로 써서 순서와 무관하게 이기지만
      이중 안전장치다. ⚠️ 그 무스코프 규칙을 좁히지 마라 — v1 도 무스코프이고(`styles.css:2558`)
      좁히면 S3-9 를 건드리고 v1 과 갈린다. R-2 가 이 순서를 지킨다.
   🔴 **옮기지 않은 것** — v1 `:2680`·`:2681`·`:2751`(`__os_notes_layout` 세트)은 **S3-14c 의 이월 잔여**다.
      🔴 **2026-09-19 세션 50 정정 — 종전 주석의 「S3-14d 몫」은 오귀속이었다**(§6.4 `[AMENDED]`).
      그 셋은 `.__os_recfolder` 스코프라 **Recs 행**이고, S3-14d 는 **PlayData 행**(`__v3c_layout_badge`,
      무스코프 + `.__v3c_layoutmode`)이라 축이 다르다. 정본 「`:2680` 을 **혼자** 옮기지 마라」 절이 정본이다.
      `:2680` 만 떼어 옮기면 Recs 행 PC 에서 배치 라벨이 사라진다(대신 보여줄 사본이 v3 에 없다).
      `:2635` 의 `grid-column: 1 / -1` 도 정본 §E 가 명시로 금지했다. R-10 이 둘 다 지킨다.
   🔴 색 리터럴을 쓰지 마라 — 전부 `--v3-*` 토큰이다(R-5). */

/* ── 트랙 · 행 골격 (v1 :2644-2650) ──
   🔴 트랙 폭은 tbody 가 **선언만** 하고 행이 소비한다(정본 §1.2.1) — 변형 셀렉터에 행을 이어 붙이면
      (0,3,0)이 되어 2줄 블록의 (0,2,0)을 이겨 버린다. 유연 트랙이 `minmax(72px, 1fr)` 인 것은
      v1 그대로이고 C1 base 의 `minmax(0, 1fr)` 과 **일부러 다르다**(추천 폴더는 곡명 최소폭을 잡는다). */
.__v3c_tbody.__v3c_ostbody { --v3c-tracks: 8px 40px minmax(72px, 1fr) 56px 40px 130px 92px; }
/* 배경은 cell 과 같은 색 — 트랙 사이로 다른 색이 비쳐 격자선처럼 보이는 것을 막는다(v1 :2648 주석). */
.__v3c_tbody.__v3c_ostbody .__v3c_tr { grid-template-rows: 20px 20px; background: var(--v3-bg-row); }

/* ── 셀 배치 (v1 :2657-2664) ──
   행 자체가 2줄 grid(20+20=40px)라 일반행용 `min-height: 40px` 를 0 으로 리셋한다 —
   안 그러면 줄마다 40px = 80px 가 된다. */
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell { padding-top: 0; padding-bottom: 0; min-height: 0; }
/* hover 시 cell 과 배경을 통일 — 트랙 사이 격자선 방지. */
.__v3c_tbody.__v3c_ostbody .__v3c_tr:hover { background: var(--v3-bg-surface); }
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_lamp { grid-column: 1; grid-row: 1 / 3; }
/* 🔴 `!important` 가 **필요하다** — C1 의 `.__v3c_cell.__v3c_level` 이 `padding: 2px 6px !important` 다.
   빼면 2줄 높이가 **조용히** 어긋난다. v1 도 같은 이유로 `!important` 를 쓴다(:2661). 지우지 마라. */
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_level { grid-column: 2; grid-row: 1 / 3; flex-direction: column; justify-content: center; gap: 0; padding: 0 4px !important; font-size: 17px; }
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_title { grid-column: 3; grid-row: 1 / 3; display: flex; align-items: center; }
/* 곡명 앞 zasa/rank — 20px 슬롯 가운데 정렬 (SP12 tier rank S/A/B 등). */
.__v3c_tbody.__v3c_ostbody .__v3c_zasa { text-align: center; }

/* ── 1줄: 해시태그 · 배치칸 / 2줄: NOTES · LAMP · RATE · SCORE (v1 :2669-2679) ── */
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_rectags { grid-column: 4 / 7; grid-row: 1; justify-content: flex-start; align-items: center; gap: 4px; font-size: 10px; color: var(--v3-accent-hover); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 🔴 PC 배치추천 뱃지 자리 — 2줄 NOTES 칸(col4 row2, 램프 글자 바로 왼쪽)에 왼쪽 붙임(세션 95 사용자 지시).
   종전 col7 row1 은 ★/BP(`__v3c_numtop`, num 칸 위로 absolute)와 겹쳤다. NOTES 칸은 PC Recs 행에서 비어 있다
   (보조 슬롯의 중복 표시는 PC 에서 숨긴다). 모바일은 아래 @container 가 col3 row2 로 옮긴다. */
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_layoutcell { grid-column: 4; grid-row: 2; justify-content: flex-start; align-items: center; position: relative; z-index: 1; }
/* 🔴 위 `position: relative; z-index: 1` 을 빼지 마라 — NOTES 칸이 **같은 격자 칸**(col4 row2)에 DOM 순서상 뒤에 그려져
   배경색으로 뱃지를 덮는다(세션 95 배포 뒤 사용자가 「뱃지가 사라졌다」로 발견). */
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_notes { grid-column: 4; grid-row: 2; }
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_lamptext { grid-column: 5; grid-row: 2; }
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_ratebar { grid-column: 6; grid-row: 2; }
.__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_num { grid-column: 7; grid-row: 2; }
/* 해시태그 칸 — 해시태그(좌, 넘치면 …) + 배치추천 뱃지(우측 정렬, 핑크 테두리) */
.__v3c_tbody.__v3c_ostbody .__v3c_rectags_list { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.__v3c_tbody.__v3c_ostbody .__v3c_reclayout { flex-shrink: 0; margin-left: auto; color: var(--v3c-layout-user-color, var(--v3-accent)); font-size: 9px; font-weight: 700; padding: 0 3px; border: 1px solid var(--v3c-layout-user-color, var(--v3-accent)); border-radius: 2px; line-height: 1.2; white-space: nowrap; }

/* ── ★난이도 / BP — num 셀 위에 absolute (v1 :2687·:2694-2709) ──
   num 셀이 이미 `position: relative`(C1 절)라 여기 별도 relative 가 필요없다.
   EX스코어(43px)·등급차(46px)와 **같은 폭**으로 그 위에 맞춘다. */
.__v3c_tbody.__v3c_ostbody .__v3c_recbp { font-size: 10px; color: var(--v3-text-disabled); white-space: nowrap; }
.__v3c_numtop { position: absolute; bottom: 100%; left: 0; display: flex; white-space: nowrap; pointer-events: none; }
.__v3c_numtop .__v3c_recdiff { flex: 0 0 43px; text-align: right; margin-right: 4px; font-size: 10px; color: var(--v3-text-disabled); }
.__v3c_numtop .__v3c_recbp { flex: 0 0 46px; text-align: left; }
/* ★ 이 없으면(SP11·SP10·rating 없는 곡) BP 가 ★ 자리(왼쪽 43px+4px)로 당겨진다 — 어느 화면이든 BP 를 제자리에 둔다(세션 120 곡 상세 → 10-11 추천곡으로 확대). */
.__v3c_numtop > .__v3c_recbp:first-child { margin-left: 47px; }
/* PC — 목표 rate 는 해시태그 칸 우측 끝. notes 자리 사본은 PC 에서 숨긴다(모바일에서 되살린다). */
.__v3c_tbody.__v3c_ostbody .__v3c_recgoal_pc { margin-left: auto; flex-shrink: 0; }
.__v3c_tbody.__v3c_ostbody .__v3c_cell.__v3c_notes .__v3c_recgoal { display: none; }
.__v3c_tbody.__v3c_ostbody .__v3c_estgoal_pc { margin-right: 4px; }

/* ── 섹션 헤더 (v1 :2635-2637) ──
   🔴 v1 의 `grid-column: 1 / -1` 은 **옮기지 않는다**(정본 §E 명시 금지 · R-10 이 지킨다).
   tbody 가 flex column 이라 full-width 가 자동이다.
   ⚠️ 클래스 이름은 v1 의 `__dbr_recsection` 이 아니라 `__v3c_recsection` 이다 — S3-14c 가 개명했다.
      CSS 테스트 셋이 전부 `.__v3c_` 정규식이라 `__dbr_` 이름은 통째로 검사 밖이었다. */
.__v3c_recsection { padding: 6px 10px 3px; font-size: 11.5px; font-weight: 700; background: var(--v3-bg-recsection); border-top: 1px solid var(--v3-border-subtle); letter-spacing: .3px; }
.__v3c_recsection:first-child { border-top: none; }
.__v3c_recseccnt { opacity: .55; font-weight: 500; font-size: 10.5px; }
/* 섹션 헤더의 부기 (v1 :2724 `.__os_v3note` 이식 — S3-15d-1).
   🔴 `--v3-text-dim` 은 `#868e96` 이고 **v1 값과 같다**(tokens.css:44) — 변환이 아니라 그대로다.
      리터럴 `#868e96` 을 쓰지 마라. R-5 가 Recs 절의 색 리터럴을 금지한다.
   🔴 `font-weight: 400` 을 지우지 마라 — 부모 `.__v3c_recsection` 이 `700` 이라
      빼면 부기가 본 라벨과 같은 굵기가 된다(v1 주석 `styles.css:2723` 「본 라벨보다 약하게」).
   ⚠️ DP 가 이 `<span>` 에 싣는 부기는 `short`(「후보 부족」) 하나다 — v1 의 「범위 확장·목표 하향」은
      `buildGrowthLandscape` 가 그 필드를 안 내서 DP 에서 안 뜬다(정본 §6.2.3 S3-15c 행 ⑤). */
.__v3c_recsecnote { font-size: 10px; font-weight: 400; color: var(--v3-text-dim); }

/* DBR 곡행 추천도 배지 (S3-12b-2a · v1 `styles.css:2618` `.__dbr_recbadge` 이식).
   🔴 **색은 여기서 주지 않는다** — 기호마다 다르므로 생산자(`playdata.js` 의 `buildDbrBadgeNode`)가
      `style.color = var(--v3-…)` 로 칠한다. R-5(Recs 절 색 리터럴 금지)를 우회하는 것이 아니라,
      **값이 데이터에 달린 색**이라 CSS 로 못 적는 것이다(`--v3c-lamp` 커스텀 프로퍼티와 같은 사정).
   ⚠️ v1 값 그대로다 — `font-weight: 700; margin-right: 3px`. */
.__v3c_recbadge { font-weight: 700; margin-right: 3px; }
/* SP 배치 배지 — v1 spHelpers.js:559-561 `__sp_arr` 인라인 스타일의 이식(S3-22c).
   🔴 색(color·border-color)은 여기서 주지 마라 — `sp-rec-row.js` 의 `SP_ARR_TOKEN` 이 배치별로 칠한다. */
.__v3c_sparr { display: inline-block; font-size: 9px; font-weight: 700; border: 1px solid; border-radius: 3px; padding: 0 3px; line-height: 1.5; cursor: pointer; white-space: nowrap; }

/* ── 폴더 헤더 컨트롤 ──
   🔴 v1 은 이 셋에 CSS 규칙이 **0건**이다 — 인라인 style 문자열로 칠한다(정본 「인라인 출처」 절).
   출처: `user/components/recFolder.js:165`(ctl) · `:150`(reroll) · `:147` outline()(lv). */
.__v3c_clearctl { display: inline-flex; align-items: center; gap: 4px; margin: 0 8px; }
.__v3c_clearctl_reroll { height: 20px; box-sizing: border-box; font-size: 15px; border: none; background: none; color: var(--v3-text-secondary); padding: 0 4px; line-height: 18px; cursor: pointer; }
/* 🔴 `color`·`border-color` 를 여기 쓰지 마라 — `playdata.js` 가 인라인으로 칠한다
   (DP12 빨강 / DP11+ 초록). CSS 로 얹으면 인라인에 덮여 **죽은 선언**이 된다.
   `border: 1px solid` 는 색을 생략해 `currentColor` 를 쓰므로 인라인 `color` 하나로 둘 다 따라온다.
   R-10 이 이 금지를 지킨다. */
.__v3c_clearctl_lv { height: 20px; box-sizing: border-box; font-size: 11px; font-weight: 700; border: 1px solid; border-radius: 3px; background: none; padding: 0 7px; line-height: 18px; cursor: pointer; }
/* 🔴 Special 의 축 토글(`__v3c_clearctl_axis`, playdata.js:2353)은 **규칙이 0건인 것이 계약이다** —
   「빠뜨렸다」고 여기 규칙을 만들지 마라(S3-15d-1 이 실측으로 닫았다. 정본 §6.2.3 S3-15d-1 행 ②).
   근거: v1 의 축 토글 인라인(`user/components/recFolder.js:393-394`)과 클리어추천 DP12/DP11+ 토글의 것
   (`:147-148`)이 **공백 정규화 후 리터럴까지 동일**하다(v1 자신이 `:389` 에 「동형」이라 적었다)
   ⇒ 바로 위 `.__v3c_clearctl_lv` 가 그 선언 **전부**이고, 남은 색 둘은 위 금지대로 JS 인라인이 칠한다.
   `_axis` 는 식별 훅일 뿐이다 — `__v3c_rcheader_label` 과 같은 성격이고, 그쪽도
   `recent-cells.css.test.mjs:156-159` 가 「규칙 없음」을 단정한다. 여기서는 R-12 가 지킨다. */
/* 빈 상태 문구 — v1 대응 규칙이 **없다**(v3 신설). 위 `.__v3c_searchempty` 의 관례를 따랐다.
   `<p>` 라 UA 기본 margin 이 붙으므로 0 으로 지운다. */
.__v3c_recempty { margin: 0; padding: 6px 10px; font-size: 12px; color: var(--v3-text-disabled); font-style: italic; }
/* S3-21c-2 ⓑ — ON Training 첫 요청 예외 때만 뜨는 「다시 시도」. 실패 문구(.__v3c_recempty) 바로 아래에 붙는다. */
.__v3c_recretry { margin: 0 10px 6px; padding: 2px 10px; font-size: 12px; border: 1px solid var(--v3-border-default); border-radius: 4px; background: none; color: var(--v3-text-secondary); cursor: pointer; }
.__v3c_recretry:disabled { opacity: 0.5; cursor: default; }

/* ── 연습곡(Training) 전용 — S3-15d-2 ① v1 `styles.css:2685-2686` 이관 ──
   훅은 폴더의 `data-band="weak"` 다 — `playdata.js:2533-2534` 가 「S3-15d-2 가 잡을 훅」이라 적어 뒀다
   (`:2533` 이 그 문구 · `:2534` 가 훅을 다는 `folder({… band: 'weak' …})` 호출이다).
   Origin 은 `rec` · Special 은 `v3` 라 이 축으로만 Training 이 갈린다.
   🔴 v1 의 `:not(.__os_spweakfolder)` 반쪽은 S3-22c(세션 92)에 **옮겼다** — SP 연습곡 폴더가 생겼다. v3 표지는 `data-sp`(`playdata.js` `buildSpTrainingFolder`).
      v1 SP 연습곡은 발광★(`diffValue` — CPI 환산)를 **보여 준다**. 이 `:not` 을 지우면 SP 연습곡의 ★가 조용히 사라진다.
   🔴 `visibility: hidden` 을 `display: none` 으로 바꾸지 마라 — v1 이 사유를 값으로 적었다(v1 :2682-2683):
      ★ 자리를 **비워 둬야** BP 가 항상 같은 칸(43px+4px)만큼 밀려 시작해, ★가 있는 클리어추천과
      **똑같은 x 위치에서** grade 와 정렬된다. `display: none` 이면 그 칸이 사라져 정렬이 깨진다.
   🔴 `!important` 도 v1 그대로다(:2685). 지우지 마라. */
.__v3c_folder[data-band="weak"]:not([data-sp]) .__v3c_recdiff { visibility: hidden !important; }

/* ── 연습곡 조작 패널 — S3-15d-2 ② **신규 작성**(이관이 아니다) ──
   🔴 v1 에 대응 CSS 규칙이 **0건**이다 — `styles.css` 에서 원본을 찾다가 「없다」로 결론내지 마라.
      출처는 `user/components/recFolder.js` 의 **인라인 style 문자열**이고, 위 「폴더 헤더 컨트롤」과 같은 성격이다:
      `:276`(패널) · `:277`·`:284`(그룹 둘) · `:258` `ctl` · `:259` `inp` · `:260` `sel` ·
      `:262` `selParent` · `:263` `lab`.
   🔴 **배색만 v1 과 일부러 갈랐다 — 2026-09-20 사용자 결정.** v1 은 이 컨트롤을 **밝게** 쓰지만
      (흰 바탕 · 짙은 글자 · `recFolder.js:258`) **v3 는 어둡게 간다**: `--v3-bg-input` · `--v3-text-main` ·
      `--v3-border-default`. 사유 = **v3 안에서 밝은 컨트롤이 오히려 이질적**이다 —
      v3 검색 입력이 이미 어둡고(`--v3-bg-search`), **같은 Recs 폴더의 ↻·DP12 토글도 `background: none`** 이라
      이 패널만 흰색 섬이었다. **「v1 리터럴 계승」에서 「v3 팔레트 채택」으로 축이 바뀐 것이다.**
      🔴 **「배경을 패널과 똑같이(#1f1f1f) + 흰 글씨」는 명시로 기각됐다** — 테두리만 남아 입력칸 인지가 안 된다.
      ⚠️ 🔴 **크기·모양은 v1 그대로다**(22px · 11px · 3px · 54/73px · `cursor:pointer`) — 색만 갈랐다.
      **「어둡게 갔으니 크기도 v3 식으로」로 번지지 마라.**
      🔴 **`color-scheme: dark` 를 지우지 마라** — 어두운 `<select>` 는 펼친 **옵션 목록**이 시스템 색
      (흰 바탕)으로 떠서 글자가 안 보인다. number 스피너도 같다. **jsdom 으로 못 잡아 실기 확인 몫이다**
      (정본 `:53` 이 「금지는 `prefers-color-scheme` 미디어 쿼리이지 `color-scheme` 속성이 아니다」를 적어 뒀다).
   ⚠️ v1 은 그룹이 둘이고 선언이 **다르다** — `:277` levelgrp 에는 `flex-wrap`·`justify-content` 가 없고
      `:284` ctlgrp 에는 있다. v3 는 S3-15b 가 둘 다 `__v3c_wkgrp` 한 클래스로 냈으므로 **합집합**을 준다.
      `justify-content` 는 **지금 두 그룹이 내용폭 flex 아이템이라 무효일 뿐**이다 — 아래 `flex-wrap` 과
      **같은 단서가 걸린다**(§6.2 회귀 2차 N-1). wrap 이 터지는 국면이 곧 그룹이 내용폭 아래로 줄어든
      국면이고, 그때 그룹 안에 줄이 생기면 마지막 줄에 여유가 남아 `flex-end` 가 **실제로 보인다.**
      v1 도 `:286-287` 에서 「컨테이너가 flex-end 정렬이라 (2차가 뒤면) 1차가 밀린다」고 그 가시성을 값으로 적었다.
      🔴 **`flex-wrap` 은 「무효」가 아니라 「여유폭이라 안 터지는」 것이다 — 확정 사실로 인용하지 마라**
      (§6.2 회귀 검토 1차 「B」가 내 종전 사유를 정정했다. 종전엔 「패널이 먼저 줄바꿈하므로 발동하지
      않는다」고 적었는데 기제가 틀렸다). 진짜 기제는 **자동 최소 크기**다 — `min-width: auto` 가
      min-content 로 풀리는데 v1 levelgrp 는 `nowrap` 이라 그 값이 **아이템 합**이고, v3 는 `wrap` 이라
      **최대 아이템 하나**로 떨어진다. 지금 안 갈리는 이유는 levelgrp 내용폭(☆+54+~+54+BPM ≈ 250px)이
      실사용 패널 폭을 안 넘기 때문이다. 갈리면 모양은 **v1 = 가로 넘침 / v3 = 그룹 안에서 접힘**이다
      (v3 쪽이 낫다). ⇒ 지금은 v1 과 같은 결과이고, **위치 셀렉터로 가르지 않는 판단은 그대로 유효하다**
      (생산자가 순서를 바꾸면 조용히 어긋나고 아무 테스트도 못 지킨다).
   🔴 **`__v3c_wksub`(2차 드롭박스)에 규칙을 만들지 마라 — 규칙 0건인 것이 계약이다**
      (정본 §6.2.3 S3-15d-2 행 ② · R-14 가 단정한다). v1 `recFolder.js:288` 에서 2차가 받는 것은
      공통 `sel`(= 아래 `__v3c_wksel`)과 초기 `display:none` 둘뿐이고, 후자는 v3 가 JS `sub.hidden` 으로 한다.
      폭 고정도 **1차에만** 있다(v1 `:262`) — 2차 유무로 1차가 밀리는 것은 v1 이 **DOM 순서**로 막았다(`:285-287`).
   🔴 **그래서 `__v3c_wksel` 에 `display` 를 주면 안 된다** — author 규칙이 UA 의 `[hidden] { display: none }` 을
      이겨 **2차가 항상 보인다.** v1 `ctl`(`:258`)에도 `display` 가 없으니 계승에 문제 없다. */

.__v3c_wkpanel { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; padding: 6px 8px; background: var(--v3-bg-panel); border-bottom: 1px solid var(--v3-border-wkpanel); }
.__v3c_wkgrp { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 4px; }
/* 공통 컨트롤 — v1 `ctl`(:258). `__v3c_wknum` = `inp`(+`width: 54px`) · `__v3c_wksel` = `sel`(+`cursor: pointer`). */
.__v3c_wknum, .__v3c_wksel { height: 22px; box-sizing: border-box; font-size: 11px; border: 1px solid var(--v3-border-default); border-radius: 3px; background: var(--v3-bg-input); color: var(--v3-text-main); padding: 0 4px; line-height: 20px; vertical-align: middle; color-scheme: dark; }
.__v3c_wknum { width: 54px; }
.__v3c_wksel { cursor: pointer; }
/* 🔴 1차(대표피처) 드롭박스만 폭을 고정한다 — 옵션 라벨 길이에 따라 폭이 널뛰는 것을 막는다
   (v1 `:261-262` 주석, 2026-08-08). 2차(`__v3c_wksub`)에는 주지 마라 — v1 도 안 준다. */
.__v3c_wkparent { width: 73px; }
/* ☆ · ~ 라벨 — v1 `lab`(:263). `#e9ecef` 는 `--v3-text-main` 과 같은 값이다. */
.__v3c_wklab { font-size: 11px; color: var(--v3-text-main); font-weight: 600; line-height: 22px; }

/* ── 모바일 재배치 (v1 :2732-2762 중 `.__os_recfolder`/`.__os_estgoal_mb` 줄만) ──
   🔴 `@media` 가 아니라 `@container` 다. 전환점 600px 은 C1 2줄 전환과 **같은 기준**이고
      새 전환점을 만들면 안 된다(R-6 이 지킨다).
   🔴 컨테이너 선언자(`.__v3c_tbody`)를 subject 로 쓰지 마라 — 자기 쿼리의 대상이 아니라
      영원히 안 맞는 죽은 규칙이 된다(G-7 부칙 · R-7 이 지킨다).
   ⚠️ v1 블록 안의 `.__os_rivalfolder` 2줄(:2765-2766, 4차) ·
      `__os_notes_layout`(:2751, **S3-14c 이월 잔여** — 🔴 세션 50 이 「S3-14d」에서 정정)은
      **일부러 뺀 것**이다. 「빠졌다」고 채우지 마라.
   ✅ **`.__dbr_folders` 2줄(:2754-2755)은 채웠다** — 이 절이 지목한 `S3-12b` 가 바로 세션 69 의
      `S3-12b-2a` 다. 아래 `@container` 블록 **안** 끝에 `[data-axis="dbr"]` 스코프로 들어 있다. */

/* DBR 밴드 폴더 전용 (S3-12b-2a · v1 `.__dbr_folders` 스코프 이식).
   🔴 **새 클래스를 만들지 않는다** — `renderFolderList` 가 root 에 `data-axis="dbr"` 를 이미 박으므로
      (`folder-list.js:11`) **속성만으로** 스코프한다. 🔴 `.__v3c_folderlist` 를 셀렉터에 쓰지 마라 —
      R-4 의 생산자 집합은 `playdata.js` 와 Recs DOM 뿐이라 그 클래스가 **미등록으로 잡힌다**(실제로 잡혔다).
      ⚠️ 모바일 둘은 경쟁자가 `(0,5,0)` 이라 `__v3c_ostbody` 를 끼워 `(0,6,0)` 으로 이긴다.
   🔴 **이 절을 Recs 절 밖으로 옮기지 마라** — `recs.css.test.mjs` 가 수집하는 범위가 이 절이다.
   ⚠️ §6.2 회귀 5차가 찾았다 — 1차 구현은 tbody/tr 트랙 **두 줄만** 대조하고
      「CSS 신설 0」으로 **일반화**했다. `.__dbr_folders` 스코프 규칙 목록을 안 봤다. */
/* 🔴 **다만 「root」는 *폴더 목록 래퍼* 다 — 개별 폴더에는 안 박는다.** (🔴 **여기에 그 두 클래스 이름을 리터럴로 적지 마라** — `recs.css.test.mjs` R-4 가 **주석까지 훑어** 생산자 없는 클래스로 잡는다. 2026-09-21 세션 70 이 실제로 걸렸다.)
   폴더가 `data-axis` 를 받는 길은 `g.data.axis` 뿐이고, `buildBandGroups` 가 그것을 싣는다
   (2026-09-21 세션 70 §6.4 ④). **CSS 근거로는 이 문장이 참이지만**(규칙이 전부 자손 결합자다)
   **「폴더에도 있다」로 읽으면 거짓**이다 — 세션 69 인계가 실제로 그렇게 읽고 틀렸다. */
/* 🔴 DBR 난이도는 `12.59` 처럼 **5글자**라 곡명 앞 zasa 폭을 넓힌다(v1 `styles.css:2619-2620`).
   base `.__v3c_zasa` 는 `20px` 이라 그대로 두면 **숨거나 줄바꿈한다**.
   ⚠️ 같은 처방을 Recent DBR 토글은 이미 받았다(`:718` `.__v3c_rcdbr` = `25px`) — 밴드 폴더만 빠졌었다.
      값이 다른 것은 v1 그대로다(`:2620` 30px / `:2622` 25px). **통일하지 마라.** */
[data-axis="dbr"] .__v3c_zasa { width: 30px; padding-right: 4px; }
/* 곡명 칸이 flex 라 zasa 는 고정폭, titlebox 가 남는 폭을 먹어야 곡명 ellipsis 가 산다(v1 `:2628-2629`).
   ⚠️ `min-width: 0` 을 빼면 inline-flex titlebox 가 폭 0 으로 접혀 곡명이 통째로 `…` 만 된다. */
[data-axis="dbr"] .__v3c_cell.__v3c_title .__v3c_zasa { flex: 0 0 auto; }
[data-axis="dbr"] .__v3c_titlebox { flex: 1 1 auto; min-width: 0; }
/* 🔴 추천곡 폴더 라벨 강조 — v1 `styles.css:2633` 이식(S3-12b-2b).
   v1 은 `.__dbr_recfolder` 스코프인데 v3 에는 그 클래스가 없다 — **폴더 `data` 로 스코프한다**
   (`buildDbrRecFolder` 가 `data: { axis: 'dbr', band: 'rec' }` 를 싣는다).
   🔴 **색은 토큰이다 — `--v3-state-warning-strong`**(`tokens.css` 가 v1 의 `#ffd43b` 와 같은 값을 갖는다).
      hex 리터럴 금지(R-5). ⚠️ **`--v3-recstage-ex` 를 쓰지 마라** — 값이 같지만 *추천 stage 팔레트* 라
      의미 축이 다르고, **바꿔 써도 화면이 안 바뀌어 틀려도 에러가 없다**(정본 S3-12b-2b 칸 ⓑ).
   🔴 **이 한 줄이 「이 조각에 남는 CSS 전부」라고 정본이 값으로 적어 뒀는데 1차 구현이 통째로 빠뜨렸다**
      — §6.2 회귀 3차 B-1. 세션 69 회귀 5차가 잡은 「v1 DBR CSS 다섯 누락」과 자리도 모양도 같다.
      ⚠️ **기본 라벨은 `font-weight: 600` 이고 색이 상속이다** — 둘 다 덮어야 v1 화면이 된다. */
[data-axis="dbr"] .__v3c_folder[data-band="rec"] > .__v3c_summary .__v3c_folderlabel {
  color: var(--v3-state-warning-strong); font-weight: 700;
}

@container v3row (max-width: 600px) {
  .__v3c_tbody.__v3c_ostbody .__v3c_tr { grid-template-columns: 8px 36px minmax(0, 1fr) 45px 50px 60px 93px; grid-template-rows: auto auto; height: auto; column-gap: 4px; }
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_lamp { grid-column: 1; grid-row: 1 / 3; margin: 0; }
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_level { grid-column: 2; grid-row: 1 / 3; }
  /* min-height — 아이폰 사파리에서 `-webkit-box`(line-clamp) 요소가 grid auto 트랙에서 intrinsic
     높이를 0 으로 계산해 곡명이 안 보이던 버그 방어. 1.25em = 글자 1줄 line-height. */
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_title { grid-column: 3 / 7; grid-row: 1; min-height: 1.25em; align-self: center; }
  /* 모바일 — 강한 피쳐를 시리즈명 옆에 (시리즈 태그와 같은 회색). PC 기본 `display:none` 은 C1 절. */
  .__v3c_tbody.__v3c_ostbody .__v3c_seriesfeat { display: inline-block; flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--v3-text-tag); font-size: 9px; }
  /* v1 의 `justify-content: flex-start !important`(:2746)에서 `!important` 는 계승하지 않는다 —
     경쟁자가 `.__dbr_folders` 쪽이고 v3 엔 없다. */
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_notes { grid-column: 7; grid-row: 1; justify-content: flex-start; }
  /* 연습곡 목표 rate — 모바일 notes 자리에서 base `display:none` 을 되살린다. */
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_notes .__v3c_recgoal { display: block; flex: 1; min-width: max-content; text-align: right; white-space: nowrap; }
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_lamptext { grid-column: 4; grid-row: 2; }
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_ratebar { grid-column: 5 / 7; grid-row: 2; }
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_num { grid-column: 7; grid-row: 2; }
  /* 모바일 — 해시태그 미표시. */
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_rectags { display: none; }
  /* est축 목표 램프 태그 — PC 자리(해시태그 칸)가 위에서 숨겨졌으니 시리즈줄 사본을 대신 노출. */
  .__v3c_tbody.__v3c_ostbody .__v3c_estgoal_mb { display: inline-block; margin-left: 3px; }
  /* 배치추천 뱃지 — 곡행(row1) 바로 아래 빈 칸(col3, row2)에 우측정렬. */
  .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_layoutcell { display: flex; grid-column: 3; grid-row: 2; justify-content: flex-end; align-items: center; }
  /* 🔴 DBR 밴드 폴더 — 모바일 notes 칸. v1 `styles.css:2754-2755` 이식.
     ⓐ `'notes'` 라벨(`::after`)을 숨긴다 — DBR 행은 `{ recrow: false }` 라 `:not(.__v3c_recrow)` 규칙에
        **걸려서 라벨이 뜨는데**, 그 칸에 든 것은 노트수가 아니라 추천도 배지다.
     ⓑ 우측정렬 — 위 `__v3c_ostbody` 규칙이 `flex-start` 를 주는데 v1 DBR 은 `flex-end` 다.
        ⚠️ 그 규칙의 주석은 「경쟁자가 `.__dbr_folders` 쪽이고 v3 엔 없다」고 적었는데,
        **이 조각이 그 경쟁자를 만들었다.** 특정도로 이긴다(`!important` 는 계승하지 않는다). */
  [data-axis="dbr"] .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_notes::after { content: none; }
  [data-axis="dbr"] .__v3c_tbody.__v3c_ostbody .__v3c_tr > .__v3c_cell.__v3c_notes { justify-content: flex-end; }
}
/* MAX− 목표 폴더(Scoring · S3-16)의 「추정」 표기 — v1 `styles.css:2782 .__pd_target_est_row::after` 값 그대로.
   🔴 붙는 자리는 셀이 아니라 **타이틀 박스**(`__v3c_headtitlebox`)다 — v1 은 `__dbr_titlebox` 에 치환해 붙인다.
   🔴 **곱행 절(chart-row) 안으로 옮기지 마라** — 그 절은 hex 리터럴을 금지하고(T-4)
   클래스가 `chart-row.js` 에서 생산될 것을 요구한다(T-3). 이 클래스는 `playdata.js` 가 붙인다.
   (2026-09-20 세션 58 이 그 절로 옮겼다가 T-3·T-4·T-9 셋이 한꿐번에 터졌다.) */
.__v3c_target_est::after {
  content: '추정';
  color: var(--v3-lamp-ex);        /* #dcaf45 — v1 원값과 같다(`__v3c_djletter-AA` 와 같은 관례) */
  font-size: 10px;
  font-weight: 700;
  margin-left: 4px;
}


/* ===== Recs 폴더 끝 ===== */

/* ===== C6 모달 — S3-13a ===== */
/* v1 원본 2벌(styles.css 의 __shelf_rank_* 324-374 · __board_modal_* 537-562)의 공통분모 1벌.
   🔴 v1 은 두 벌의 값이 갈려 있었고 v3 는 단일화한다 — 옮겨 적지 마라:
     z-index  10000(랭킹) / 1000(Docs)        → --v3-z-modal
     딤       rgba(0,0,0,.72) / rgba(0,0,0,.75) → --v3-scrim
     모바일 경계 640px(랭킹) / 600px(Docs)     → 600px (v3 관례와 같은 쪽)
   🔴 PC 유저카드 겹침 배치(rankingModal.js:42-60)는 미계승이다 — v3 에 그 카드가 없다.
     그래서 여기에는 좌표를 재서 딤을 덮어쓰는 규칙이 없다. */
.__v3c_modal {
  position: fixed;
  inset: 0;
  z-index: var(--v3-z-modal);
  background: var(--v3-scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
}
.__v3c_modal_dialog {
  position: relative;
  width: min(760px, 100%);
  max-height: 95vh;
  background: var(--v3-bg-page);
  border: 1px solid var(--v3-border-default);
  border-radius: 8px;
  box-shadow: var(--v3-shadow-modal);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.__v3c_modal_close {
  position: absolute;
  top: 12px;
  right: 14px;
  z-index: 1;
  border: 0;
  background: transparent;
  color: var(--v3-text-dim);
  font-size: 22px;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
}
.__v3c_modal_close:hover { color: var(--v3-text-hover); }
.__v3c_modal_content {
  overflow: auto;
  padding: 24px;
  scrollbar-width: thin;                     /* Firefox — 얇게 */
  scrollbar-color: var(--v3-bg-surface-muted) transparent;   /* thumb / track(투명) */
  container-type: inline-size;               /* 🔴 곡행(__v3c_tbody)이 @container 2줄 전환을 타도록.
                                                모달 안에는 컨테이너 조상이 없어 이것이 없으면
                                                좁은 모달에서도 행이 1줄로 남는다 — v1 이 뒤늦게 고친 버그다. */
}
.__v3c_modal_content::-webkit-scrollbar { width: 6px; height: 6px; }
.__v3c_modal_content::-webkit-scrollbar-track { background: transparent; }
.__v3c_modal_content::-webkit-scrollbar-thumb { background: var(--v3-bg-surface-muted); border-radius: 3px; }
.__v3c_modal_content::-webkit-scrollbar-thumb:hover { background: var(--v3-border-strong); }

@media (max-width: 600px) {
  .__v3c_modal { padding: 0; align-items: stretch; }
  .__v3c_modal_dialog {
    width: 100%;
    max-height: 100dvh;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }
  .__v3c_modal_content { padding: 22px 12px; }
  /* 🔴 v1 은 여기서 X 버튼을 display:none 으로 감췄다 — 계승하지 않는다.
       v1 은 포커스 트랩이 없어서 그래도 됐다. v3 는 트랩이 켜지므로 화면 안에 닫는 수단이
       반드시 하나는 있어야 한다: 풀스크린이라 배경 클릭이 불가능하고, 모바일엔 ESC 키가 없어
       X 를 감추면 남는 것이 브라우저 뒤로가기뿐이라 화면 안 어포던스가 0 이 된다. */
}
/* ===== C6 모달 끝 ===== */

/* ===== C6-L 로그인 모달 — S3-13b ===== */
/* v1 원본은 `styles.css:2806-2814` 의 `__dbr_m*` 한 벌이다.
   🔴 **하드코딩 hex 를 옮겨 적지 마라 — v3 는 토큰을 쓴다.**
   🔴 **배경·테두리·둥근 모서리·z-index·딤은 여기에 쓰지 마라 — 셸(C6)이 이미 준다.** */
.__v3c_login_form { display: flex; flex-direction: column; gap: 10px; }
.__v3c_login_title { font-weight: 700; font-size: 1.2em; margin-bottom: 4px; }
.__v3c_login_row { display: flex; align-items: center; gap: 8px; }
.__v3c_login_row > span:first-child { flex: 0 0 64px; }
.__v3c_login_row input { flex: 1; min-width: 0; background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default); padding: 6px 8px; border-radius: var(--v3-radius-md); }
.__v3c_login_idfixed { font-family: var(--v3-font-mono); }
/* 🔴 min-height 로 자리를 미리 잡는다 — 없으면 메시지가 뜰 때 버튼이 아래로 밀린다(v1 계승). */
.__v3c_login_msg { color: var(--v3-state-danger-soft); font-size: .85em; min-height: 1.2em; }
.__v3c_login_btns { display: flex; gap: 8px; }
.__v3c_login_btns button { flex: 1; padding: 7px 10px; border: 0; border-radius: var(--v3-radius-md); cursor: pointer; color: var(--v3-text-main); }
.__v3c_login_submit { background: var(--v3-accent); }
.__v3c_login_cancel { background: var(--v3-bg-surface-muted); }
.__v3c_score { display: flex; flex-direction: column; gap: 12px; }
.__v3c_score_title { color: var(--v3-text-main); font-weight: 700; }
.__v3c_score_lamps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
/* 🔴 램프 색은 `--v3c-lampbtn` 한 칸을 거쳐 쓴다 — `currentColor` 로 직접 칠하지 마라.
   `background: currentColor` 와 `color: …` 를 **같은 선언 블록**에 두면 `currentColor` 가
   그 블록이 새로 정한 color 를 가리켜 **배경과 글자가 같은 색이 되고 글자가 사라진다.**
   ⚠️ 에러가 없다. `.__v3c_lamp-NP { --v3c-cell-bg: … }`(:821)가 쓰는 것과 같은 수법이다.
   🔴 색값은 램프 **텍스트** 색표(:1019-1027)와 같은 표다 — 배지색 표가 아니다. 새 토큰은 0개다. */
.__v3c_score_lampbtn { background: transparent; color: var(--v3c-lampbtn); border: 1px solid var(--v3c-lampbtn); border-radius: var(--v3-radius-md); padding: 7px 8px; cursor: pointer; }
.__v3c_score_lampbtn.active { background: var(--v3c-lampbtn); color: var(--v3-bg-page); }
.__v3c_score_lampbtn-F { --v3c-lampbtn: var(--v3-diff-another); }
.__v3c_score_lampbtn-AC { --v3c-lampbtn: var(--v3-lamp-ac); }
.__v3c_score_lampbtn-EC { --v3c-lampbtn: var(--v3-lamp-ec); }
.__v3c_score_lampbtn-NC { --v3c-lampbtn: var(--v3-lamp-nc); }
.__v3c_score_lampbtn-HC { --v3c-lampbtn: var(--v3-text-main); }
.__v3c_score_lampbtn-EX { --v3c-lampbtn: var(--v3-lamp-ex); }
.__v3c_score_ex { background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default); padding: 7px 8px; }
.__v3c_score_msg { color: var(--v3-state-danger-soft); min-height: 1.2em; }
.__v3c_score_save, .__v3c_score_cancel { padding: 7px 10px; border: 0; border-radius: var(--v3-radius-md); cursor: pointer; }
.__v3c_score_save { background: var(--v3-accent); color: var(--v3-bg-page); }
.__v3c_score_cancel { background: var(--v3-bg-surface-muted); color: var(--v3-text-main); }
/* DBR 행 커서 — v1 `styles.css:2794` 의 `.__dbr_folders .__pd_tr { cursor: pointer }` 대응.
   🔴 **`.__v3c_tr` 전체에 주지 마라.** 그것은 Recent·Level·Series 등 **클릭이 없는 행까지**
   포인터로 만들어 「눌러도 아무 일이 없는」 화면이 된다(v3 곡행 라우팅은 `[data-song-id]` 가
   있는 행만 걸린다 — `playdata.js:179`).
   ⚠️ `.__v3c_recrow { cursor: pointer }`(:1079)는 DBR 행에 **안 걸린다** —
   DBR 은 `chartRecRow(…, { recrow: false })` 경로라 그 class 가 안 붙는다. */
.__v3c_folderlist[data-axis="dbr"] .__v3c_tr { cursor: pointer; }

/* ===== C6-L 로그인 모달 끝 ===== */

/* ===== 4차 Rival 페이지 (S4-2 골격) =====
   🔴 별도 rival.css 를 만들지 않고 여기 둔다 — v3/index.html 은 tokens·styles·components 셋만 링크한다
   (2026-09-26 세션 96 결정 · 정본 §9.4.3 S4-2 파일 칸). 새 파일을 만들면 링크를 빠뜨려 규칙이 통째로 안 먹는다.
   Rival 은 독립 페이지다 — PlayData 절(`.__v3c_pd…`)의 규칙을 여기서 덮어쓰지 마라(RV-P1). */
.__v3c_rivalpage { display: flex; flex-direction: column; gap: var(--v3-space-4); }
.__v3c_rivalstate { margin: 0; padding: var(--v3-space-6) var(--v3-space-4); text-align: center; color: var(--v3-text-muted); }
.__v3c_rivalstate[data-state="error"] { color: var(--v3-state-danger-soft); }
/* 🔴 [hidden] 을 되살리는 규칙 — 위 `display` 류 규칙이 생기면 hidden 속성이 지고 빈 칸이 남는다. 지우지 마라. */
.__v3c_rivalstate[hidden] { display: none; }

/* ===== 4차 Rival 페이지 (S4-2 골격) 끝 ===== */

/* ===== 4차 Rival 페이지 (S4-3 selector·요약) =====
   §9.1 상단: 왼쪽 「점수 비교 | 램프 비교」 · 오른쪽 등록 라이벌 선택. §9.2 요약: WIN/LOSE/TIE + 승패 막대 + 접힌 레벨별.
   색은 토큰만(리터럴 금지 — RV-CSS-3 이 이 절까지 잰다). WIN=success · LOSE=danger · TIE=text-faint. */
.__v3c_rivaltop { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--v3-space-3); }
.__v3c_rivalkindtabs { display: inline-flex; gap: var(--v3-space-1); }
.__v3c_rivalkindtab { padding: var(--v3-space-2) var(--v3-space-4); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); background: none; color: var(--v3-text-secondary); cursor: pointer; }
/* 🔴 선택 표시는 aria-pressed 로만 건다 — 별도 active 클래스를 만들지 마라(상태가 두 벌이 되면 갈린다). */
.__v3c_rivalkindtab[aria-pressed="true"] { color: var(--v3-accent-hover); border-color: var(--v3-accent-hover); }
.__v3c_rivalselect { background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); padding: var(--v3-space-2) var(--v3-space-3); max-width: 100%; }
.__v3c_rivalmergewarning { margin: 0; color: var(--v3-state-warning); font-size: 12px; }
.__v3c_rivalsummary { display: flex; flex-direction: column; gap: var(--v3-space-2); }
.__v3c_rivalmetrics { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--v3-space-4); font-weight: 700; }
.__v3c_rivalmetric--win { color: var(--v3-state-success); }
.__v3c_rivalmetric--lose { color: var(--v3-state-danger); }
.__v3c_rivalmetric--tie { color: var(--v3-text-faint); }
.__v3c_rivalcount { color: var(--v3-text-muted); font-weight: 400; font-size: 12px; }
.__v3c_rivalbar { display: flex; height: 8px; border-radius: var(--v3-radius-sm); overflow: hidden; background: var(--v3-bg-surface-muted); }
.__v3c_rivalseg--win { background: var(--v3-state-success); }
.__v3c_rivalseg--lose { background: var(--v3-state-danger); }
.__v3c_rivalseg--tie { background: var(--v3-text-faint); }
.__v3c_rivalnote, .__v3c_rivalrowerror { margin: 0; color: var(--v3-text-muted); }
.__v3c_rivalrowerror { color: var(--v3-state-danger-soft); }
.__v3c_rivallevelbtn { align-self: flex-start; background: none; border: 0; padding: var(--v3-space-1) 0; color: var(--v3-text-secondary); cursor: pointer; }
.__v3c_rivallevels { display: flex; flex-direction: column; gap: var(--v3-space-1); }
/* 🔴 기본 접힘(§9.2)은 hidden 속성이다 — 위 display:flex 가 hidden 을 이기므로 이 규칙이 없으면 접힘이 풀린다. 지우지 마라. */
.__v3c_rivallevels[hidden], .__v3c_rivallevelbtn[hidden], .__v3c_rivalsummary[hidden] { display: none; }
.__v3c_rivallevelrow { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--v3-space-3); padding: var(--v3-space-1) 0; border-bottom: 1px solid var(--v3-border-subtle); }
.__v3c_rivallevellabel { min-width: 72px; color: var(--v3-text-main); }
/* ===== 4차 Rival 페이지 (S4-3 selector·요약) 끝 ===== */

/* ===== 4차 Rival 페이지 (S4-4 폴더) =====
   비교 폴더·도전장 영역(S4-4a·S4-4b). 폴더·곡행 자체는 C5·C1 절 규칙을 그대로 쓴다 — 여기는 영역 여백과 안내 문구뿐이다.
   🔴 실패 문구는 요약 자리 실패 문구(.__v3c_rivalrowerror)와 같은 danger-soft 다(세션 97 §6.2 비블로킹 ①). */
.__v3c_rivalfolders { margin-top: var(--v3-space-2); }
.__v3c_rivalfoldernote { margin: 0; color: var(--v3-text-muted); }
.__v3c_rivalfoldernote[data-state="error"] { color: var(--v3-state-danger-soft); }
/* 격파 폴더(S4-4d) — 안내문 한 줄 · 밴드 머리 색 · 본문 안 문구 여백.
   🔴 ↻ 는 공용 .__v3c_clearctl 규칙(DP/SP Origin 과 같은 모양)을 그대로 쓴다 — 여기서 새로 만들지 마라.
   ⚠️ v1 의 안내문 마퀴(넘치면 흐르기)는 옮기지 않았다 — 줄바꿈으로 다 보인다(세션 99 Opus 결정).
   밴드색은 v1 RV_SECTIONS(user/components/rivalFolder.js:13-17) 값과 같은 토큰이다 — JS 에 리터럴 색을 두지 않고 data-rvband 로 칠한다. */
.__v3c_rivaltgtnote { margin: 0; padding: 4px 10px; font-size: 11px; line-height: 1.4; color: var(--v3-text-muted); white-space: normal; }
.__v3c_rivaltgtnote b { color: var(--v3-text-secondary); }
.__v3c_recsection[data-rvband="strong"] { color: var(--v3-recstage-ec); }
.__v3c_recsection[data-rvband="possible"] { color: var(--v3-recstage-fc); }
.__v3c_folder[data-band="rivaltarget"] .__v3c_rivalfoldernote { padding: 10px; }
/* ===== 4차 Rival 페이지 (S4-4 폴더) 끝 ===== */

/* ===== How To · Links 정적 페이지 (md) =====
   v1 styles.css 「마크다운 렌더」(:243-294) · 「blockquote / hr / link」(:490-499) 를 v3 토큰으로 옮겼다(세션 100).
   🔴 토큰에 같은 값이 없는 색 넷(#232323 · #fab005 · #2d2818 · #5a5a5a)만 리터럴이다 — 새 토큰을 만들지 않았다.
   ⚠️ v1 의 table·th·td 규칙은 옮기지 않았다 — md-lite(v3/shared/md-lite.js)가 표를 그리지 않는다.
   🔴 셀렉터는 전부 .__v3c_mdpage 로 시작한다 — 다른 페이지의 h2·code·a 에 번지지 않게. */
/* v1 #content 의 본문 폭·행간 대응 — 셸 main 안이라 가운데 정렬은 하지 않는다 */
.__v3c_mdpage { display: block; padding: 16px; max-width: 900px; line-height: 1.6; color: var(--v3-text-main); }
.__v3c_mdpage h1 { font-size: 28px; margin: 0 0 8px; color: var(--v3-text-heading); }
.__v3c_mdpage h2 {
  font-size: 20px; margin: 32px 0 12px; padding-bottom: 6px;
  border-bottom: 2px solid var(--v3-border-default); color: var(--v3-text-heading);
}
.__v3c_mdpage h3 { font-size: 16px; margin: 20px 0 8px; color: var(--v3-text-secondary); }
.__v3c_mdpage p { margin: 8px 0; }
.__v3c_mdpage ul, .__v3c_mdpage ol { padding-left: 24px; margin: 8px 0; }
.__v3c_mdpage li { margin: 4px 0; }
.__v3c_mdpage code {
  background: var(--v3-bg-surface); padding: 2px 6px; border-radius: 3px;
  font-size: 13px; font-family: "SF Mono", Menlo, Consolas, monospace;
  color: var(--v3-accent-hover);
}
.__v3c_mdpage pre {
  background: #232323; border: none; border-radius: 0 0 6px 6px;
  padding: 14px; overflow-x: auto; font-size: 13px;
  margin: 0;
  scrollbar-width: thin; scrollbar-color: var(--v3-border-scrollbar) transparent;
}
.__v3c_mdpage .__v3c_codeblock {
  margin: 12px 0;
  border-radius: 6px;
  overflow: hidden;
  transition: box-shadow 0.2s;
  cursor: pointer;
}
.__v3c_mdpage .__v3c_codeblock:hover { box-shadow: 0 0 0 1px var(--v3-border-scrollbar); }
.__v3c_mdpage .__v3c_codehead {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--v3-bg-panel); padding: 8px 14px;
  font-size: 11px; letter-spacing: 0.5px; font-weight: 600;
  border-bottom: 1px solid var(--v3-border-default);
}
.__v3c_mdpage .__v3c_codelang { color: var(--v3-text-disabled); }
.__v3c_mdpage .__v3c_codecopy { color: var(--v3-accent); }
.__v3c_mdpage .__v3c_codeblock:hover .__v3c_codecopy { color: var(--v3-accent-soft); }
.__v3c_mdpage pre::-webkit-scrollbar { height: 6px; }
.__v3c_mdpage pre::-webkit-scrollbar-track { background: transparent; }
.__v3c_mdpage pre::-webkit-scrollbar-thumb { background: var(--v3-border-scrollbar); border-radius: 3px; }
.__v3c_mdpage pre::-webkit-scrollbar-thumb:hover { background: #5a5a5a; }
.__v3c_mdpage pre code {
  background: transparent; padding: 0; color: var(--v3-text-main); font-size: 13px;
}
.__v3c_mdpage blockquote {
  border-left: 4px solid #fab005; background: #2d2818;
  margin: 12px 0; padding: 8px 14px; color: var(--v3-text-secondary);
}
.__v3c_mdpage blockquote p { margin: 4px 0; }
.__v3c_mdpage hr { border: none; border-top: 1px solid var(--v3-border-default); margin: 24px 0; }
.__v3c_mdpage a { color: var(--v3-state-info); text-decoration: none; }
.__v3c_mdpage a:hover { text-decoration: underline; }
.__v3c_mdpage strong { color: var(--v3-text-heading); }
/* ===== How To · Links 정적 페이지 (md) 끝 ===== */

/* ===== 4차 Settings 페이지 (S4-5f) =====
   사이드바 /settings 한 페이지(R-16) — v3/pages/settings.js 가 만드는 클래스 전부. v1 styles.css:2873-2910(설정 모달)을 v3 토큰으로 옮겼다(세션 102).
   🔴 별도 settings.css 를 만들지 않고 여기 둔다 — v3/index.html 은 tokens·styles·components 셋만 링크한다(정본 S4-5f 행 · Rival 절과 같은 이유).
   🔴 리터럴 색 0 — v1 hex 는 전부 토큰에 같은 값이 있다(#1a1d22 bg-input · #343a40 border-default · #e9ecef text-main · #adb5bd text-muted ·
      #868e96 text-dim · #6c757d text-disabled · #ff8787 danger-soft · #ff6b9d accent). 대응값이 없던 하나(드래그 행 배경 #2b3038)는
      가장 가까운 bg-surface-raised(#2d323c)로 — 새 토큰을 만들지 않았다. ⚠️ 입력칸 테두리는 v1 #3a3f47 이 아니라 로그인 모달(C6-L)과 같은 border-default 다.
   🔴 셀렉터는 전부 .__v3c_settings 로 시작한다 — 다른 페이지에 번지지 않게. */
.__v3c_settingspage { display: flex; flex-direction: column; gap: var(--v3-space-4); padding: 16px; max-width: 640px; color: var(--v3-text-main); }
.__v3c_settingsstate { margin: 0; padding: var(--v3-space-6) var(--v3-space-4); text-align: center; color: var(--v3-text-muted); }
.__v3c_settingsstate[data-state="error"] { color: var(--v3-state-danger-soft); }
/* 🔴 [hidden] 을 되살리는 규칙 — ready 면 상태 줄이 hidden 인데 위 규칙의 여백이 남으면 빈 칸이 생긴다. 지우지 마라. */
.__v3c_settingsstate[hidden] { display: none; }
.__v3c_settingslogin, .__v3c_settingsretry { align-self: center; padding: 7px 16px; border: 0; border-radius: var(--v3-radius-md); cursor: pointer; background: var(--v3-accent); color: var(--v3-bg-page); }
.__v3c_settingsretry { background: var(--v3-bg-surface-muted); color: var(--v3-text-main); }

/* ── 절(비밀번호 · 라이벌) ── */
/* 🔴 편집 영역도 flex 열이어야 한다 — 절·메시지·저장 버튼은 페이지(.__v3c_settingspage)가 아니라 이 영역의 자식이라
      페이지 gap 이 안 닿는다. 이 규칙이 없으면 절 사이가 0px 로 붙고 저장 버튼의 align-self 가 무효가 된다(세션 102 §6.2 비블로킹 ①). */
.__v3c_settings_editor { display: flex; flex-direction: column; gap: var(--v3-space-4); }
.__v3c_settings_panel { display: flex; flex-direction: column; gap: var(--v3-space-4); min-width: 0; }
.__v3c_settings_panel[hidden] { display: none; }
.__v3c_settings_csv { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.__v3c_settings_csvhint { margin: 0 0 4px; font-size: 11px; line-height: 1.5; color: var(--v3-text-dim); }
.__v3c_settings_csvrow { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--v3-text-muted); }
.__v3c_settings_csvrow > span { flex: 0 0 84px; }
.__v3c_settings_csvfile { min-width: 0; max-width: 100%; color: var(--v3-text-main); }
.__v3c_settings_csvversion { background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); padding: 6px 7px; }
.__v3c_settings_csvbtn { align-self: flex-start; padding: 8px 24px; border: 0; border-radius: var(--v3-radius-md); cursor: pointer; background: var(--v3-accent); color: var(--v3-bg-page); font-weight: 700; }
.__v3c_settings_csvbtn:disabled { opacity: 0.5; cursor: default; }
.__v3c_settings_csvresult { margin: 0; min-height: 1.2em; font-size: 13px; color: var(--v3-text-muted); overflow-wrap: anywhere; }
.__v3c_settings_section { display: flex; flex-direction: column; gap: 8px; padding-bottom: var(--v3-space-4); border-bottom: 1px solid var(--v3-border-default); }
.__v3c_settings_title { margin: 0 0 2px; font-size: 14px; font-weight: 700; color: var(--v3-text-main); }
.__v3c_settings_hint { margin: 0 0 4px; font-size: 11px; line-height: 1.5; color: var(--v3-text-dim); }
.__v3c_settings_hint b { color: var(--v3-text-muted); }

/* 입력칸 공통(비밀번호 · 본인 AC · 라이벌 행) — 로그인 모달 입력칸과 같은 어두운 모양(v1 :2878-2879). */
.__v3c_settings_pwrow input:not([type="checkbox"]),
.__v3c_settings_ownrow input:not([type="checkbox"]),
.__v3c_settings_rival input:not([type="checkbox"]) {
  background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default);
  border-radius: var(--v3-radius-md); padding: 6px 7px; font-size: 13px; font-family: inherit; min-width: 0;
}
/* 비밀번호·본인 AC 줄 — 라벨은 줄바꿈 없이, 비밀번호는 4자리라 100px 고정(v1 :2881-2884). */
.__v3c_settings_pwrow, .__v3c_settings_ownrow { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--v3-text-muted); }
.__v3c_settings_pwrow > span, .__v3c_settings_ownrow > span { flex: 0 0 84px; white-space: nowrap; }
.__v3c_settings_pw, .__v3c_settings_confirm { flex: 0 0 100px; width: 100px; }
.__v3c_settings_ownrow .__v3c_settings_ownac { flex: 0 0 150px; width: 150px; }
/* 체크박스(본인·라이벌 AC 병합) — 격자 칸 가운데 */
.__v3c_settings_ownmerge, .__v3c_settings_rivalmerge { margin: 0; justify-self: center; cursor: pointer; }

/* ── 라이벌 행 — 열이 행마다 어긋나지 않게 격자 고정. [핸들|번호|닉네임|ID|AC ID|병합|삭제](v1 :2887-2891) ── */
.__v3c_settings_rivals { display: flex; flex-direction: column; }
.__v3c_settings_rivalhdr, .__v3c_settings_rival {
  display: grid; grid-template-columns: 18px 22px minmax(56px, 1fr) minmax(120px, 1.4fr) minmax(96px, 1.1fr) 18px auto;
  gap: 6px; align-items: center;
}
/* 🔴 헤더는 라이벌이 0명이면 hidden 이다 — 위 display:grid 가 hidden 을 이기므로 이 규칙이 없으면 빈 헤더가 남는다. 지우지 마라. */
.__v3c_settings_rivalhdr[hidden] { display: none; }
.__v3c_settings_rival { padding: 5px 0; border-bottom: 1px solid var(--v3-border-default); }
.__v3c_settings_rivalhdr { padding: 0 0 4px; font-size: 11px; font-weight: 600; color: var(--v3-text-disabled); border-bottom: 1px solid var(--v3-border-default); }
.__v3c_settings_hdr_ac { grid-column: span 2; }
/* ID·AC 입력칸은 격자 칸을 꽉 채운다 — 기본 폭(약 20자)이면 좁은 칸을 넘친다(v1 은 폭을 안 줬다). */
.__v3c_settings_rivalid, .__v3c_settings_rivalac { width: 100%; box-sizing: border-box; }
/* 🔴 touch-action:none 은 핸들에만 — 행 전체에 주면 모바일에서 목록을 스크롤할 수 없다. 이게 없으면 터치 드래그가 페이지 스크롤로 샌다(R-15 · v1 :2892). */
.__v3c_settings_drag { cursor: grab; color: var(--v3-text-disabled); font-size: 14px; line-height: 1; text-align: center; touch-action: none; user-select: none; }
/* 드래그 중인 행(v1 __settings_dragging :2893) */
.__v3c_settings_dragging { opacity: 0.55; background: var(--v3-bg-surface-raised); }
.__v3c_settings_dragging .__v3c_settings_drag { cursor: grabbing; }
.__v3c_settings_no { color: var(--v3-text-disabled); font-size: 12px; font-variant-numeric: tabular-nums; }
.__v3c_settings_who { color: var(--v3-text-main); font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.__v3c_settings_blank { display: block; }
.__v3c_settings_del { background: none; border: 0; color: var(--v3-text-dim); font-size: 12px; cursor: pointer; padding: 2px 4px; font-family: inherit; }
.__v3c_settings_del:hover { color: var(--v3-state-danger-soft); }
.__v3c_settings_empty { margin: 0; padding: 6px 0; color: var(--v3-text-muted); font-size: 13px; }
.__v3c_settings_addrow { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.__v3c_settings_add { flex: 0 0 auto; background: var(--v3-bg-surface-muted); color: var(--v3-text-main); border: 0; border-radius: var(--v3-radius-sm); cursor: pointer; padding: 4px 9px; }
.__v3c_settings_add:disabled { opacity: 0.4; cursor: default; }
.__v3c_settings_addhint { font-size: 11px; color: var(--v3-text-disabled); }

/* 미저장 입력 — 사이트 강조색 테두리. 체크박스는 테두리가 안 먹어 outline 으로(v1 :2908-2909).
   🔴 셀렉터를 `.__v3c_settingspage input.…` 로 둔 것은 위 입력칸 공통 규칙(특정도 0,2,1)을 **뒤 순서로** 이기기 위해서다 —
      `.__v3c_settings_dirty` 하나로 줄이면 공통 규칙의 border 가 이겨 표시가 조용히 사라진다(!important 를 쓰지 않았다). */
.__v3c_settingspage input.__v3c_settings_dirty { border-color: var(--v3-accent); }
.__v3c_settingspage input[type="checkbox"].__v3c_settings_dirty { outline: 2px solid var(--v3-accent); outline-offset: 1px; }

/* ── 메시지 · 저장 ── */
/* 🔴 min-height 로 자리를 미리 잡는다 — 없으면 문구가 뜰 때 저장 버튼이 아래로 밀린다(로그인 모달과 같다). */
.__v3c_settings_msg { margin: 0; min-height: 1.2em; font-size: 13px; color: var(--v3-text-muted); }
.__v3c_settings_msg[data-state="error"] { color: var(--v3-state-danger-soft); }
.__v3c_settings_msg[data-state="setup"] { color: var(--v3-state-warning); }
.__v3c_settings_msg[data-state="saved"] { color: var(--v3-state-success); }
.__v3c_settings_save { align-self: flex-start; padding: 8px 24px; border: 0; border-radius: var(--v3-radius-md); cursor: pointer; background: var(--v3-accent); color: var(--v3-bg-page); font-weight: 700; }
.__v3c_settings_save:disabled { opacity: 0.5; cursor: default; }

/* 좁은 화면 — 닉네임 칸을 접어 ID·AC 칸 폭을 확보한다(v1 :2902-2906). */
@media (max-width: 560px) {
  .__v3c_settings_rivalhdr, .__v3c_settings_rival { grid-template-columns: 18px 18px minmax(104px, 1.4fr) minmax(84px, 1.1fr) 18px auto; }
  .__v3c_settings_who, .__v3c_settings_hdr_name { display: none; }
  /* 핸들을 손가락으로 잡을 수 있게 줄 높이를 키운다(v1 엔 없다 — 모바일 조작성, 정본 S4-5f) */
  .__v3c_settings_drag { font-size: 18px; padding: 6px 0; }
}
/* ===== 4차 Settings 페이지 (S4-5f) 끝 ===== */

/* ===== 5차 Grid 서열표 — v1 원문 (2026-09-28) ===== */
/* 🔴 v1 styles.css 의 서열표 규칙 원문 복사다(62-187 · 244 · 2706-2710). 바꾼 것은 `#content` → `#__v3sh_main` 하나뿐.
   격자 자체의 CSS 는 OhSorryShelf 라이브러리가 injectStyle 로 넣는다. v1 을 고치면 여기도 같이 고쳐라.
   탭 줄은 v3 페이지 nav(.__v3c_pdtabs) 라 .shelf-level-tabs 규칙은 옮기지 않았다. */
/* v3 — 서열표 탭은 상태 전환이라 <a> 가 아니라 <button> 이다. 버튼 기본 배경·테두리만 걷어 PlayData 탭(<a>)과 같게 보이게 한다. */
button.__v3c_pdtab { background: none; border: 0; border-bottom: 2px solid transparent; font-family: inherit; cursor: pointer; }
/* v3 — 스택바 + 램프/스코어 전환 버튼 한 줄(제목 <h1> 은 없앴다, 2026-09-28). 위 여백은 스택바 대신 줄이 갖는다. */
#__v3sh_main .__v3g_stackrow { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
#__v3sh_main .__v3g_stackrow > .shelf-stackbar { margin-top: 0; flex: 1 1 auto; min-width: 0; }
#__v3sh_main .__v3g_stackrow > #__shelf_title:empty { display: none; }
/* 서열표 상단 컨트롤 — PC 1줄 / 모바일 2줄 */
.shelf-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 10px 0 12px;
  flex-wrap: wrap;
}
.shelf-controls-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1 1 auto;
  position: relative;
}
.shelf-controls-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: wrap;
}
@media (max-width: 760px) {
  .shelf-controls { flex-wrap: wrap; }
  .shelf-controls-left { width: 100%; }
  .shelf-controls-right {
    width: 100%;
    justify-content: flex-end;
    flex-wrap: wrap;
  }
}
/* 모바일 — 곡 셀이 EX스코어·DJ레벨을 둘 다 표시(gist 3열 격자, 768px 기준)하므로 "표시" 토글 불필요.
   #__v3sh_main 로 특정성을 올려 뒤에 오는 .shelf-control-group { display:flex } 에 안 덮이게. */
/* 서열표 제목(<h1>)에 붙는 램프/스코어 모드 스위치 — 제목이 곧 스위치다(정본 §6.1).
   박스·배경·테두리 없음: 제목 옆 보조 라벨처럼 보이되 색으로 누를 수 있음을 알린다. */
#__v3sh_main .shelf-title-mode {
  background: none;
  border: none;
  padding: 0;
  margin-left: 0;
  font-family: inherit;
  font-size: 13px;
  white-space: nowrap;
  font-weight: 700;
  color: #ff6b9d;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
#__v3sh_main .shelf-title-mode:hover { color: #ff9bce; }
/* DP12 탭 — 스코어 모드가 없는 화면. 숨기면 제목이 탭 이동마다 흔들려서 흐리게만 둔다. */
#__v3sh_main .shelf-title-mode:disabled { color: #495057; cursor: default; }
#__v3sh_main .shelf-title-mode:disabled:hover { color: #495057; }

@media (max-width: 768px) {
  #__v3sh_main .shelf-control-rightfield { display: none; }
  /* 곡명은 최대 두 줄 + 점수 한 줄. 바깥 고정 높이는 짧은 제목을 상하 가운데로 둔다. 곡명 12px(2026-10-01 사용자 요청). */
  #__v3sh_main .shelf-song { min-height: 46px; }
  /* 서열 라벨 sticky — 공유 규칙(gist-modules/ohsorryShelf.js)은 top:0 이라 v3 셸 sticky 줄(헤더·탭) 뒤에 가려진다. 셸 스택 높이만큼 내린다. */
  #__v3sh_main .shelf-level { top: var(--v3-sticky-top, 0px); }
  #__v3sh_main .shelf-song .shelf-song-text {
    font-size: 12px;
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    min-width: 0;
    height: 2.5em;
    min-height: 2.5em;
    max-height: 2.5em;
    white-space: normal;
    overflow-wrap: anywhere;
    overflow: hidden;
    line-height: 1.25;
    transform: none !important;
  }
  #__v3sh_main .shelf-song-title-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    max-height: 2.5em;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* 랭크差·EX 점수가 좁은 3열 칸에서도 잘리지 않게 한다. */
  #__v3sh_main .shelf-song-meta { overflow: visible; }
  /* 기록 없는 칸의 No Play·- — 배경에 가까운 어두운색 */
  #__v3sh_main .shelf-song-meta-np { color: #4a4a4a; }
}
/* 캡처 버튼 — 카메라 아이콘(텍스트 없음): 아이콘 중앙 정렬 + 패딩 축소 */
#__v3sh_main .shelf-capture-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 5px 10px;
}

.shelf-control-group {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  flex-wrap: nowrap;
}
/* 서열표 범례 — 항목(FULL COMBO 등) 안은 줄바꿈 없음. 8개 항목은 4개씩 2덩어리로 묶여
   (shelf.js 가 .shelf-lamp-legend-group 생성), 덩어리 단위로만 줄바꿈. */
#__v3sh_main .shelf-lamp-legend-item { white-space: nowrap; }
.shelf-lamp-legend-group {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px 12px;
}
.shelf-control-label {
  color: #868e96;
  font-size: 12px;
}

/* 서열표 링크형 컨트롤(박스 없는 버튼) */
.shelf-dot-sep {
  color: #495057;
  font-size: 12px;
  user-select: none;
}
.shelf-link-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--link-color, #adb5bd);
  padding: 2px 2px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  line-height: 1.2;
}
.shelf-link-btn:hover {
  color: var(--link-color, #ced4da);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.shelf-link-btn.active {
  color: var(--link-color, #f1f3f5);
  font-weight: 800;
}
/* lamp(vType) 필터 + DP11 기준(dp11axis) 토글 — 미선택은 회색 (선택 시 위 .active 가 램프색 + 볼드 적용).
   sort/rightField 버튼은 --link-color 가 없어 영향 없음. */
.shelf-link-btn[data-vtype]:not(.active),
.shelf-link-btn[data-dp11axis]:not(.active) {
  color: #868e96;
}
/* 곡 셀 좌측 lamp 색박스 — hover 마퀴로 흐르는 곡명이 색박스 위가 아닌 아래로 가려지게.
   기본은 곡명(.shelf-song-text)이 뒤 형제라 위에 그려짐 → 색박스에 z-index 부여해 올림. */
.shelf-song-lampbox { position: relative; z-index: 2; }
/* SP12 서열표 — tier 그룹 안 '개인차 N곡' 라벨. 모바일=전체폭 상단 in-flow / PC=왼쪽 레벨컬럼 absolute. */
.__sp_caution_div { grid-column: 1 / -1; font-size: 10px; font-weight: 700; color: #868e96; padding: 4px 6px 2px; }
@media (min-width: 769px) {
  .__sp_caution_div { position: absolute; left: -64px; width: 58px; text-align: right; grid-column: auto; padding: 0; }
}
/* ===== 5차 Grid 서열표 끝 ===== */

/* ===== v1 곡 모달(송 디테일) — v1 원문 (2026-09-28) ===== */
/* 🔴 v1 styles.css 에서 곡 모달·곡행이 쓰는 규칙(.__shelf_rank* · .__shelf_hist* · .__pd_* · .__sp_song* · .__dbr_/__os_ 곡행 일부)을
   @media 구조째 원문 그대로 뽑은 것이다(263규칙). 사용자 결정: 송 디테일은 v1 을 그대로 가져온 뒤 v3 설계(§15~19)로 바꿔 간다.
   손으로 고치지 말고, v3 설계로 바꿀 때 이 절을 통째로 갈아라. */
/* 서열표 곡별 랭킹 모달 */
.__shelf_rank_modal {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: rgba(0, 0, 0, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
}
.__shelf_rank_dialog {
  position: relative;
  width: min(760px, 100%);
  max-height: 95vh;
  background: #15171b;
  border: 1px solid #343a40;
  border-radius: 8px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.__shelf_rank_close {
  position: absolute;
  top: 12px;
  right: 14px;
  z-index: 1;
  border: 0;
  background: transparent;
  color: #868e96;
  font-size: 22px;
  cursor: pointer;
}
.__shelf_rank_close:hover { color: #f1f3f5; }
.__shelf_rank_content {
  max-height: 95vh;
  overflow: auto;
  padding: 24px;
  scrollbar-width: thin;                   /* Firefox — 얇게 */
  scrollbar-color: #3a3f47 transparent;    /* thumb / track(투명) */
  container-type: inline-size;             /* 난이도행(__pd_tbody)이 @container(max-width:600px) 2줄 전환을 타도록 — 모달엔 컨테이너 조상이 없어 누락돼 있던 기존 버그 */
}
.__shelf_rank_content::-webkit-scrollbar { width: 6px; height: 6px; }
.__shelf_rank_content::-webkit-scrollbar-track { background: transparent; }
.__shelf_rank_content::-webkit-scrollbar-thumb { background: #3a3f47; border-radius: 3px; }
.__shelf_rank_content::-webkit-scrollbar-thumb:hover { background: #4a505a; }
.__shelf_rank_loading,
.__shelf_rank_error,
.__shelf_rank_empty {
  color: #adb5bd;
  text-align: center;
  padding: 34px 12px;
}
.__shelf_rank_error { color: #ffc9c9; }
.__shelf_rank_head { padding-right: 34px; }
.__shelf_rank_series {
  color: #868e96;
  font-size: 12px;
  margin-bottom: 4px;
}
.__shelf_rank_head h2 {
  color: #f8f9fa;
  font-size: 20px;
  line-height: 1.3;
  margin: 0 0 6px;
}
/* 난이도(N/H/A/L) 행 — 플레이데이터 row(__pd_tr) 룩 재사용. __pd_tbody grid 가 전역이라 그대로 적용됨.
   클릭 시 해당 난이도 랭킹으로 전환, 현재 난이도는 __shelf_rank_pdactive 로 강조. */
.__shelf_rank_pdtabs {
  margin: 0 0 14px;
  border-radius: 6px;
  overflow: hidden;
}
.__shelf_rank_pdtabs .__pd_tr { cursor: pointer; }
/* 현재 선택 난이도 강조 — 그 난이도 slot 색을 옅게(opacity) 깐 배경. (NORMAL/HYPER/ANOTHER/LEGGENDARIA)
   __pd_tr 은 display:contents 라 셀에 배경 → 라이벌 행(__pd_rival_*)과 동일하게:
   1) box-shadow 로 tbody gap(행 사이 1px) 메우고  2) RATE bar 트랙(.__pd_rate_bg, 불투명)에도 색을 먹인다.
   셀 배경은 rgba(0.2) 유지(lamp 등 셀 고유색이 비쳐야 함) — gap/트랙엔 rgb(31,31,31) 위 그 0.2 를 합성한 불투명색을 써 톤을 맞춘다. */
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="NORMAL"]      > .__pd_cell { background: rgba(25, 113, 194, 0.2); box-shadow: 0 1px 0 rgb(30, 47, 64), 0 -1px 0 rgb(30, 47, 64); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="HYPER"]       > .__pd_cell { background: rgba(220, 175, 69, 0.2); box-shadow: 0 1px 0 rgb(69, 60, 39), 0 -1px 0 rgb(69, 60, 39); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="ANOTHER"]     > .__pd_cell { background: rgba(220, 53, 69, 0.2); box-shadow: 0 1px 0 rgb(69, 35, 39), 0 -1px 0 rgb(69, 35, 39); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="LEGGENDARIA"] > .__pd_cell { background: rgba(214, 120, 200, 0.2); box-shadow: 0 1px 0 rgb(68, 49, 65), 0 -1px 0 rgb(68, 49, 65); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="NORMAL"]      .__pd_rate_bg { background: rgb(30, 47, 64); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="HYPER"]       .__pd_rate_bg { background: rgb(69, 60, 39); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="ANOTHER"]     .__pd_rate_bg { background: rgb(69, 35, 39); }
.__shelf_rank_pdtabs .__pd_tr.__shelf_rank_pdactive[data-diff="LEGGENDARIA"] .__pd_rate_bg { background: rgb(68, 49, 65); }
/* 🔴 2026-09-29 세션 120 — 곡 상세 난이도 행이 추천곡 행(chartRecRow · __v3c_tr)으로 바뀌었다(사용자 결정: 행 모양·데이터 = 추천곡 기준).
   위 __pd_tr 규칙은 v3 곡 상세에서는 더 쓰이지 않는다(남겨 둔 것은 v1 과 같은 클래스라 지우는 범위를 넓히지 않으려는 것).
   강조 색은 __pd_tr 시절과 같은 rgba(0.2) 값 — 칠하는 방식은 아래 「반투명 덮개」 절. */
.__shelf_rank_pdtabs > .__v3c_tr { cursor: pointer; }
/* 🔴 선택 행 강조 = 행 전체에 반투명 덮개 한 겹(::after, 투명도 0.2 — 세션 120 사용자 지시 「오파시티」).
   칸마다 rgba 를 칠하면 칸이 없는 자리(행 배경)·두 칸이 겹치는 자리(notes+배치 칸)의 진하기가 달라져 얼룩진다(세션 120 사용자 보고).
   덮개는 격자 틈까지 한 번에 덮는다. 🔴 램프 칸은 덮개 위(z-index 2)로 올린다 — 램프 색이 가려지면 안 된다(사용자 지시).
   ⚠️ 절대배치 ::after 는 grid 항목이 아니라 행(grid 컨테이너)의 padding box 기준으로 깔린다 — 행 position: relative 가 필요하다. */
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive { position: relative; }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive[data-diff="NORMAL"]::after      { background: rgba(25, 113, 194, 0.2); }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive[data-diff="HYPER"]::after       { background: rgba(220, 175, 69, 0.2); }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive[data-diff="ANOTHER"]::after     { background: rgba(220, 53, 69, 0.2); }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive[data-diff="LEGGENDARIA"]::after { background: rgba(214, 120, 200, 0.2); }
.__shelf_rank_pdtabs > .__v3c_tr.__shelf_rank_pdactive > .__v3c_cell.__v3c_lamp { position: relative; z-index: 2; }
/* 점수 히스토리 그래프 — 난이도 행과 랭킹 테이블 사이. played_version 별 색 라인(SVG). */
.__shelf_rank_history {
  margin: 0 0 14px;
  padding: 8px 8px 4px;
  position: relative;   /* 툴팁(.__shelf_hist_tip) 기준 상자 */
}
/* touch-action: 세로 스크롤은 살리고 가로 드래그만 그래프가 먹는다 — 그래프 위에서 페이지가 안 내려가면
   모바일에서 곡 모달을 스크롤할 수가 없다. */
.__shelf_hist_svg { width: 100%; height: auto; display: block; touch-action: pan-y; }
.__shelf_hist_grid { stroke: #2d323c; stroke-width: 1; }
.__shelf_hist_axis { fill: #868e96; font-size: 8px; font-variant-numeric: tabular-nums; }
.__shelf_hist_toplabel { fill: #adb5bd; font-size: 6px; }
.__shelf_hist_topscore { fill: #f8f9fa; font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; }
.__shelf_hist_topdate { fill: #868e96; font-size: 6px; font-variant-numeric: tabular-nums; }
.__shelf_hist_legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; padding: 0 4px; }
.__shelf_hist_leg { display: inline-flex; align-items: center; gap: 4px; color: #adb5bd; font-size: 11px; }
.__shelf_hist_leg i { width: 12px; height: 3px; border-radius: 2px; display: inline-block; }
/* 라인별 DJ NAME — 그 유저의 최고점 옆. paint-order:stroke 로 글자 뒤에 배경색 테두리를 깔아
   라인·격자 위에 겹쳐도 읽힌다(fill 은 JS 가 라인 색으로 지정). */
.__shelf_hist_name {
  font-size: 7px; font-weight: 700;
  paint-order: stroke; stroke: #14171c; stroke-width: 2.5px; stroke-linejoin: round;
}
/* hover/터치 툴팁 — 점수·날짜는 여기서만 보여준다(그래프에 다 적으면 라벨이 서로 밟는다). */
.__shelf_hist_tip {
  position: absolute; transform: translate(-50%, calc(-100% - 8px));
  pointer-events: none; z-index: 5;
  display: flex; align-items: baseline; gap: 6px;
  padding: 3px 7px; border: 1px solid #3b4048; border-radius: 4px;
  background: #12151a; white-space: nowrap;
  font-size: 11px; font-variant-numeric: tabular-nums;
}
.__shelf_hist_tip[hidden] { display: none; }
.__shelf_hist_tipname { color: #f1f3f5; font-weight: 700; }
.__shelf_hist_tipscore { color: #f8f9fa; font-weight: 700; }
.__shelf_hist_tipdate { color: #868e96; font-size: 10px; }
@media (max-width: 640px) {
  .__shelf_rank_modal { padding: 0; align-items: stretch; }
  .__shelf_rank_dialog { width: 100%; max-height: var(--vh, 100dvh); border-radius: 0; border-left: 0; border-right: 0; }
  .__shelf_rank_content { max-height: var(--vh, 100dvh); padding: 22px 12px; }
  .__shelf_rank_close { display: none; }
}
/* 합본/라이벌 플레이데이터 — 출처 뱃지(AC/INF) + 라이벌 DJNAME. PC: 점수 셀 위 / 모바일: LAMP텍스트 왼쪽. */
.__pd_tr > .__pd_cell.__pd_num { position: relative; }
/* PC 뱃지 absolute 기준 */
.__pd_srcwrap {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  pointer-events: none;   /* 점수 row 클릭(랭킹 모달) 방해 안 하게 */
}
.__pd_srcwrap_pc {       /* PC — 점수 셀 안 absolute, 점수 위에. */
  position: absolute;
  top: -1px;
  left: 10px;
  z-index: 1;
}
.__pd_srcwrap_mo { display: none; }
/* 모바일판 — PC 에선 숨김. */
.__pd_srcbadge {
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  padding: 1px 3px;
  border-radius: 3px;
  letter-spacing: 0.3px;
}
.__pd_src-ac { color: #ffd8a8; background: rgba(232, 130, 30, 0.35); }
.__pd_src-inf { color: #a5d8ff; background: rgba(34, 139, 230, 0.35); }
.__pd_rivalname {
  font-size: 9px;
  color: #ced4da;
  white-space: nowrap;
  max-width: 88px;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 820px) {
  .__pd_tr:not(.__pd_recrow) > .__pd_cell.__pd_num .__pd_srcwrap_pc { display: none; }
  .__pd_tr:not(.__pd_recrow) > .__pd_cell.__pd_lamptext { position: relative; overflow: visible; }
  .__pd_tr:not(.__pd_recrow) > .__pd_cell.__pd_lamptext .__pd_srcwrap_mo {
    display: inline-flex;
    position: absolute;
    right: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-right: 4px;
    z-index: 2;
  }
}
/* 라이벌 승패 행 — .__pd_tr 은 display:contents 라 셀에 배경. 지면 붉은/이기면 푸른 미세 배경.
   tbody gap(행 사이 1px)과 RATE bar 트랙(.__pd_rate_bg, 불투명)에도 색이 먹도록:
   - 셀 배경을 (기존 셀색 + 틴트) 블렌딩한 solid 로, box-shadow 1px 로 위아래 gap 까지 칠함.
   - RATE bar 트랙도 같은 solid 로 덮어 빈 트랙 부분에 색이 보이게. */
.__pd_tr.__pd_rival_lose > .__pd_cell { background: #382224; box-shadow: 0 1px 0 #382224, 0 -1px 0 #382224; }
.__pd_tr.__pd_rival_win  > .__pd_cell { background: #1f2d39; box-shadow: 0 1px 0 #1f2d39, 0 -1px 0 #1f2d39; }
.__pd_tr.__pd_rival_lose:hover > .__pd_cell { background: #492427; box-shadow: 0 1px 0 #492427, 0 -1px 0 #492427; }
.__pd_tr.__pd_rival_win:hover  > .__pd_cell { background: #20374b; box-shadow: 0 1px 0 #20374b, 0 -1px 0 #20374b; }
.__pd_tr.__pd_rival_lose .__pd_rate_bg { background: #382224; }
.__pd_tr.__pd_rival_win  .__pd_rate_bg { background: #1f2d39; }
.__pd_tr.__pd_rival_lose:hover .__pd_rate_bg { background: #492427; }
.__pd_tr.__pd_rival_win:hover  .__pd_rate_bg { background: #20374b; }
/* 랭킹 모달 — 라이벌 비교시 본인/라이벌 난이도행 구분 라벨. */
.__shelf_rank_cmplabel {
  font-size: 11px;
  font-weight: 700;
  color: #adb5bd;
  margin: 8px 0 3px;
  padding: 0 2px;
}
/* 검색창 wrap — drop 위치 기준 (position: relative). */
.__pd_search_wrap {
  position: relative;
  flex: 1 1 120px;
  min-width: 100px;
  max-width: 240px;
}
/* 곡명 검색창 — 기존 토글 버튼과 톤 통일. wrap 안 가득 채움. */
.__pd_searchbox {
  width: 100%;
  height: 27px;
  box-sizing: border-box;
  background: #1c2027;
  border: 1px solid #2d323c;
  color: #e9ecef;
  font-family: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 4px;
  outline: none;
}
.__pd_searchbox:focus { border-color: #ff6b9d; }
/* 검색 결과 drop — input 바로 아래, 최대 5개 항목. JS 가 display 토글. */
.__pd_search_drop {
  position: absolute;
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  background: #1c2027;
  border: 1px solid #2d323c;
  border-radius: 4px;
  z-index: 10;
  display: none;
  /* 5 항목 높이 (padding 6+6 + font 13*1.4 ≈ 31) × 5 = 155 — wheel/swipe 로 추가 스크롤. */
  max-height: 155px;
  overflow-y: auto;
  scrollbar-width: none;        /* Firefox */
  -ms-overflow-style: none;     /* IE 11 */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.__pd_search_drop::-webkit-scrollbar { display: none; }
.__pd_search_item {
  padding: 6px 10px;
  font-size: 13px;
  color: #e9ecef;
  cursor: pointer;
  border-bottom: 1px solid #2d323c;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.__pd_search_item:last-child { border-bottom: none; }
.__pd_search_item:hover { background: #2a2f37; color: #ff6b9d; }
.__pd_search_empty {
  padding: 6px 10px;
  font-size: 12px;
  color: #6c757d;
  font-style: italic;
}
/* 레벨모드 검색 결과의 레벨 뱃지 — 같은 곡명이 다른 레벨에도 있을 수 있어 구분용. */
.__pd_search_lvl {
  color: #74a9e0;
  font-size: 11px;
  font-weight: 700;
}
.__pd_difffilter {
  margin-left: auto;       /* 검색창은 왼쪽 고정, 토글 버튼 묶음은 오른쪽으로 밀림 */
  box-sizing: border-box;
  text-align: center;
}
/* 레벨모드 — 난이도 토글은 레벨별 폴더에 아무 영향이 없어 숨김. 대신 sortbtn 이 margin-left:auto 를 이어받아
   남은 토글(정렬/배치)이 계속 오른쪽에 붙게 한다. */
.__pd_lvlmode .__pd_difffilter { display: none; }
.__pd_lvlmode .__pd_sortbtn { margin-left: auto; }
.__pd_zasamode .__pd_difffilter { display: none; }
.__pd_zasamode .__pd_sortbtn { margin-left: auto; }
.__pd_exmode .__pd_difffilter { display: none; }
.__pd_exmode .__pd_sortbtn { margin-left: auto; }
/* 길게 누르기(1.5초) 선택 목록 — 모드/난이도/정렬 토글 공용.
   touch-action:none 이라야 누른 채 위아래로 끌 때 페이지가 스크롤되지 않는다. */
.__pd_foldermodebtn, .__pd_difffilter, .__pd_sortbtn { touch-action: none; }
.__pd_pickmenu {
  position: fixed;
  z-index: 10002;
  min-width: 132px;
  padding: 4px;
  background: #2d2d2d;
  border: 1px solid #495057;
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0,0,0,.5);
}
.__pd_pickitem {
  padding: 8px 12px;
  font-size: 13px;
  color: #ced4da;
  border-radius: 4px;
  white-space: nowrap;
  cursor: pointer;
}
.__pd_pickitem.active { color: #fff; font-weight: 700; }
.__pd_pickitem.hover { background: #ff6b9d; color: #fff; }
/* 배치 토글 — OFF 일 때도 또렷하게 (다른 토글은 항상 active 라 무관) */
.__pd_layoutbtn {
  background: transparent;
  border-color: #495057;
  color: #e9ecef;
  font-weight: 600;
}
/* 폴더 모드 토글(S/L) — 검색창과 높이 동일한 정사각 버튼. 시리즈/레벨 폴더 섹션 중 하나만 보여준다. */
.__pd_foldermodebtn {
  flex: 0 0 auto;
  width: 27px;
  height: 27px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}
.__pd_seriesfolders, .__pd_lvlfolders, .__pd_zasafolders, .__pd_targetwrap, .__pd_exfolders, .__pd_rivalfolders {
  display: flex;
  flex-direction: column;
}
.__pd_lvlfolders { display: none; }
.__pd_zasafolders { display: none; }
.__pd_targetwrap { display: none; }
.__pd_exfolders { display: none; }
.__pd_rivalfolders { display: none; }
.__pd_lvlmode .__pd_seriesfolders { display: none; }
.__pd_lvlmode .__pd_lvlfolders { display: flex; }
.__pd_lvlmode .__pd_zasafolders { display: none; }
.__pd_lvlmode .__pd_exfolders { display: none; }
.__pd_zasamode .__pd_seriesfolders { display: none; }
.__pd_zasamode .__pd_lvlfolders { display: none; }
.__pd_zasamode .__pd_zasafolders { display: flex; }
.__pd_zasamode .__pd_exfolders { display: none; }
.__pd_exmode .__pd_seriesfolders,
.__pd_exmode .__pd_lvlfolders,
.__pd_exmode .__pd_zasafolders { display: none; }
.__pd_exmode .__pd_targetwrap { display: flex; }
.__pd_exmode .__pd_exfolders { display: flex; }
.__pd_rivalmode .__pd_seriesfolders, .__pd_rivalmode .__pd_lvlfolders, .__pd_rivalmode .__pd_zasafolders, .__pd_rivalmode .__pd_exfolders { display: none; }
.__pd_rivalmode .__pd_rivalfolders { display: flex; }
/* R모드는 라이벌 폴더만 — 추천곡·연습곡·추천곡 v3 는 .__uprofile_pdfolders 직속이라 여기서 숨긴다.
   (v3 도 __os_recfolder 를 같이 달아 아래 선택자에 그대로 걸린다 — 사용자 스펙: R모드엔 미표시.)
   (격파·도전장 폴더도 __os_recfolder 를 달지만 .__pd_rivalfolders 안에 있어 직속 선택자에 안 걸린다.) */
.__pd_rivalmode .__uprofile_pdfolders > .__os_recfolder,
.__pd_rivalmode .__uprofile_pdfolders > .__os_weakfolder { display: none; }
/* E모드도 마찬가지 — 목표/환산난이도 폴더만 두고 추천곡·연습곡·추천곡 v3 는 숨긴다. */
.__pd_exmode .__uprofile_pdfolders > .__os_recfolder,
.__pd_exmode .__uprofile_pdfolders > .__os_weakfolder { display: none; }
.__pd_rivalmode .__pd_difffilter, .__pd_rivalmode .__pd_search_wrap { display: none; }
.__pd_rivalmode .__pd_sortbtn { margin-left: auto; }
/* ─── 채보별 row 테이블 — INFOhSorry ChartTable (.ct-tr / .ct-cell) 디자인 포팅 ─── */
/* 폴더 안 row 영역은 grid 7 컬럼: [lamp색박스 | LV | 곡명 | NOTES | LAMP텍스트 | RATE바 | SCORE+grade] */
.__pd_tbody {
  display: grid;
  grid-template-columns: 8px 32px 1fr 45px 100px 130px 95px;
  gap: 1px 0;                  /* 가로줄만 (row 사이 1px) */
  background: #14171c;          /* gap 색 = 어두운 줄 */
  border-top: 1px solid #2d323c;
}
/* bpLayout(BP 보유 INF 유저) — 꼬리 4셀 순서가 [노트수,램프,레이트,EX] → [램프,레이트,EX,BP] 로 바뀌는
   표 전체 스위치. DOM 순서만 바뀌므로 트랙 폭도 그 순서(100/130/95/45)로 재배정. */
.__pd_tbody.__pd_bplayout {
  grid-template-columns: 8px 32px 1fr 100px 130px 95px 45px;
}
.__pd_tr { display: contents; }
.__pd_tr > .__pd_cell {
  padding: 6px 8px;
  box-sizing: border-box;
  min-height: 40px;            /* PC 곡 목록 row 높이 — Recent/PlayData/추천곡/폴더 일관 40px */
  background: rgb(31, 31, 31);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  cursor: pointer;
  color: #e9ecef;
}
.__pd_tr:hover > .__pd_cell,
.__pd_tr.__pd_highlight > .__pd_cell { background: #2d2d2d; }
.__pd_tr > .__pd_cell.__pd_num {
  justify-content: flex-start;
  padding-left: 12px;
}
.__pd_tr > .__pd_cell.__pd_notes {
  justify-content: flex-end;    /* 노트수 우측 정렬 — 뱃지 + 노트수 함께 표시 시 노트수 위치 일관 */
  padding-right: 6px;
  overflow: visible;            /* 토글 ON 일 때 뱃지가 좁은 NOTES cell 넘어가도 보이게 */
}
/* BP 셀(bpLayout) — "BP" 라벨과 숫자를 별도 span 으로 감싸 그리드(cell) 안에서 라벨은 왼쪽정렬,
   숫자는 오른쪽정렬(2026-08-16, PC·모바일 공통). 어두운(muted) 색. */
.__pd_tr > .__pd_cell.__pd_notes.__pd_bp .__pd_bp_wrap {
  display: flex;
  flex: 1;
  min-width: 0;
  justify-content: space-between;
  align-items: center;
  gap: 4px;
}
.__pd_tr > .__pd_cell.__pd_notes.__pd_bp .__pd_bp_label {
  flex: 0 0 auto;
  color: #6c757d;
  font-size: 0.85em;
}
.__pd_tr > .__pd_cell.__pd_notes.__pd_bp .__pd_notes_val {
  color: #6c757d;
  text-align: right;
}
/* ★값(zasaLevel) — 일반 곡 row 에서는 PC 화면엔 안 보이고 모바일(@container 600px)에서만 notes 칸 왼쪽에 표시(2026-08-16).
   추천곡(__os_recdiff)은 별개 클래스라 무관. */
.__pd_notes_star {
  display: none;
}
/* 노트수 값이 '-' (textage-meta 매칭 실패) 인 cell — 가운데 정렬 */
.__pd_tr > .__pd_cell.__pd_notes.__pd_notes_empty {
  justify-content: center;
  padding-right: 0;
}
/* NOTES cell — 노트수는 항상 보임. 배치 뱃지는 toggle ON 일 때만 노트수 앞에 같이. */
.__pd_layout_val { display: none; }
.__pd_layoutmode .__pd_layout_val { display: inline-block; }
/* 배치 라벨 뱃지 — ohsorryRender 의 .rec-flip 과 같은 스타일 (pink 보더 + 9px bold). */
.__pd_layoutmode .__pd_layout_badge {
  color: #ff6b9d;
  font-size: 9px;
  font-weight: 700;
  padding: 0 3px;
  margin-right: 4px;            /* 노트수와 간격 */
  border: 1px solid #ff6b9d;
  border-radius: 2px;
  line-height: 1.2;
  white-space: nowrap;          /* "F M/N" 같이 공백 있어도 한 줄 */
  position: relative;
  z-index: 1;                   /* 옆 cell 위에 떠서 잘 보이게 */
}
/* lamp 색박스 — 좁은 컬럼, gap(1px) 침범해서 색이 row 끊김 없이 이어짐.
   색 정의는 일반 .__pd_lamp-XX 로 분리 — 폴더 summary 의 색박스도 같은 클래스 재사용. */
.__pd_cell.__pd_lamp {
  margin: -1px 0;
  padding: 0 !important;
  cursor: help;
}
/* !important — `.__pd_tr > .__pd_cell { background:#21252c }` (specificity 0,2,0) 가
   단순 클래스 lamp 셀렉터 (0,1,0) 보다 강해서 색이 안 먹힘 → 강제 override. */
.__pd_lamp-NP { background: #2a2e35 !important; }
.__pd_lamp-AC { background: #9966cc !important; }
.__pd_lamp-EC { background: #7bc16a !important; }
.__pd_lamp-NC { background: #5cb8ea !important; }
.__pd_lamp-HC {
  background: #ffffff !important;
  border-left: 1px solid #c8c8c8;
  border-right: 1px solid #c8c8c8;
}
.__pd_lamp-EX { background: #dcaf45 !important; }
.__pd_lamp-F { animation: __pd_lampBlinkF 0.9s linear infinite; }
.__pd_lamp-FC { animation: __pd_lampBlinkFC 1.4s linear infinite; }
.__pd_lamp-PFC { animation: __pd_lampBlinkFC 0.9s linear infinite; }
/* 시리즈 폴더 summary 의 lamp 색박스 — 곡 row 의 lamp cell 처럼 왼쪽 가장자리에 붙고 상하 가득. */
.__pd_summarylamp {
  align-self: stretch;       /* summary height(40px) 가득 */
  width: 8px;                /* 곡 row lamp 컬럼 폭과 동일 */
  margin-right: 10px;
  flex-shrink: 0;
}
/* LV — 큰 숫자, 가운데 정렬, slot 색 (inline style) */
.__pd_cell.__pd_level {
  padding: 2px 6px !important;
  font-size: 20px;
  line-height: 1;
  font-weight: 700;
  justify-content: center;
}
/* 곡명 — 1fr. 한 줄 고정, 길면 오른쪽 말줄임. LEG 면 † 접두사 + slot 색 (inline style) */
.__pd_cell.__pd_title {
  display: flex;
  align-items: center;       /* 세로 가운데 정렬 (셀 높이 40px) */
  overflow: hidden;
  font-size: 14px;
  padding-left: 0;
}
/* 곡명 위 시리즈 태그(#시리즈명) — 추천곡 폴더(__dbr_titlebox/__dbr_series)와 동일한 컴포넌트 재사용.
   zasa 는 titlebox 밖의 별도 span 이라 태그가 곡명 줄에만 얹히고 zasa 위로는 안 올라감(PC/모바일 공통 — 모바일도
   같은 DOM 이라 별도 분기 불필요). __pd_headtitlebox 는 chartRow/chartRecentRow(headCells) 전용 마커 —
   __dbr_titlebox 기본값(min-width:0)에 flex:1 1 auto 만 더해 title cell 의 남는 폭을 정확히 차지하게 한다
   (DBR 에서 이미 겪은 "titlebox 폭 0 으로 접혀 곡명이 통째로 안 보이는" 버그와 동일 원인 — 여기서도 동일 처방). */
.__pd_cell.__pd_title > .__pd_headtitlebox { flex: 1 1 auto; }
/* 시리즈 폴더(S모드)는 폴더 자체가 이미 그 시리즈로 묶여 있어 곡마다 태그가 중복 — 숨김.
   레벨/zasa 폴더는 S/L/Z 토글로 컨테이너 자체가 보이거나 숨겨지므로 모드 클래스 분기 없이 이 규칙 하나로 충분. */
.__pd_seriesfolders .__dbr_series { display: none; }
/* 곡명 텍스트 — 한 줄 고정, 넘치면 오른쪽 말줄임(줄바꿈 없음). flex 자식이라 min-width:0 로 줄어들게. (titlebox 중첩 무관하게 descendant 매칭) */
.__pd_cell.__pd_title .__pd_songname {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  line-height: 1.25;
}
/* 곡명 앞 zasa 레벨 — 있을 때만 표시. 작고 회색. width 20px 고정 (마진 없음). */
.__pd_zasa {
  display: inline-block;
  width: 20px;
  margin-right: 3px;
  text-align: right;
  color: #868e96;
  font-size: 10.5px;
  font-weight: 400;
}
/* PC 한 줄 모드 — level cell 안의 zasa span 은 숨김 (zasa 는 곡명 앞 `.__pd_zasa` 가 담당).
   두 줄 모드 container query 안에서 다시 표시 + flex column 배치 (game 1줄 / zasa 1줄). */
.__pd_level_zasa { display: none; }
.__pd_cell.__pd_lamptext {
  font-size: 11.5px;
  letter-spacing: 0.3px;
  justify-content: center;
  font-weight: 700;
}
/* RATE 막대 — 진행률 + cut 라인 + letter + 백분율 */
.__pd_cell.__pd_ratebar {
  padding: 0 !important;
  position: relative;
  overflow: hidden;
}
.__pd_rate_bg {
  position: absolute; inset: 0;
  background: rgb(31, 31, 31);
}
.__pd_rate_fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: linear-gradient(to right, #c44444, #e58484);
}
.__pd_rate_cut {
  position: absolute; top: 0; bottom: 0;
  width: 1px; background: #4a4f58; z-index: 1;
}
.__pd_rate_text {
  position: relative; z-index: 2;
  padding-left: 6px;
  font-size: 11.5px;
  color: #e9ecef;
  text-shadow: 0 0 2px rgba(0, 0, 0, 0.7);
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
}
.__pd_rate_letter {
  font-weight: 700;
  color: #b0b5bd;
  text-shadow:
    -1px -1px 0 #1a1d23,
     1px -1px 0 #1a1d23,
    -1px  1px 0 #1a1d23,
     1px  1px 0 #1a1d23;
}
.__pd_rate_pct { font-size: 10.5px; color: #adb5bd; }
.__pd_empty { color: #5e6168; padding-left: 6px; }
.__pd_cell.__pd_empty { color: #5e6168; }
/* Recent 전용 — grid 6 컬럼 (lamp box / LV / 곡명 / lamp변동 / DJ변동 / SCORE변동). */
/* 🔴 Recent 행 — **10열 2행**. PC·모바일이 같은 모양이다(2026-09-23 개편. v3 와 같은 격자로 맞췄다).
     col1 램프 · col2 레벨(게임레벨/zasa) · col3~7 곡명(윗줄)
     아랫줄 → col4 램프 prev(NP→) · col5 현재 램프 · col6 DJ prev(B→) · col7 rate 바
     col8 ★ ↔ EX prev · col9 BP prev ↔ EX 현재 · col10 BP 현재 ↔ 등급차
     (🔴 2026-09-25 세션 90 — 램프 prev 를 제 열로 갈라 9열 → 10열. 종전 col4 46px = 28px + 22px)
   🔴 **오른쪽 셋을 열로 가른 이유** — 종전처럼 한 칸(__pd_rcexchange)에 몰면 prev 유무·자릿수에 따라
   현재값의 가로 위치가 행마다 흔들린다. 열이 갈려야 세로가 맞는다.
   🔴 **행을 grid 로 돌린다** — __pd_tr 은 기본이 display:contents 라 tbody grid 에 셀이 직접 놓이는데,
   그러면 여러 행이 같은 grid 행에 겹친다. 추천곡 폴더(__os_recfolder)가 이미 한 전환과 같다. */
.__pd_rctbody { display: flex; flex-direction: column; gap: 1px; }
/* 🔴 Recent hover — **셀에도 건다.** 셀마다 자기 배경이 있어서 행 배경만 바꾸면
   **칸마다 hover 색이 갈린다**(2026-09-24 사용자 지적).
   ⚠️ 단 **9열은 hover 에서도 투명**이다 — 불투명해지면 10열로 넘긴 글자 뒤를 가린다. */
.__pd_tr.__pd_recrow:hover > .__pd_cell,
.__pd_tr.__pd_recrow.__pd_highlight > .__pd_cell { background: #2d2d2d; }
.__pd_tr.__pd_recrow:hover > .__pd_cell.__pd_rcbpprev,
.__pd_tr.__pd_recrow:hover > .__pd_cell.__pd_rcexcur,
.__pd_tr.__pd_recrow.__pd_highlight > .__pd_cell.__pd_rcbpprev,
.__pd_tr.__pd_recrow.__pd_highlight > .__pd_cell.__pd_rcexcur { background: transparent; }
.__pd_tr.__pd_recrow:hover { background: #2d2d2d; }
.__pd_tr.__pd_recrow {
  display: grid;
  grid-template-columns: 8px 32px minmax(0, 1fr) 28px 22px 34px 82px 45px 40px 45px;
  grid-template-rows: auto auto;
  background: rgb(31, 31, 31);   /* cell 과 같은 색 — 열 사이 틈으로 다른 색이 비쳐 격자선처럼 보이는 것 방지 */
}
/* 일반행용 min-height:40px 리셋 — 2행이면 줄마다 40px = 80px 가 된다. */
.__pd_tr.__pd_recrow > .__pd_cell { padding-top: 0; padding-bottom: 0; min-height: 0; }
/* ── 셀 배치 ── */
.__pd_tr.__pd_recrow > .__pd_cell.__pd_lamp { grid-column: 1; grid-row: 1 / 3; }
/* 🔴 Recent 레벨 칸 — 2행 모드라 게임레벨/zasa 를 **세로로** 쌓는다.
   ⚠️ **기본 `.__pd_cell.__pd_level` 은 `font-size: 20px` 에 flex 가로 방향이다.**
   zasa 를 `display:block` 으로 켜기만 하면 `12` 와 `12.4` 가 **가로로 붙어 `1212.4` 로 보이고
   20px 로 거대해진다** — 2026-09-24 프로덕션 실사고. 세로 쌓기가 **모바일 미디어쿼리 안에만**
   있었던 것이 원인이다. 추천곡 폴더(`__os_recfolder`)도 같은 처방을 쓴다(`:2673`). */
.__pd_tr.__pd_recrow > .__pd_cell.__pd_level {
  grid-column: 2;
  grid-row: 1 / 3;
  flex-direction: column;
  line-height: 1;
  justify-content: center;
  align-items: center;
  gap: 2px;
  padding: 0 4px !important;
}
.__pd_tr.__pd_recrow > .__pd_cell.__pd_level .__pd_level_game { font-size: 18px; font-weight: 600; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_level .__pd_level_zasa {
  display: block;
  font-size: 10.5px;
  font-weight: 400;
  color: #868e96;
}
.__pd_tr.__pd_recrow > .__pd_cell.__pd_title { grid-column: 3 / 8; grid-row: 1; min-height: 1.25em; align-self: center; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rclampprev { grid-column: 4; grid-row: 2; justify-content: flex-end; padding-left: 0; padding-right: 0; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_lamptext { grid-column: 5; grid-row: 2; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcdjprev { grid-column: 6; grid-row: 2; justify-content: flex-end; padding-left: 0; padding-right: 2px; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_ratebar { grid-column: 7; grid-row: 2; padding-left: 0; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcstar { grid-column: 8; grid-row: 1; justify-content: flex-start; padding-left: 0; padding-right: 0; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcbpcur { grid-column: 10; grid-row: 1; justify-content: flex-end; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcexprev { grid-column: 8; grid-row: 2; justify-content: flex-end; padding-right: 0; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcgrade { grid-column: 10; grid-row: 2; justify-content: flex-end; }
/* 9열 — 🔴 넘쳐도 잘리지 않는다. 원인은 overflow 가 아니라 **다음 셀(10열)의 배경**이라,
   배경을 투명으로 두고(행 배경이 뒤를 받친다) z-index 로 올린다. */
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcbpprev,
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcexcur {
  grid-column: 9;
  justify-content: flex-start;
  padding-left: 0;
  padding-right: 0;
  white-space: nowrap;
  overflow: visible;
  background: transparent;
  position: relative;
  z-index: 1;
}
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcbpprev { grid-row: 1; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_rcexcur { grid-row: 2; }
/* zasa★ 위치 — 🔴 Recent 는 화면폭과 무관하게 **레벨 칸 아래줄**이다(곡명 앞은 숨긴다).
   종전엔 PC=곡명 앞 / 모바일=레벨 아래로 갈렸는데, 2행으로 통일하면서 한쪽만 쓴다. */
.__pd_tr.__pd_recrow > .__pd_cell.__pd_title .__pd_zasa { display: none; }
/* ── 새 칸 글자 스타일 ── */
/* DJ prev(B→) — rate 바 안 글자와 같은 크기·굵기. 글자색은 인라인 style(등급색)이 준다. */
.__pd_cell.__pd_rcdjprev { font-size: 11.5px; font-weight: 700; }
/* ★ 수치 — 어두운 색. */
.__pd_cell.__pd_rcstar .__pd_recdiff { font-size: 10px; color: #6c757d; }
/* BP — 라벨·prev 는 회색, 현재값만 밝게(종전 __pd_rcbp 계약 그대로). */
.__pd_cell.__pd_rcbpprev { color: #6c757d; font-size: 11px; }
.__pd_cell.__pd_rcbpcur { color: #6c757d; font-size: 11px; }
.__pd_cell.__pd_rcbpcur .__pd_excur { color: #e9ecef; }
/* 🔴 prev 화살표는 값 글자의 색을 따라가지 않고 **회색 고정**이다. 그래서 별도 span 으로 뺐다. */
.__pd_cell .__pd_arrow { color: #6c757d; }
/* 🔴 종전 `.__pd_cell.__pd_rcbp` · `.__pd_rcdjchange` · `.__pd_rcexchange` 규칙은 10열 개편(2026-09-23)으로
   **그 셀들이 없어져 죽은 CSS 가 되었으므로 지웠다.** BP 는 `__pd_rcbpprev`/`__pd_rcbpcur`,
   DJ 는 `__pd_rcdjprev`, EX 는 `__pd_rcexprev`/`__pd_rcexcur` 로 갈렸다(각 규칙은 위 블록에 있다). */
/* 현재 램프 cell(`EX`) — 가운데 정렬, 작은 글자. 🔴 2026-09-25 부터 **현재 램프만** 낸다 — prev(`NP→`)는 바로 아래
   `__pd_rclampprev` 칸이다(한 칸에 두면 prev 유무로 현재 램프가 행마다 좌우로 흔들린다 — 사용자 지적). 종전 문안: 「램프 전이 cell(`AC→EC`) … prev → now 한 줄」.
   🔴 **Recent 행에만 건다** — `__pd_lamptext` 는 일반 곡행·추천곡 폴더도 쓰는 클래스다.
   ⚠️ 종전 셀렉터는 `__pd_rclampchange` 였는데 **v3 와 이름을 맞추려고 바꿨다**(2026-09-23) —
   오라클 테스트가 v1↔v3 클래스 시그니처를 대조하므로 이름이 갈리면 동등성이 깨진다. */
.__pd_recrow > .__pd_cell.__pd_lamptext {
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
/* 램프 prev cell(`NP→`) — col4. **오른쪽 붙임**이라 화살표가 col5 현재 램프에 붙어 보인다(DJ prev `B→` 와 같은 처방).
   ⚠️ 28px 이다 — `PFC→`·`EXH→` 가 넘치는지는 브라우저로 안 봤다(세션 90). 넘치면 다음 셀 배경이 덮는다(col9/10 의 z-index 처방 참고). */
.__pd_recrow > .__pd_cell.__pd_rclampprev {
  justify-content: flex-end;
  padding-left: 0;
  padding-right: 0;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
/* SCORE 의 prev → 표기 — 작은 회색. 기본 inline 흐름. */
.__pd_recprev {
  color: #6c757d;
  font-size: 10.5px;
  margin-right: 4px;
  font-weight: 400;
}
/* 🔴 종전 SCORE cell(`__pd_rcexchange`) 안의 absolute prev/grade 규칙 둘도 지웠다 —
   10열 개편으로 prev·현재값·등급차가 **각자 다른 열**을 갖게 되어 띄울 이유가 사라졌다. */
/* Recent row 클릭 가능 표시 */
.__pd_recrow { cursor: pointer; }
/* SCORE cell 옆 — 다음 DJ Level 컷까지 부족분 (예: "AAA-13"). 작고 회색, 괄호 X. */
.__pd_grade {
  color: #6c757d;
  font-size: 10.5px;
  font-weight: 400;
  margin-left: 6px;
  white-space: nowrap;
}
/* DBR난이도는 12.59 처럼 5글자라 곡명 앞 zasa 폭을 넓힘 (플레이데이터 4글자=20px 는 유지) */
.__dbr_folders .__pd_zasa { width: 30px; padding-right: 4px; }
/* Recent DBR 모드 — 곡명 앞 zasa 자리에 DBR 난이도(13.01) 표시라 폭 25px (일반 DP Recent 는 20px 유지) */
.__rc_dbr .__pd_zasa { width: 25px; }
.__dbr_folders .__pd_cell.__pd_title { padding-top: 6px; padding-bottom: 6px; }
/* DBR 곡명 cell — 곡명을 titlebox(시리즈줄+곡명 2줄 묶음)로 감싸므로 기본 block ellipsis 가 동작 안 함
   (inline-flex titlebox 가 폭 0 으로 접혀 곡명 전체가 '…' 로만 보임).
   flex 로 되돌리고 zasa 는 고정폭, titlebox 가 남는 폭을 차지(min-width:0)하게 해 곡명 ellipsis 정상화. */
.__dbr_folders .__pd_cell.__pd_title { display: flex; align-items: center; }
.__dbr_folders .__pd_cell.__pd_title .__pd_zasa { flex: 0 0 auto; }
.__dbr_folders .__dbr_titlebox { flex: 1 1 auto; min-width: 0; }
/* 노트수 자리에 패턴 해시태그 — 노트수 컬럼 폭을 넓혀 표시 (좌측정렬, 줄바꿈 없음) */
.__dbr_folders .__pd_tbody { grid-template-columns: 8px 34px 1fr 120px 44px 116px 84px; }
/* 추천곡 폴더(__os_recfolder) — 2줄 레이아웃. 1줄: level·곡명·해시태그(__os_rectags) / 2줄: ★난이도(__os_recdiff)·LAMP텍스트·rate·score.
   lamp·level 은 2줄 세로 span. (.__os_recfolder 안에서만 — 플레이데이터/DBR/통계 무관) */
/* 각 row 를 독립 2줄 grid 로 — __pd_tbody grid 에 cell 을 직접 두면 여러 row 가 같은 grid 행에 겹치므로(rate/DJ레벨 두 겹) __pd_tr 자체가 grid container. row 높이 ~40px(20+20).
   zasa★ 위치는 기본 동작 그대로: PC 는 곡명 앞(__pd_zasa), 모바일(@media 820px)은 game 레벨 밑(__pd_level_zasa). */
.__os_recfolder .__pd_tbody { display: flex; flex-direction: column; gap: 1px; }
.__os_recfolder .__pd_tr {
  display: grid;
  grid-template-columns: 8px 40px minmax(72px, 1fr) 56px 40px 130px 92px;
  grid-template-rows: 20px 20px;
  background: rgb(31, 31, 31);   /* cell 과 동일 색 — 트랙 사이로 다른 색이 비쳐 격자선처럼 보이는 것 방지 */
}
/* 추천/연습 폴더는 2줄 그리드(20px+20px=40px) — 일반행용 min-height:40px 를 0 으로 리셋(안 그러면 각 줄 40px=80px). */
.__os_recfolder .__pd_tr > .__pd_cell { padding-top: 0; padding-bottom: 0; min-height: 0; }
.__os_recfolder .__pd_tr:hover { background: #2d2d2d; }
/* hover 시 cell(#2d2d2d)과 배경 통일 — 트랙 사이 격자선 방지 */
.__os_recfolder .__pd_tr > .__pd_cell.__pd_lamp { grid-column: 1; grid-row: 1 / 3; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_level { grid-column: 2; grid-row: 1 / 3; flex-direction: column; justify-content: center; gap: 0; padding: 0 4px !important; font-size: 17px; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_title { grid-column: 3; grid-row: 1 / 3; display: flex; align-items: center; }
/* 추천곡 폴더 곡명 앞 zasa/rank — 20px 슬롯 가운데 정렬 (SP12 tier rank S/A/B 등). */
.__os_recfolder .__pd_zasa { text-align: center; }
.__os_recfolder .__pd_tr > .__pd_cell.__os_rectags { grid-column: 4 / 7; grid-row: 1; justify-content: flex-start; align-items: center; gap: 4px; font-size: 10px; color: #ff9bce; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.__os_recfolder .__pd_tr > .__pd_cell.__os_layoutcell { grid-column: 7; grid-row: 1; justify-content: flex-end; align-items: center; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_notes { grid-column: 4; grid-row: 2; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_lamptext { grid-column: 5; grid-row: 2; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_ratebar { grid-column: 6; grid-row: 2; }
.__os_recfolder .__pd_tr > .__pd_cell.__pd_num { grid-column: 7; grid-row: 2; }
/* 해시태그 칸 — 해시태그(좌, 넘치면 …) + 배치추천 뱃지(우측 정렬, 핑크 테두리) */
.__os_recfolder .__os_rectags_list { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.__os_recfolder .__os_reclayout { flex-shrink: 0; color: #ff6b9d; font-size: 9px; font-weight: 700; padding: 0 3px; border: 1px solid #ff6b9d; border-radius: 2px; line-height: 1.2; white-space: nowrap; }
/* 배치추천 뱃지 — PC 는 해시태그 옆 layoutcell(col7) 대신 notes 칸(__dbr_notestags)에 표시(2026-08-17, 연습곡추천과
   같은 위치로 통일). 모바일은 아래 @container 에서 곡행 바로 아래 빈 칸(row2 col3)으로 다시 옮긴다. */
.__os_recfolder .__os_layoutcell { display: none; }
.__os_recfolder .__pd_cell.__pd_notes .__os_notes_layout { display: inline-flex !important; align-items: center; }
.__os_weakfolder .__os_recgoal { font-size: 10px; color: #74c0fc; white-space: nowrap; }
/* BP(미스카운트) — bp 있으면 목표rate/★ 자리 대신 표시, 어두운색 */
/* ★난이도/BP — num 셀(EX스코어+등급차) 바로 위에 absolute 로 고정. 컨테이너 폭에 따라 notes 칸의
   grid-row 배정이 해시태그 줄과 겹쳐버리는 문제를 grid 배치와 무관하게 우회 — 항상 num 셀 좌측 상단에 뜬다.
   __pd_num 이 이미 position:relative(공용 규칙, PC 뱃지 기준)라 여기 별도 relative 불필요.
   EX스코어(__pd_num_val)/등급차(__pd_grade) 와 같은 43px·46px 고정폭으로 num 셀을 재정렬, ★/BP 도 같은 폭으로
   그 위에 맞춘다(2026-08-16, "클리어추천/연습곡추천 모두 __pd_cell __pd_num 구조 적용" 요청) — PC/모바일 공통.
   notes 칸의 배치추천 뱃지와는 같은 모서리를 공유해 겹칠 수 있어, 뱃지 쪽을 좌표로 밀어 회피(아래 __os_notes_layout). */
.__pd_numtop {
  position: absolute;
  bottom: 100%;
  left: 0;
  display: flex;
  white-space: nowrap;
  pointer-events: none;
}
.__pd_tr.__pd_recrow > .__pd_cell.__pd_num { padding-left: 0; padding-right: 0; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_num .__pd_num_val { flex: 0 0 43px; text-align: right; margin-right: 4px; }
.__pd_tr.__pd_recrow > .__pd_cell.__pd_num .__pd_grade { flex: 0 0 46px; text-align: left; margin-left: 0; }
.__pd_numtop .__os_recdiff { flex: 0 0 43px; text-align: right; margin-right: 4px; font-size: 10px; color: #6c757d; }
.__pd_numtop .__os_recbp { flex: 0 0 46px; text-align: left; }
/* PC — 목표 rate 는 해시태그 칸(__os_rectags) 우측 끝(margin-left:auto). notes 자리 사본은 PC 에서 숨김(모바일 @container 에서 다시 표시). */
.__os_recfolder .__os_recgoal_pc { margin-left: auto; flex-shrink: 0; }
.__os_recfolder .__pd_cell.__pd_notes .__os_recgoal { display: none; }
/* est축 사다리(추천곡 v3) 목표 램프 태그(#HC목표) — 한 폴더에 EC/HC/EXH 가 섞여 나오므로 행마다 표시한다.
   PC = 해시태그 칸(__os_rectags) **맨 앞** / 모바일 = 시리즈 태그 바로 다음(그 칸이 모바일엔 숨겨서).
   두 자리에 같은 값을 심어두고 한쪽씩 숨긴다 — __os_recgoal 과 같은 방식. */
.__os_estgoal { font-size: 10px; font-weight: 700; color: #adb5bd; white-space: nowrap; flex-shrink: 0; }
/* 램프별 색 — 추천곡 폴더 섹션 헤더(OS_REC_STAGES)와 같은 값으로 맞춘다. */
.__os_estgoal_ec { color: #52a447; }
/* EASY */
.__os_estgoal_hc { color: #dc3545; }
/* HARD */
.__os_estgoal_exh { color: #d4a017; }
/* EX-HARD */
.__os_estgoal_cl { color: #4d8fd6; }
/* SP CLEAR — FC(하늘)와 겹치지 않게 진한 파랑 */
.__os_estgoal_ex { color: #ffd43b; }
/* SP EX-HARD — 노랑 */
.__os_estgoal_fc { color: #5cb8ea; }
/* SP FULLCOMBO — 하늘 */
.__os_estgoal_mb { display: none; }
.__os_recfolder .__os_estgoal_pc { margin-right: 4px; }
/* 연습곡 — 예전엔 layoutcell(col 7) 이 비어있어 rectags 를 그 컬럼까지 확장했으나, 이제 col 7 은 numTop(★/BP)이
   row1 위로 떠 있는 자리라 태그가 길면 그 위로 침범해 겹친다(2026-08-16) — 확장을 접고 base(4/7)로 원복. */
/* 배치추천 뱃지 — 우측 정렬 (추천곡 데스크탑 해시태그칸 layoutcell + 모바일 notes칸, 연습곡 공통) */
.__os_recfolder .__os_reclayout { margin-left: auto; }
.__os_recfolder .__os_seriesline, .__dbr_folders .__os_seriesline { display: flex; align-items: center; gap: 5px; min-width: 0; }
.__os_recfolder .__os_seriesfeat, .__dbr_folders .__os_seriesfeat { display: none; }
.__dbr_notestags { justify-content: flex-start !important; gap: 5px; font-size: 10px; color: #ff9bce; overflow: visible; white-space: nowrap; padding-right: 2px !important; }
/* 시리즈 해시태그 (#CastHour 등) — 곡명 위 작은 줄 (titlebox 로 곡명과 한 묶음, zasa 와는 같은 줄) */
.__dbr_titlebox { display: inline-flex; flex-direction: column; justify-content: center; min-width: 0; line-height: 1.15; vertical-align: middle; }
.__dbr_series { font-size: 10px; color: #8a8f98; font-weight: 400; white-space: nowrap; line-height: 1.1; }
.__pd_rstar_axis_met { color: #dcaf45; }
.__pd_target_est_row::after { content: '추정'; color: #dcaf45; font-size: 10px; font-weight: 700; margin-left: 4px; }
.__pd_scoreoffset { font-size: 10px; font-weight: 700; }
.__pd_scoreoffset_pos { color: #dcaf45; }
.__pd_scoreoffset_neg { color: #7fa8c9; }
.__pd_scoreoffset_neutral { color: #8a8f98; }
.__pd_rstar_low { color: #8a8f98; font-size: 10px; font-weight: 700; }
/* E모드 폴더 곡명 옆 대표 패턴 피처 태그 — 시리즈명(#8a8f98)과 구분되게 살짝 푸른 회색 */
.__pd_feattag { color: #7fa8c9; }
.__dbr_songname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 행 클릭 = 점수 입력 */
.__dbr_folders .__pd_tr { cursor: pointer; }
[data-panel="recommendDBR"] .__pd_search_wrap { margin-right: auto; }
[data-panel="recommendDBR"] .__pd_searchbox { height: 30px; box-sizing: border-box; }
@media (max-width: 820px) {
  .__sp_song_content .__pd_tbody {
    grid-template-columns: 8px 30px 0 0 minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, 1.3fr);
  }
  .__sp_song_content .__pd_tr > .__pd_cell { overflow: hidden; padding-left: 4px; padding-right: 4px; }
  .__sp_song_content .__pd_tr > .__pd_cell.__pd_title { padding: 0; }
  .__sp_song_content .__pd_tr > .__pd_cell.__pd_notes { padding: 0; }
  .__sp_song_content .__pd_tr > .__pd_cell.__pd_num { padding-left: 4px; }
}
/* ===== v1 곡 모달 끝 ===== */

/* ===== v3 곡 페이지 셸 (song.js) — 2026-09-28 세션 112 사용자 요청 ===== */
/* 닫기(×) — 종전 「← 돌아가기」 줄 대신 곡 머리(시리즈·곡명) 줄 오른쪽 끝. DP 머리는 v1 이 닫기 자리로 비워 둔
   padding-right:34px(.__shelf_rank_head)가 있고, SP 제목 줄은 song-detail-sp.js openShell 이 오른쪽 40px 를 비운다. */
.__v3c_songpage { position: relative; }
.__v3c_songclose {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 1;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: transparent;
  color: #868e96;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.__v3c_songclose:hover { color: #f1f3f5; }
/* (세션 120 삭제) 모바일에서 곡 상세 난이도 행 곡명 칸을 0 트랙으로 접던 규칙 — 행이 추천곡 Origin 행(__v3c_ostbody 의 2줄 배치)으로 바뀌어
   grid-template-columns 를 컨테이너에 거는 방식이 더는 맞지 않는다. 좁은 화면 배치는 추천곡 CSS 를 그대로 따른다. */

/* 곡 상세 라이벌 5칸 패널 */
.__v3sd_rv_graph { color: var(--v3-text-main); padding-top: 16px; }
.__shelf_rank_body > .__v3c_rivalkindtabs { margin-bottom: var(--v3-space-3); }
.__v3sd_rv_plot { position: relative; height: 96px; margin-left: 28px; border-bottom: 1px solid var(--v3-border-default); }
.__v3sd_rv_guide { position: absolute; left: 0; right: 0; border-top: 1px solid var(--v3-border-subtle); color: var(--v3-text-muted); font-size: 10px; }
.__v3sd_rv_guide span { position: absolute; right: 100%; margin-right: 4px; transform: translateY(-50%); }
.__v3sd_rv_bars { position: absolute; inset: 0; display: flex; align-items: flex-end; gap: var(--v3-space-3); }
.__v3sd_rv_col { position: relative; flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; justify-content: center; }
.__v3sd_rv_bar { width: 70%; background: var(--v3-state-rival); border-radius: var(--v3-radius-sm) var(--v3-radius-sm) 0 0; }
.__v3sd_rv_col.__v3sd_rv_me .__v3sd_rv_bar { background: var(--v3-state-self); }
.__v3sd_rv_rate { position: absolute; left: 0; right: 0; text-align: center; color: var(--v3-text-secondary); font-size: 11px; }
.__v3sd_rv_labels { display: flex; gap: var(--v3-space-3); margin-left: 28px; }
.__v3sd_rv_cell { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: var(--v3-space-2); }
.__v3sd_rv_cell.__v3sd_rv_me .__v3sd_rv_name { font-weight: 700; }
.__v3sd_rv_name, .__v3sd_rv_score { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; }
.__v3sd_rv_score { color: var(--v3-text-muted); }
.__v3sd_arrbest:not(:empty) { color: var(--v3-accent); border: 1px solid currentColor; padding: 2px 5px; border-radius: var(--v3-radius-sm); font-size: 11px; line-height: 1.3; }
.__v3sd_rv_empty { margin: var(--v3-space-5) 0 0; color: var(--v3-text-muted); text-align: center; }
/* 곡명 아래 태그(#BPM · 대표 피처) — 비면 칸 높이 0. */
.__v3sd_tagrow { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--v3-space-2); margin-top: var(--v3-space-2); margin-bottom: var(--v3-space-5); }
.__v3sd_tags:not(:empty) { display: flex; flex-wrap: wrap; gap: var(--v3-space-2); }
.__v3sd_arrange:not(:empty) { display: flex; align-items: center; gap: var(--v3-space-2); margin-left: auto; font-size: 12px; white-space: nowrap; }
.__v3sd_arrwrap { position: relative; display: inline-flex; align-items: center; gap: var(--v3-space-2); }
.__v3sd_arrbadge { color: var(--v3-state-warning-strong); border: 1px solid currentColor; background: transparent; padding: 2px 5px; border-radius: var(--v3-radius-sm); font: inherit; font-size: 11px; line-height: 1.3; cursor: pointer; }
.__v3sd_arrstat { color: var(--v3-text-muted); font-size: 10px; }
.__v3sd_arrpop { position: absolute; right: 0; top: calc(100% + var(--v3-space-2)); z-index: 20; display: grid; grid-template-columns: repeat(3, max-content); gap: var(--v3-space-2); padding: var(--v3-space-3); background: var(--v3-bg-panel); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); box-shadow: 0 6px 18px #0006; }
.__v3sd_arrcol { display: flex; flex-direction: column; gap: var(--v3-space-2); }
.__v3sd_arrhead { text-align: center; color: var(--v3-text-muted); font-size: 11px; line-height: 16px; height: 16px; }  /* 빈 머리(FLIP 열)와 한글 머리의 글꼴별 기본 줄 높이 차로 첫 버튼 행이 어긋나지 않게 고정 */
.__v3sd_arrcol button { background: var(--v3-bg-input); color: var(--v3-text-main); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-sm); padding: 2px 5px; cursor: pointer; }
.__v3sd_arrcol button.__v3sd_arrselected { color: var(--v3-state-warning-strong); border-color: currentColor; font-weight: 700; }
.__v3sd_tag { padding: 2px 5px; border-radius: var(--v3-radius-sm); background: var(--v3-bg-surface); color: var(--v3-text-tag); font-size: 11px; line-height: 1.3; }
/* 곡 피처 레이더 — v1 공용 radarSvg(130px)는 그대로 두고 v3 곡 상세에서만 100px 로(viewBox 라 비율 유지). 막대 그룹 둘과 360px 폭 한 줄. */
.__v3sd_featradar { flex:0 0 auto; line-height:0; }
.__v3sd_featradar svg { width:100px; height:100px; }
.__v3sd_dist { color:#dee2e6; font-size:11px; padding:10px 0; }
.__v3sd_viewbar { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center; gap:var(--v3-space-4); font-size:13px; margin-bottom:10px; }
.__v3sd_viewgrp { display:inline-flex; align-items:center; gap:var(--v3-space-4); }
.__v3sd_viewgrp:last-child { margin-left:auto; }
.__v3sd_viewsep { color:var(--v3-text-dim); }
.__v3sd_viewbtn { padding:0; border:0; background:none; font:inherit; color:var(--v3-text-dim); cursor:pointer; }
.__v3sd_viewbtn[aria-pressed="true"] { font-weight:700; color:var(--v3-text-strong); }
.__v3sd_distchart { position:relative; padding-right:25px; }
.__v3sd_distguides { position:absolute; inset:0 0 39px 0; height:120px; pointer-events:none; }
.__v3sd_distguide { position:absolute; left:0; right:0; border-top:1px dashed #495057; }
.__v3sd_distguide span { position:absolute; right:0; top:-7px; color:#adb5bd; font-size:9px; }
.__v3sd_distcells { position:relative; display:flex; gap:2px; width:100%; }
.__v3sd_distcell { flex:1; min-width:0; text-align:center; color:#dee2e6; }
.__v3sd_distplot { height:120px; position:relative; display:flex; justify-content:center; align-items:flex-end; }
.__v3sd_distbar { width:65%; background:#4dabf7; border-radius:3px 3px 0 0; }
.__v3sd_distnumber { position:absolute; bottom:100%; transform:translateY(13px); font-size:9px; white-space:nowrap; }
.__v3sd_distnumber_out { transform:translateY(-2px); }
.__v3sd_distname, .__v3sd_distdetail, .__v3sd_distcaption { font-size:9px; white-space:normal; overflow-wrap:anywhere; }
.__v3sd_distcaption { color:var(--v3-text-muted); text-align:left; margin-top:2px; }
.__v3sd_distme { color:#74c0fc; font-weight:700; }
.__v3sd_distme .__v3sd_distbar { background:#ffd43b; }

/* Ranking(6차) — v1 유저 목록 표·필터(styles.css:779-821,902-955) 이식. 색은 v3 토큰, sticky 는 뺐다(v3 는 sticky-top.js 가 top 을 계산 — top:0 이면 헤더 밑으로 들어간다). */
.__v3r_page .__users_layout { display:flex; gap:20px; align-items:flex-start; }
.__v3r_page .__users_list_col { flex:0 0 auto; min-width:0; max-width:100%; }
.__v3r_page .__rating_filters { display:grid; grid-template-columns:minmax(0, 1fr) auto auto; align-items:center; gap:8px; margin:0 0 12px; padding:10px 0; background:var(--v3-bg-page); }
.__v3r_page #__v3r_search { width:100%; min-width:0; box-sizing:border-box; padding:8px; border:1px solid var(--v3-border-default); border-radius:3px; background:var(--v3-bg-search); color:var(--v3-text-main); font:inherit; }
.__v3r_page #__v3r_search:focus { outline:1px solid var(--v3-accent); }
.__v3r_page .__v3r_types { display:flex; gap:8px; white-space:nowrap; }
.__v3r_page .__f_count { justify-self:end; white-space:nowrap; }
.__v3r_page[data-ranking-layout="desktop"] .__rating_filters { grid-template-columns:var(--ranking-list-width, 50%) 1fr auto; }
.__v3r_page .__rating_filters label { display:inline-flex; align-items:center; gap:3px; cursor:pointer; }
.__v3r_page .__users_table { width:auto; max-width:100%; border-collapse:separate; border-spacing:0; font-size:13px; }
.__v3r_page .__users_table_wrap { overflow-x:auto; max-width:100%; }
.__v3r_page .__users_table thead th { background:var(--v3-bg-page); white-space:nowrap; }
.__v3r_page .__users_table th, .__v3r_page .__users_table td { border:0; border-bottom:1px solid var(--v3-border-default); padding:5px; }
.__v3r_page .__users_table tbody tr[data-id] { cursor:pointer; transition:background .12s; }
.__v3r_page .__users_table tbody tr[data-id]:hover { background:var(--v3-bg-surface-hover); }
.__v3r_page .__users_table tbody tr.__v3r_detail { cursor:default; transition:none; }
.__v3r_page .__users_table tbody tr.__v3r_detail:hover { background:transparent; }
.__v3r_page .__users_table tbody tr.__v3r_detail[hidden] { display:none !important; }
.__v3r_page .__users_table tbody tr.__v3r_detail > td { padding:10px 12px 14px; text-align:left; white-space:normal; vertical-align:top; }
.__v3r_page .__v3r_position { white-space:nowrap; }
@media (max-width:480px) { .__v3r_page .__users_table { width:100%; } }

.__v3r_page .__users_list_col { flex:1 1 0; }
.__v3r_page .__users_table { min-width:max-content; }
.__v3r_page .__users_table tbody tr[data-id] > td:first-child { vertical-align:middle; padding-bottom:5px; }
.__v3r_page .__users_table tbody tr[data-id] > td.__v3r_player { vertical-align:middle; }
.__v3r_page .__v3r_identity { display:flex; align-items:flex-end; gap:6px; height:26px; line-height:1; }
.__v3r_page .__v3r_summary { display:block; height:22px; line-height:22px; font-size:13px; }
.__v3r_page .__v3r_summary .__v3r_rstar { font-size:12px; color:var(--v3-text-muted); }
.__v3r_page .__v3r_player { min-width:0; }
.__v3r_page .__v3r_identity { overflow-wrap:anywhere; }
.__v3r_page .__v3r_identity [data-player-id], .__v3r_page .__v3r_identity .__v3r_id { white-space:nowrap; }
.__v3r_page .__v3r_identity [data-player-id], .__v3r_page .__v3r_identity .__v3r_id { font-family:var(--v3-font-mono, monospace); font-size:11px; color:var(--v3-text-muted); }
.__v3r_page .__v3r_position .__v3r_rank { display:flex; justify-content:flex-end; align-items:flex-end; height:26px; font-size:22px; line-height:1; font-weight:700; color:#868e96; }
.__v3r_page .__v3r_position .__v3r_sigma { display:block; height:22px; line-height:22px; font-size:13px; color:#ff9bce; }
.__v3r_page .__v3r_detailtoggle, .__v3r_page .__v3r_userpage { color:var(--v3-text-main); font-family:inherit; cursor:pointer; }
.__v3r_page .__v3r_detailtoggle { appearance:none; border:0; background:transparent; padding:0; text-align:left; font-size:18px; font-weight:700; }
.__v3r_page .__v3r_detailtoggle:focus-visible, .__v3r_page .__v3r_userpage:focus-visible { outline:2px solid var(--v3-accent); outline-offset:2px; }
.__v3r_page .__v3r_detailbody { display:flex; flex-wrap:wrap; align-items:flex-start; gap:16px; width:0; min-width:100%; box-sizing:border-box; --detail-gap:16px; }
.__v3r_page .__v3r_detailradar { flex:0 0 calc(50% - var(--detail-gap)/2); width:auto; min-width:0; max-width:none; }
.__v3r_page .__v3r_detailradar svg { display:block; width:100%; height:auto; overflow:visible; }
.__v3r_page .__v3r_detailradar text { font-size:11px; }
.__v3r_page .__v3r_detailaxes { order:-1; flex:0 0 calc(50% - var(--detail-gap)/2); width:calc(50% - var(--detail-gap)/2); min-width:0; max-width:calc(50% - var(--detail-gap)/2); overflow-x:auto; }
.__v3r_page .__v3r_detailaxes > table { width:100%; min-width:0; border-collapse:collapse; table-layout:fixed; }
.__v3r_page .__v3r_detailaxes th, .__v3r_page .__v3r_detailaxes td { padding:5px 7px; text-align:left; vertical-align:top; white-space:normal; }
.__v3r_page .__v3r_detailaxes th { color:var(--v3-text-muted); font-size:11px; }
.__v3r_page .__v3r_detailaxes td { color:var(--v3-text-main); font-size:13px; }
.__v3r_page .__v3r_detailcomposite { width:100%; min-width:0; margin-top:16px; border-collapse:collapse; }
.__v3r_page .__v3r_detailcomposite th, .__v3r_page .__v3r_detailcomposite td { padding:5px 7px; text-align:left; font-size:13px; overflow-wrap:anywhere; }
.__v3r_page .__v3r_detailbody > .__v3r_userpage { order:2; flex:0 0 100%; width:100%; box-sizing:border-box; appearance:none; background:transparent; border:1px solid #495057; border-radius:3px; padding:2px 8px; font-size:13px; }
.__v3r_page .__v3r_userpage:hover { border-color:#ff6b9d; }
.__v3r_page .__v3r_compare { display:block; margin:8px auto; padding:4px 8px; background:transparent; color:var(--v3-text-main); border:1px solid #868e96; border-radius:3px; font:inherit; cursor:pointer; }
.__v3r_page .__v3r_compare[aria-pressed="true"] { border-color:#fff; box-shadow:inset 0 0 0 1px #fff; }
.__v3r_page .__v3r_compare:focus-visible { outline:2px solid var(--v3-accent); outline-offset:2px; }
.__v3r_page .__v3r_radar { width:56px; min-width:56px; }
.__v3r_page .__v3r_radar svg { display:block; width:56px; height:56px; }
.__v3r_page .__users_layout { flex-wrap:wrap; }
.__v3r_page .__v3r_detailhost { display:none; }
.__v3r_page .__v3r_detailpanel[hidden] { display:none; }
.__v3r_page .__users_table tbody tr.__v3r_selected { background:var(--v3-bg-surface-hover); }
.__v3r_page .__users_table tbody tr.__v3r_selfcopy > td { border-bottom:3px solid var(--v3-border-default); }
.__v3r_page[data-ranking-layout="desktop"] .__users_layout { flex-wrap:nowrap; gap:0; }
.__v3r_page[data-ranking-layout="desktop"] .__users_list_col { flex:0 0 auto; width:max-content; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailhost { display:block; flex:0 0 auto; min-width:0; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailpanel { width:100%; padding:12px; gap:18px; border-top:1px solid var(--v3-border-default); border-bottom:1px solid var(--v3-border-default); }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailaxes { flex:1 1 0; width:auto; max-width:none; border-collapse:collapse; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailradar { flex:1 1 0; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailradar svg { width:150px; max-width:100%; margin:0 auto; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailaxes th, .__v3r_page[data-ranking-layout="desktop"] .__v3r_detailaxes td { padding:8px 7px; border-bottom:1px solid var(--v3-border-default); white-space:nowrap; }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailcomposite th, .__v3r_page[data-ranking-layout="desktop"] .__v3r_detailcomposite td { border-bottom:1px solid var(--v3-border-default); }
.__v3r_page[data-ranking-layout="desktop"] .__v3r_detailpanel > .__v3r_userpage { padding:4px 8px; }
.__v3r_page[data-ranking-layout="desktop"] .__users_table tbody tr.__v3r_selected { background:var(--v3-bg-surface-hover); }
@media (max-width:480px) {
  .__v3r_page .__users_layout { gap:12px; }
  .__v3r_page .__users_list_col { flex-basis:100%; }
}

/* Administrator shelf editor uses the shared modal and theme tokens. */
.shelf-song:has(.__shelf_override_badge) { position: relative; }
.__shelf_override_badge {
  position: absolute; top: 1px; left: 2px; z-index: 3;
  padding: 0 4px; border-radius: 999px; pointer-events: none;
  font-size: 8px; line-height: 1.4; font-weight: 600;
  color: var(--v3-accent); background: var(--v3-bg-panel);
  border: 1px solid var(--v3-border-default);
}
.__v3c_shelf_override { display: flex; flex-wrap: wrap; align-items: center; gap: var(--v3-space-3); color: var(--v3-text-main); }
.__v3c_shelf_override > h2, .__v3c_shelf_override > p, .__v3c_shelf_override > section { flex-basis: 100%; margin: 0; }
.__v3c_shelf_override > h2 { font-size: 18px; color: var(--v3-text-heading); }
.__v3c_shelf_override_summary, .__v3c_shelf_override_values { padding: var(--v3-space-4); border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); background: var(--v3-bg-panel); font-size: 13px; line-height: 1.7; overflow-wrap: anywhere; }
.__v3c_shelf_override_values > div + div { border-top: 1px solid var(--v3-border-subtle); }
.__v3c_shelf_override label { display: inline-flex; align-items: center; gap: var(--v3-space-2); font-size: 13px; }
.__v3c_shelf_override input[type='checkbox'] { accent-color: var(--v3-accent); }
.__v3c_shelf_override_bucket, .__v3c_shelf_override_password { order: 1; min-width: 0; max-width: 100%; padding: 8px 10px; border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); background: var(--v3-bg-input); color: var(--v3-text-main); font: inherit; }
.__v3c_shelf_override_password { flex: 1 1 160px; }
.__v3c_shelf_override_message { order: 2; color: var(--v3-state-warning); font-size: 13px; }
.__v3c_shelf_override_save, .__v3c_shelf_override_clear { order: 3; padding: 8px 16px; border: 1px solid var(--v3-border-default); border-radius: var(--v3-radius-md); background: var(--v3-bg-surface); color: var(--v3-text-main); font: inherit; cursor: pointer; }
.__v3c_shelf_override_save { background: var(--v3-accent); color: var(--v3-bg-page); border-color: var(--v3-accent); font-weight: 600; }
.__v3c_shelf_override :disabled { opacity: .55; cursor: default; }
.__v3c_shelf_override :is(button, input, select):focus-visible, .__v3g_admin_tier_toggle:focus-visible { outline: 2px solid var(--v3-accent); outline-offset: 2px; }
