/* [CORE & FONTS] */
:root {
    /* `only` is the opt-out keyword: plain `dark` merely says "dark is supported", which still
       lets Chrome's Auto Dark Theme / Samsung Internet re-tint author colours. `only dark` tells
       the UA the page is already dark and must not be auto-adjusted — this is what keeps the
       signature red (#f2112c) from being washed out in mobile dark mode. */
    color-scheme: only dark;
    --font-display: 'Roboto', sans-serif;
    --font-body: 'Poppins', sans-serif;

    /* ── 중성색 계단 ───────────────────────────────────────────────────────────
       2026-09-22 신설. 그전에는 네 단(#151515 / #0D0D0D / #7A7A7A / #EAEAEA)뿐이라
       카드 표면·테두리·호버·비활성 상태를 구분할 계단이 없었고, 그 일을 갈래색이
       대신하면서 화면이 부산해졌습니다. 디자이너 지적("왜 굳이 대비색 셋이냐")의
       실체가 이것입니다.

       ⚠️ --n-2 는 #151515, --n-9 는 #EAEAEA, --n-1 은 #0D0D0D, --n-6 은 #7A7A7A 로
          기존 값과 **정확히 같습니다.** 새로 추가된 것은 0/3/4/5/7/8 여섯 단입니다.
       ⚠️ 괄호 안은 #151515 위에서 글자로 썼을 때의 APCA Lc 입니다.
          60 을 못 넘으면 글자로 쓰면 안 됩니다. */
    --n-0: #0A0A0A;   /* 가장 깊은 면 - 모달 뒤, 푸터 */
    --n-1: #0D0D0D;   /* 카드 면 */
    --n-2: #151515;   /* ★ 배경. 시그니처 */
    --n-3: #1F1F1F;   /* 떠 있는 면 - 호버, 드롭다운 */
    --n-4: #2C2C2C;   /* 구분선 · 기본 테두리 */
    --n-5: #4A4A4A;   /* 강한 테두리 · 비활성 (Lc 18) */
    --n-6: #7A7A7A;   /* 보조 글자 (Lc 33) */
    --n-7: #AEAEAE;   /* 부제 · UI 라벨 (Lc 59) */
    --n-8: #D4D4D4;   /* 강조 글자 (Lc 80) */
    --n-9: #EAEAEA;   /* 본문 (Lc 93) */

    --bg-main: #151515;
    --bg-card: #0D0D0D;

    --color-text: #EAEAEA;
    --color-text-muted: #7A7A7A;

    /* ── 갈래색 ────────────────────────────────────────────────────────────────
       2026-09-22 단색 체계로 전환. 갈래를 색으로 나누지 않습니다.

       ⚠️ 왜 바꿨는가 - 취향이 아니라 **가독성** 입니다.
          #151515 위에서 APCA 를 재면 MUSIC 24.4 / PARTY 60.0 / LIVE 32.9 였습니다.
          Lc 60 이 제목·본문의 하한인데 셋 중 둘이 못 넘었고, 특히 레드(32.9)는
          일부러 흐리게 처리한 보조 텍스트(--color-text-muted, 32.9)와 같은 대비로
          글자에 189번 쓰이고 있었습니다.
          거기에 명도가 0.54/0.77/0.61 로 제각각이라 앰버만 혼자 튀었고,
          PARTY-LIVE 는 hue 가 45도밖에 안 떨어져 서로 싸웠으며,
          셋 중 둘이 Tailwind 기본값(violet-600, amber-500)이었습니다.

       설계 규칙 (OKLCH 기준):
          · 명도 L 을 0.80 으로 **통일** -> 세 색의 가독성과 시각적 무게가 같아집니다.
            어두운 배경에서 APCA 는 사실상 L 이 결정합니다.
          · 채도 C 를 0.10 으로 **통일**(편차 0.002) -> '네온' 느낌이 사라집니다.
            세 hue 의 sRGB 채도 한계 중 최솟값에 맞춘 값입니다.
          · hue 를 시그니처 레드(25도)에서 정확히 **120도씩** 띄웠습니다.

       ⚠️ 이 값들을 손으로 고칠 때는 **L 을 0.80 아래로 내리지 마십시오.**
          L 0.75 면 Lc 55 로 떨어져 제목으로 쓸 수 없게 됩니다. 색을 더 진하게
          하고 싶으면 --fill-* 쪽을 쓰십시오. 그쪽은 면과 발광 전용입니다. */
    --neon-music: var(--n-8);
    /* Azure — 레이블/아카이브. OKLCH L 0.800 C 0.100 H 264.8, APCA 66.6 */
    --neon-party: var(--n-8);
    /* Jade — 이벤트. OKLCH L 0.800 C 0.099 H 145.0, APCA 68.7 */
    --neon-live: #f2112c;
    /* Coral — 주간 방송. 시그니처 레드와 **같은 hue(25도)** 의 밝은 변주입니다.
       레드 원본은 아래 --neon-pif-red / --fill-live 에 그대로 살아 있습니다. */

    --neon-pif-red: #f2112c;
    /* ⚠️ 시그니처 레드. 절대 바꾸지 마십시오. 발광/그라디언트 전용이라
       글자 가독성 문제가 없습니다(background-image 로만 쓰입니다). */

    /* 면과 발광 전용. 시그니처 레드의 명도(L 0.609)에 맞춰 세 색을 나란히 둔 것입니다.
       ⚠️ 이 값들을 **글자색으로 쓰지 마십시오.** L 이 낮아 Lc 33~41 입니다. */
    --fill-music: var(--n-8);
    --fill-party: var(--n-8);
    --fill-live: #f2112c;

    --shadow-music: rgba(212, 212, 212, 0.35);
    --shadow-party: rgba(212, 212, 212, 0.35);
    --shadow-live: rgba(242, 17, 44, 0.4);
    --shadow-pif-red: rgba(242, 17, 44, 0.6);
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

html {
    scroll-behavior: smooth;
    color-scheme: only dark;
}

body {
    background-color: var(--bg-main);
    color: var(--color-text);
    font-family: var(--font-body);
    line-height: 1.6;
    overflow: hidden;
    /* `forced-color-adjust: none` was removed here on 2026-07-27, for the same three reasons the
       duplicate at the end of this file was removed: it is unscoped so it reaches Chrome/Safari/iOS
       which never had a problem; it does nothing for Samsung's forced-dark (a different mechanism —
       the on-device measurement found background-clip:text is the actual fix); and it switches off
       Windows High Contrast Mode site-wide for the users who deliberately enabled it. */
}

/* Hardware GPU Acceleration & Buttery Smooth 60/120fps Layer Promotion */
.gateway-card,
.content-section,
.event-modal,
.item-card,
.lightbox-nav-btn,
.lightbox-main-media,
.close-btn {
    transform: translateZ(0);
    backface-visibility: hidden;
}

a {
    color: var(--color-text);
    text-decoration: none;
}

#noise-overlay,
#vignette-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 1000;
    transform: translateZ(0); /* Force GPU compositing layer to keep animations buttery smooth */
    will-change: transform, opacity;
}

#noise-overlay {
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='0.015'/%3E%3C/svg%3E");
}

#vignette-overlay {
    background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.7) 100%);
}

/* [HEADER] */
#main-header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24px 40px;
    z-index: 999;
    /* Float above all content sections, just below modals */
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.2) 50%, rgba(0, 0, 0, 0) 100%);
    border: none;
    pointer-events: auto;
    transition: all 0.3s ease;
}

/* Truly optically center the nav between logo and social icons */
#main-header nav {
    position: absolute;
    left: calc(50% - 36px);
    transform: translateX(-50%);
    pointer-events: none;
    /* let clicks pass through gap */
}

#main-header nav a {
    pointer-events: all;
}

.logo img {
    height: 26px;
    opacity: 0.7;
    transition: opacity 0.3s ease;
}

.logo:hover img {
    opacity: 1;
}

.social-links-header {
    display: flex;
    gap: 22px;
    font-size: 1.1rem;
}

.social-links-header a {
    color: var(--color-text-muted);
    transition: all 0.3s ease;
}

.social-links-header a:hover {
    color: var(--color-text);
    transform: scale(1.1);
}

/* [MAIN LAYOUT] */
#main-content {
    width: 100%;
    height: 100vh;
    position: relative;
    overflow: hidden;
}

/* [GATEWAYS] */
#gateways {
    display: flex;
    width: 100%;
    height: 100%;
    transition: opacity 0.5s ease, transform 0.5s ease;
    opacity: 0;
    transform: scale(0.98);
    overflow: hidden;
    isolation: isolate;
    contain: layout paint;
}

#gateways.visible {
    opacity: 1;
    transform: scale(1);
}

.gateway-card {
    flex: 1;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    contain: layout paint;
    box-sizing: border-box;
    transition: flex 0.7s cubic-bezier(0.25, 1, 0.5, 1), transform 0.5s cubic-bezier(0.25, 1, 0.5, 1);
    transform-style: preserve-3d;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    will-change: transform, flex;
}

.gateway-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    background-size: cover;
    background-position: center;
    filter: grayscale(100%) brightness(0.52) contrast(1.08);
    transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1), filter 0.5s cubic-bezier(0.25, 1, 0.5, 1);
    z-index: 0;
    overflow: hidden;
    contain: paint layout;
    will-change: transform, filter;
}

.gateway-bg video,
.global-bg video,
.content-section > video {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: translate(-50%, -50%);
    z-index: 0;
    filter: grayscale(100%) brightness(0.55) contrast(1.08);
    transition: filter 0.5s cubic-bezier(0.25, 1, 0.5, 1);
    will-change: filter;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

#gateway-music .gateway-bg {
    background-image: url('../images/backgrounds/music_poster.webp');
}

#gateway-party .gateway-bg {
    background-image: url('../images/backgrounds/party_poster.webp');
}

#gateway-live .gateway-bg {
    background-image: url('../images/backgrounds/live_poster.webp');
}

/* Prevent double-layer ghosting/bleed-through when video is loaded inside gateway-bg */
.gateway-bg:has(video),
.gateway-bg.has-video {
    background-image: none !important;
    background-color: #0d0d0d;
}

.gateway-bg video {
    background-color: #0d0d0d;
    backface-visibility: hidden;
}

/* Soft Feather Vignette Mask & Ambient Depth Layer */
.gateway-card::after {
    content: '';
    position: absolute;
    inset: 0;
    box-sizing: border-box;
    background: radial-gradient(ellipse at center, transparent 35%, rgba(12, 12, 18, 0.45) 75%, rgba(5, 5, 10, 0.88) 100%);
    pointer-events: none;
    z-index: 1;
    transition: background 0.5s ease, opacity 0.5s ease;
}

.gateway-logo {
    height: 100px;
    margin-bottom: 20px;
    filter: drop-shadow(0 0 15px rgba(0, 0, 0, 0.8));
    transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
    transform: translate3d(0, 0, 50px);
    will-change: transform;
    z-index: 2;
}

.gateway-title {
    font-family: var(--font-display);
    font-size: 3rem;
    font-weight: 900;
    letter-spacing: 5px;
    transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
    transform: translate3d(0, 0, 30px);
    text-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
    will-change: transform;
    z-index: 2;
}

.gateway-card::before {
    /* 3D Glassmorphism Neon Border Sealed Fit */
    content: '';
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(255, 255, 255, 0.08);
    transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1);
    z-index: 3;
    pointer-events: none;
}

/* --- Hover & Active States (Grayscale to Vibrant Color & 3D Ambient Glow) --- */
.gateway-card:hover .gateway-bg,
.gateway-card:hover .gateway-bg video,
.gateway-card.touch-active .gateway-bg,
.gateway-card.touch-active .gateway-bg video {
    filter: grayscale(0%) brightness(0.95) contrast(1.12); /* Smooth transition to vivid full color without 0.1px layer separation */
}

.gateway-card:hover::after {
    background: radial-gradient(ellipse at center, transparent 48%, rgba(12, 12, 18, 0.22) 80%, rgba(5, 5, 10, 0.65) 100%);
}

.gateway-card:hover .gateway-logo {
    transform: translate3d(0, 0, 70px) scale(1.03);
}

.gateway-card:hover .gateway-title {
    transform: translate3d(0, 0, 50px) scale(1.03);
}

/* 게이트웨이 카드 호버 발광.
   ⚠️ 2026-09-22 단색 체계 전환. 세 카드는 동등한 관계라 하나만 빨갛게 빛나면
      자의적으로 보입니다. 셋 다 중성 발광으로 두고, 빨강은 카드 위 로고와
      활성 상태에만 남깁니다. 갈래를 색으로 나누지 않는 것이 이 체계의 핵심입니다. */
#gateway-music:hover::before,
#gateway-music.touch-active::before {
    border-color: rgba(212, 212, 212, 0.7);
    box-shadow: 0 0 25px rgba(212, 212, 212, 0.4), inset 0 0 20px rgba(212, 212, 212, 0.25);
}

#gateway-party:hover::before,
#gateway-party.touch-active::before {
    border-color: rgba(212, 212, 212, 0.7);
    box-shadow: 0 0 25px rgba(212, 212, 212, 0.4), inset 0 0 20px rgba(212, 212, 212, 0.25);
}

#gateway-live:hover::before,
#gateway-live.touch-active::before {
    border-color: rgba(212, 212, 212, 0.7);
    box-shadow: 0 0 25px rgba(212, 212, 212, 0.4), inset 0 0 20px rgba(212, 212, 212, 0.25);
}


/* [CONTENT SECTIONS] */
.content-section {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    max-width: 100vw;
    height: 100%;
    background-color: rgba(21, 21, 21, 0.92); /* Matches custom #151515 color scheme */
    backdrop-filter: blur(12px); /* Reduced from 20px for high performance rendering */
    -webkit-backdrop-filter: blur(12px);
    opacity: 0;
    visibility: hidden;
    transform: translateY(15px);
    transition: opacity 0.5s ease, visibility 0s 0.5s, transform 0.5s ease;
    z-index: 200;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 100px 60px 260px !important;
    border: 1px solid rgba(255, 255, 255, 0.03);
    box-sizing: border-box;
}

/* Ambient Radial Glow Backdrops for each section */
#music::before,
#party::before,
#live::before {
    content: '';
    position: absolute;
    width: 600px;
    height: 600px;
    pointer-events: none;
    z-index: -1;
    filter: blur(120px);
    opacity: 0.45;
    transition: all 0.5s ease;
}

#music::before {
    top: -10%;
    right: -5%;
    background: radial-gradient(circle, var(--neon-music) 0%, rgba(0, 0, 0, 0) 70%);
}

#party::before {
    top: -10%;
    right: -5%;
    background: radial-gradient(circle, var(--neon-party) 0%, rgba(0, 0, 0, 0) 70%);
}

#live::before {
    top: -10%;
    right: -5%;
    background: radial-gradient(circle, var(--neon-live) 0%, rgba(0, 0, 0, 0) 70%);
}

.content-section.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
}

.content-section h2 {
    font-family: var(--font-display);
    font-size: 3rem;
    font-weight: 900;
    text-align: center;
    margin-bottom: 40px;
    text-transform: uppercase;
    letter-spacing: 2px;
}

#music h2 {
    color: var(--neon-music);
    text-shadow: 0 0 15px var(--shadow-music);
}

#party h2 {
    color: var(--neon-party);
    text-shadow: 0 0 15px var(--shadow-party);
}

#live h2 {
    color: var(--neon-live);
    text-shadow: 0 0 15px var(--shadow-live);
}


/* ====== GLOBAL FLOATING FIXED BACK BUTTON (100% SCREEN VIEWPORT FIXED) ====== */
.content-section > .close-btn {
    display: none !important;
}

#global-section-close-btn {
    position: fixed;
    top: 105px;
    right: 40px;
    z-index: 99999;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-10px);
    transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1), transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), background 0.25s ease, border-color 0.25s ease;
    background: rgba(12, 12, 16, 0.92);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.25);
    color: #fff;
    font-family: var(--font-body);
    font-size: 0.8rem;
    font-weight: 800;
    padding: 10px 22px;
    border-radius: 30px;
    cursor: pointer;
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.7), 0 0 12px rgba(255, 255, 255, 0.08);
}

/* Base Visible State: Visible when idle/still */
#global-section-close-btn.visible {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0);
}

/* While actively scrolling: Fade out smoothly */
#global-section-close-btn.visible.is-scrolling {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    transform: translateY(-6px) !important;
}

#global-section-close-btn.menu-open,
#global-section-close-btn.modal-open {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    transform: translateY(-10px) !important;
}

#global-section-close-btn:hover {
    transform: scale(1.06);
    background: #fff;
    color: #151515;
}

#global-section-close-btn.theme-music:hover {
    background: var(--neon-music);
    color: #151515;
    border-color: var(--neon-music);
    box-shadow: 0 0 20px var(--shadow-music);
}

#global-section-close-btn.theme-party:hover {
    background: var(--neon-party);
    color: #151515;
    border-color: var(--neon-party);
    box-shadow: 0 0 20px var(--shadow-party);
}

#global-section-close-btn.theme-live:hover {
    background: var(--neon-live);
    color: #151515;
    border-color: var(--neon-live);
    box-shadow: 0 0 20px var(--shadow-live);
}

.grid-container {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 30px;
}

/* Card styles from old CSS - can be adapted here */
/* For now, just a placeholder */
.item-card {
    background: var(--bg-card);
    border-radius: 8px;
    border: 1px solid #222;
    overflow: visible;
    /* Changed from hidden to allow badge pop-out */
    transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 0.3s ease, border-color 0.3s ease;
    cursor: pointer;
    position: relative;
    transform: translate3d(0, 0, 0); /* GPU acceleration */
    will-change: transform, box-shadow;
}

.item-card:hover {
    transform: translate3d(0, -5px, 0);
}

#music .item-card:hover {
    border-color: var(--neon-music);
    box-shadow: 0 10px 30px -10px var(--shadow-music);
}

#party .item-card:hover {
    border-color: var(--neon-party);
    box-shadow: 0 10px 30px -10px var(--shadow-party);
}

.item-img-wrapper {
    width: 100%;
    aspect-ratio: 1/1;
    background: #1a1a1a;
    position: relative;
    border-radius: 8px 8px 0 0;
    overflow: hidden;
    transform: translateZ(0);
}

.item-media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: translateZ(0);
    will-change: transform;
}

.item-info {
    padding: 15px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.item-date {
    display: block;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--neon-party);
    letter-spacing: 0.5px;
}

.item-title {
    display: block;
    font-weight: 700;
    font-size: 1.1rem;
    color: #fff;
    line-height: 1.35;
}

.item-meta {
    font-size: 0.8rem;
    color: var(--color-text-muted);
    display: flex;
    justify-content: space-between;
    margin-top: 2px;
}

/* [LIVE SECTION] */
#live-container {
    max-width: 1400px;
    margin: 0 auto;
    padding-bottom: 60px;
}

.onair-container {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 50px;
    background: transparent;
    padding: 0;
}

.onair-info {
    flex: 1 1 420px;
    display: flex;
    flex-direction: column;
    gap: 28px;
}

.onair-player {
    flex: 1 1 480px;
    min-width: 0;
}

/* PIF Brand */
.pif-brand {
    display: flex;
    align-items: center;
    gap: 16px;
}

.show-logo {
    height: 32px;
    max-height: 36px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    filter: drop-shadow(0 0 12px rgba(242, 17, 44, 0.55));
}

.show-title {
    font-family: var(--font-display);
    font-size: 2.2rem;
    font-weight: 900;
    color: var(--neon-live);
    text-shadow: 0 0 20px rgba(242, 17, 44, 0.5);
    letter-spacing: 2px;
    margin: 0;
    text-transform: uppercase;
}

.pif-tag {
    background: rgba(242, 17, 44, 0.12);
    border: 1px solid rgba(242, 17, 44, 0.35);
    color: #fff;
    border-radius: 4px;
    padding: 4px 10px;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 1px;
}

.show-desc {
    font-size: 1.1rem;
    color: rgba(255, 255, 255, 0.65);
    line-height: 1.7;
}

/* Timer */
.timer-box {
    background: rgba(242, 17, 44, 0.05);
    border: 1px solid rgba(242, 17, 44, 0.25);
    border-radius: 14px;
    padding: 20px 24px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
}

.timer-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
    gap: 10px;
    flex-wrap: wrap;
}

.timer-label-main {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.6);
}

.pif-schedule-tag {
    font-size: 0.65rem;
    font-weight: 800;
    padding: 4px 10px;
    border-radius: 20px;
    letter-spacing: 0.8px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    text-transform: uppercase;
    transition: all 0.3s ease;
}

.pif-schedule-tag.special {
    /* 단색 체계: regular 가 시그니처 빨강이므로 special 은 중성 칩으로 구분합니다.
       2026-09-22 전에는 앰버였고 잠깐 초록이었습니다. 둘 다 팔레트 밖 색이었습니다. */
    background: rgba(212, 212, 212, 0.10);
    border: 1px solid rgba(212, 212, 212, 0.35);
    color: var(--n-8);
    box-shadow: none;
}

.pif-schedule-tag.regular {
    background: rgba(242, 17, 44, 0.15);
    border: 1px solid rgba(242, 17, 44, 0.45);
    color: #fff;
}

.schedule-date-card {
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    padding: 10px 14px;
    margin-bottom: 14px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-family: 'Courier New', Courier, monospace;
}

.date-main-text {
    font-size: 1rem;
    font-weight: 900;
    color: #fff;
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.date-main-text i {
    color: var(--neon-live);
    font-size: 0.9rem;
}

.day-highlight {
    color: var(--n-8);
    font-weight: 900;
}

.time-main-text {
    font-size: 0.88rem;
    font-weight: 800;
    color: rgba(255, 255, 255, 0.8);
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.time-main-text i {
    color: rgba(255, 255, 255, 0.5);
    font-size: 0.82rem;
}

.live-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--neon-live);
    display: inline-block;
    animation: pulseDot 2s infinite;
}

#countdown {
    font-family: var(--font-display);
    font-size: 2rem;
    font-weight: 900;
    color: var(--neon-live);
    letter-spacing: 3px;
}

/* [World Time List] */
.world-time-box {
    margin-top: 20px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    padding-top: 15px;
    width: 100%;
}

.world-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 6px;
    font-size: 0.85rem;
    color: #999;
    font-family: monospace;
    letter-spacing: -0.5px;
}

/* ⚠️ 현재 시간대 행은 **밝기로** 강조합니다. 예전에는 빨강이었는데,
   13.6px 에서 빨강은 APCA Lc 32.9 로 일반 행(#999, Lc 48)보다 오히려
   안 읽혔습니다. 강조하려다 반대가 된 자리였습니다. */
.world-row.highlight {
    color: var(--n-9);
    font-weight: 700;
}

.w-time {
    font-weight: 700;
    color: #fff;
    width: 50px;
    text-align: right;
}

.world-row.highlight .w-time {
    color: var(--n-9);
}

.w-zone {
    font-size: 0.7rem;
    opacity: 0.7;
    margin-left: 5px;
    width: 40px;
    text-align: left;
}

.w-city {
    flex: 1;
    text-align: left;
    padding-left: 10px;
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: 0px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

@keyframes pulseDot {
    0% {
        box-shadow: 0 0 0 0 var(--shadow-live);
    }

    70% {
        box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
    }

    100% {
        box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
    }
}

/* Platform chips */
.platform-section {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.plat-head {
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.5);
}

.plat-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.plat-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: var(--color-text);
    border-radius: 8px;
    padding: 10px 18px;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    transition: all 0.2s ease;
}

.plat-chip:hover {
    background: var(--neon-live);
    border-color: var(--neon-live);
    color: #151515;
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(0, 255, 148, 0.3);
}

/* Video card */
.video-card {
    position: relative;
    border-radius: 14px;
    overflow: hidden;
    background: #111;
    border: 1px solid #222;
    cursor: pointer;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.video-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 16px 40px rgba(242, 17, 44, 0.15);
    border-color: rgba(242, 17, 44, 0.3);
}

.video-thumb {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    display: block;
}

.video-badge {
    position: absolute;
    top: 14px;
    left: 14px;
    background: var(--neon-live);
    color: #151515;
    font-size: 0.7rem;
    font-weight: 900;
    letter-spacing: 2px;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: 4px;
}

.play-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
    opacity: 0;
    transition: opacity 0.2s ease;
}

.video-card:hover .play-overlay {
    opacity: 1;
}

.play-icon {
    font-size: 4rem;
    color: rgba(255, 255, 255, 0.9);
    filter: drop-shadow(0 0 20px rgba(242, 17, 44, 0.6));
}

.video-title-overlay {
    padding: 14px 16px;
    font-size: 0.95rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.85);
    min-height: 48px;
}

/* [Replay Archive Carousel] */
.archive-container {
    width: 100%;
    margin-top: 40px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    padding-top: 30px;
}

.archive-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}

.archive-title {
    font-size: 0.9rem;
    font-weight: 700;
    color: #fff;
    letter-spacing: 1px;
}

.archive-scroll {
    display: flex;
    gap: 24px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 24px 8px 28px 8px;
    scrollbar-width: thin;
    scrollbar-color: rgba(242, 17, 44, 0.15) transparent;
    transition: scrollbar-color 0.3s ease;
}

.archive-scroll:hover {
    scrollbar-color: rgba(242, 17, 44, 0.6) transparent;
}

.archive-scroll::-webkit-scrollbar {
    height: 6px;
}

.archive-scroll::-webkit-scrollbar-track {
    background: transparent;
}

.archive-scroll::-webkit-scrollbar-thumb {
    background: rgba(242, 17, 44, 0.15);
    border-radius: 10px;
    transition: background 0.3s ease;
}

.archive-scroll:hover::-webkit-scrollbar-thumb {
    background: rgba(242, 17, 44, 0.6);
}

.archive-scroll::-webkit-scrollbar-thumb:hover {
    background: var(--neon-live);
    box-shadow: 0 0 10px var(--shadow-live);
}

.archive-scroll-wrapper {
    position: relative;
    width: 100%;
}

.view-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%) scale(1);
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: rgba(10, 10, 10, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.95rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.6);
}

.view-btn:hover {
    background: rgba(20, 20, 20, 0.85);
    color: #fff;
    border-color: rgba(242, 17, 44, 0.55);
    box-shadow: 0 0 16px rgba(242, 17, 44, 0.5), 0 6px 20px rgba(0, 0, 0, 0.6);
    transform: translateY(-50%) scale(1.08);
}

.view-btn:active {
    transform: translateY(-50%) scale(0.95);
}

.prev-btn {
    left: -14px;
}

.next-btn {
    right: -14px;
}

.archive-item {
    flex: 0 0 220px;
    min-width: 0 !important;
    max-width: 220px;
    scroll-snap-align: start;
    cursor: pointer;
    position: relative;
    transition: transform 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
    transform: translate3d(0, 0, 0);
    will-change: transform;
}

.archive-item:hover {
    transform: translate3d(0, -8px, 0) scale(1.03);
}

.archive-thumb-box {
    width: 100%;
    aspect-ratio: 16 / 9;
    background: #151515;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.08);
    margin-bottom: 10px;
    position: relative;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
    transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
    transform: translateZ(0);
}

.archive-item:hover .archive-thumb-box {
    border-color: var(--neon-live);
    box-shadow: 0 10px 25px var(--shadow-live);
}

.archive-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.65;
    transition: opacity 0.3s ease, transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
    transform: translateZ(0);
    will-change: transform;
}

.archive-item:hover .archive-thumb {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1.06);
}

.more-box {
    width: 100%;
    aspect-ratio: 16 / 9;
    background: rgba(255, 255, 255, 0.03);
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    color: #888;
    transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
}

.archive-item:hover .more-box {
    background: linear-gradient(135deg, var(--neon-live), rgba(242, 17, 44, 0.6));
    color: white;
    border-color: var(--neon-live);
    box-shadow: 0 10px 25px var(--shadow-live);
}

.more-text {
    text-align: center;
    font-size: 0.7rem;
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: 1px;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   [GIGS / PARTY LAYOUT]

   🐛 바로 윗줄에 **닫는 중괄호가 하나 더** 있었습니다(2026-09-21 발견, 지움). 파일 전체에서
      `{` 1020개 대 `}` 1021개, 여분은 이 한 곳뿐이었습니다.

      결과가 눈에 안 띄는 종류였습니다. CSS 파서는 맨 바깥에서 `}` 를 만나면 오류 복구에
      들어가 **바로 다음 규칙 하나를 통째로 버립니다.** 그 다음 규칙이 아래 `.party-layout`
      이었고, 그래서 이 규칙은 **브라우저에 도착한 적이 없습니다**(실측: CSSOM 에 `.party-layout`
      기본 규칙 없음, `@media (max-width:900px)` 판만 존재. 바로 뒤의 `.upcoming-section`
      과 `.past-section` 은 멀쩡히 적용 중).

      그래서 데스크톱 2단 배치(UPCOMING 62% | PAST 38%)는 오래전부터 **죽어 있었고**,
      두 섹션이 한 단으로 쌓여 있었습니다. 그런데 `.past-section` 의 `padding-left: 40px`
      과 `border-left` 는 살아 있어서, PAST EVENTS 제목만 다른 네 제목보다 **40.5px 오른쪽**
      에 서고(실측 264.3 대 223.8) 왼쪽에 쓸모없는 세로 실선이 남았습니다.

   ⚠️ 고치면서 2단으로 **되돌리지 않았습니다.** 한 단이 지금 이 탭의 전제입니다 - 목차가
      UPCOMING 과 PAST 를 별개의 목적지로 다루고, 모바일 압축 스트립도 전체 폭을 씁니다.
      그래서 `display: block` 을 **명시**해 둡니다. 우연이 아니라 선택이라는 뜻입니다.
      2단으로 되돌리려면 여기를 `display: flex; gap: 40px` 로 바꾸고 아래 두 규칙의
      `flex` 값을 되살리면 됩니다.
   ⚠️ 원래 있던 `margin-top: 30px` 은 되살리지 않았습니다. 그 값도 함께 죽어 있었으므로
      지금 화면의 간격은 그것이 없는 상태로 맞춰져 있습니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.party-layout {
    display: block;
    width: 100%;
}

.upcoming-section {
    display: flex;
    flex-direction: column;
}

/* 구분선은 **가로**입니다. 세로 실선과 40px 들여쓰기는 2단 시절의 유물이라 지웠습니다.
   이 값은 아래 `@media (max-width: 900px)` 의 것과 같습니다 - 이제 모든 폭에서 같은 모양입니다. */
.past-section {
    display: flex;
    flex-direction: column;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 30px;
}

.section-title {
    font-size: 1.1rem;
    font-weight: 800;
    letter-spacing: 2px;
    color: #fff;
    margin-bottom: 25px;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   섹션 제목 앞의 점 (2026-09-21 정리)

   ⚠️ 색이 **마크업에 인라인으로** 박혀 있었습니다. index.php 의 네 곳이 전부
      `style="background:var(--neon-party); ..."` 를 달고 있었는데, 그 값은 PARTY 색입니다.
      같은 패턴을 MUSIC 이나 LIVE 에 복사하면 보라·초록 탭에 앰버 점이 찍힙니다.
      이제 색은 **섹션이 정합니다.** 마크업에는 빈 span 하나만 둡니다.

   ⚠️ `currentColor` 기본값은 안전장치입니다. 세 섹션 밖에서 이 클래스를 쓰면 제목 글자색을
      따라가므로, 색을 못 찾아 투명한 점이 되는 일이 없습니다(실측으로 그 상태를 봤습니다).
   ⚠️ `flex: none` 도 필요합니다. 제목이 flex 줄이라 PAST EVENTS 처럼 오른쪽에 버튼이 붙는
      경우 좁은 화면에서 점이 눌려 타원이 됩니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.glow-indicator {
    width: 8px;
    height: 8px;
    flex: none;
    border-radius: 50%;
    display: inline-block;
    background: currentColor;
    box-shadow: 0 0 10px currentColor;
    animation: pulseGlow 2s infinite alternate;
}

#music .glow-indicator { background: var(--neon-music); box-shadow: 0 0 10px var(--shadow-music); }
#party .glow-indicator { background: var(--neon-party); box-shadow: 0 0 10px var(--shadow-party); }
#live .glow-indicator { background: var(--neon-live); box-shadow: 0 0 10px var(--shadow-live); }

@keyframes pulseGlow {
    0% {
        transform: scale(0.9);
        opacity: 0.6;
    }

    100% {
        transform: scale(1.2);
        opacity: 1;
    }
}

/* Upcoming grid */
.gigs-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 24px;
    /* Cards no longer all share one box ratio (see the full-artwork rule below), so a row can mix
       a 9:16 flyer with a 4:5 one. Without this they would stretch to the tallest card and the
       shorter card would pad its .item-info out with dead space - the same whitespace, just moved
       from beside the poster to under the title. `start` lets each card end where its own content
       ends. */
    align-items: start;
}

.gig-card {
    display: flex;
    flex-direction: column;
    min-width: 0;
    transition: transform 0.4s cubic-bezier(0.165, 0.84, 0.44, 1), box-shadow 0.4s ease !important;
}

.gig-card:hover {
    border-color: var(--neon-party) !important;
    box-shadow: 0 10px 25px var(--shadow-party) !important;
}

.gig-card .item-img-wrapper {
    aspect-ratio: 4 / 5 !important;
    background: #151515 !important;
    flex: 0 0 auto;
    min-height: 0;
}

.gig-card .item-img-wrapper > .item-media { position: absolute; inset: 0; }

/* Per-event framing, chosen in admin. Needs the extra specificity AND !important because the
   base rule below pins contain with !important — an inline style on the <img> loses to it, which
   is why the first attempt at this silently did nothing (2026-08-24). Covers the hover <video>
   too, since it also carries .item-media. */
.gig-card[data-fit="cover"] .item-media {
    object-fit: cover !important;
    /* Only meaningful while cropping. Defaults to centre, so a card that never set a position
       looks exactly as it did before. */
    object-position: var(--pif-pos, 50% 50%) !important;
}

.gig-card[data-fit="contain"] .item-media {
    object-fit: contain !important;
    object-position: var(--pif-pos, 50% 50%) !important;
}

.gig-card .item-media {
    object-fit: cover !important;
    object-position: center !important;
    width: 100% !important;
    height: 100% !important;
    background-color: #151515;
    /* Collaboration editions can save a 4:5 UPCOMING-specific focal point and zoom. The same
       transform reaches both the resting poster and the hover <video>, so motion never jumps to a
       different crop. Defaults leave every event that has no previewFrame exactly unchanged. */
    transform: scale(var(--pif-zoom, 1));
    transform-origin: var(--pif-pos, 50% 50%);
}

/* PC defaults to a filled 4:5 crop with each poster's saved focal point/zoom.
   Full artwork remains an explicit choice; mobile below always uses the source ratio. */
.gig-card[data-poster-layout="full"] .item-media {
    object-fit: contain !important;
    object-position: center !important;
    transform: none;
}

/* Full artwork: give the BOX the poster's own ratio instead of letterboxing inside a 4:5 one.
   Measured 2026-08-31 on the two live UPCOMING cards (LION Superclub x PIF, Muzie x PIF): both
   posters are 960x1706 (9:16) and both were being painted `contain` into a 239x299 (4:5) box, so
   each rendered 168px wide and left 71px of empty card either side. Reserving the source ratio
   makes the card taller instead of emptier - the poster meets all four edges, and `full` finally
   means "the whole artwork, at its own shape" rather than "the whole artwork, framed in grey".
   Only applies once the ratio is actually known (server-side getimagesize via PIF_POSTER_SIZES, or
   learned from the loaded image); everything else keeps the 4:5 contain behaviour it had. */
.gig-card[data-poster-layout="full"][data-poster-size-known="1"] .item-img-wrapper {
    aspect-ratio: var(--pif-poster-ratio, 4 / 5) !important;
}

/* With the box already at the source ratio, cover and contain frame identically - but cover cannot
   leave a sub-pixel hairline the way contain can, and it is what the hover <video> already uses, so
   still and clip share one framing and hover does not jump-cut between them.
   ⚠️ NOT on a rotating card. That equivalence holds only while the card shows ONE picture: the box
   takes the FIRST asset's ratio, so every other slide is a different shape and `cover` crops it.
   This rule is specificity 0,4,0 and was silently beating `[data-fit="contain"]` at 0,3,0 - which is
   why the cards still cropped after all four crop defaults were flipped. `data-rotator` is written
   by pifApplyPosterLayout whenever the card's media is a .pif-rotator. */
.gig-card[data-poster-layout="full"][data-poster-size-known="1"]:not([data-rotator="1"]) .item-media {
    object-fit: cover !important;
}

/* ── A rotating card gets a FIXED 9:16 box ──────────────────────────────────────────────────────
   Deriving the box from the poster only works while a card shows one picture. A rotator cycles
   assets of different shapes, so taking the FIRST one's ratio made every card a different height -
   the ragged PAST EVENTS grid - and guaranteed that every other slide was the wrong shape for it.
   The box is pinned instead, and anything that does not fill it letterboxes into the wash with its
   feathered edge, which is the treatment the covers already use.
   ⚠️ Gated on **data-fit="contain"**, and that matters. The first version tested
   `[data-poster-layout][data-poster-size-known]` by PRESENCE - but pifApplyPosterLayout writes both
   attributes unconditionally alongside data-rotator, so presence gated NOTHING and a CROP-mode
   rotator got the 9:16 box too. In crop mode `object-fit: cover` wins and pifCoverFade returns a
   no-op mask, so that card had no letterbox and no feather to justify the taller frame - it just
   cover-cropped a 4:5 slide into 9:16, losing ~30% of its width, and grew from 299px to 425px in a
   239px column. No event is in crop mode today, so it was latent; one admin toggle reaches it.
   data-fit is the honest signal because it is the SAME attribute the object-fit rule below reads,
   so the box ratio and the fitting can never disagree. `[data-poster-layout]` stays purely for
   specificity headroom: (0,5,0) beats
   `.gig-card[data-poster-layout="full"][data-poster-size-known="1"] .item-img-wrapper` at (0,4,0)
   outright, rather than tying it and relying on source order. Being outside the mobile @media block
   it settles phones too. --pif-poster-ratio is simply unused on a contain rotator. */
.gig-card[data-rotator="1"][data-fit="contain"][data-poster-layout] .item-img-wrapper {
    aspect-ratio: 9 / 16 !important;
}

/* A rotating card letterboxes every slide into the fixed box and fills the remainder with the
   blurred wash. Specificity 0,5,0 so it outranks every crop rule above, including the mobile block. */
.gig-card[data-rotator="1"][data-fit="contain"] .item-media,
.gig-card[data-rotator="1"][data-fit="contain"] .video-placeholder-wrap video {
    object-fit: contain !important;
    object-position: center !important;
    transform: none !important;
}

.gig-card .item-info { flex: 1; }
.gig-card-labels { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 5px; }
.gig-card .gig-card-labels > div {
    position: static;
    max-width: 100%;
    box-sizing: border-box;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
.gig-video-label { color: #aaa; font-size: 0.6rem; letter-spacing: 1px; white-space: nowrap; }
.gig-video-label i { color: var(--neon-party); margin-right: 4px; }

/* A phone is a vertical reading surface: reserve the source ratio before loading, show one
   complete poster per row, and never inherit a PC crop/zoom. Stills and videos share the rule. */
@media (max-width: 767px) {
    #party .gigs-grid, #party .past-gigs-list { grid-template-columns: minmax(0, 1fr); }
    .gig-card .item-img-wrapper { aspect-ratio: var(--pif-poster-ratio, 4 / 5) !important; }
    .gig-card[data-poster-layout] .item-media {
        object-fit: contain !important;
        object-position: center !important;
        transform: none;
    }
}

/* Past Gigs - same card grid as Upcoming */
.past-gigs-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 24px;
    align-items: start; /* same reason as .gigs-grid above */
}

/* PAST badge (muted grey, greyscale contrast to UPCOMING amber) */
.past-badge {
    position: absolute;
    top: -10px;
    left: 12px;
    z-index: 10;
    background: rgba(80, 80, 80, 0.95);
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 1.5px;
    padding: 5px 12px;
    border-radius: 4px;
    text-transform: uppercase;
    /* backdrop-filter removed 2026-07-27 (perf): the fill is 95% opaque, so only 5% of the backdrop
       showed through — a blur on that is not perceptible, but each instance still forced its own
       compositing layer. Pure cost, no visible effect. */
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
    border: 1px solid rgba(255, 255, 255, 0.05);
}

.upcoming-badge {
    position: absolute;
    top: -10px;
    left: 12px;
    z-index: 10;
    background: rgba(242, 17, 44, 0.95);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 1.5px;
    padding: 5px 12px;
    border-radius: 4px;
    text-transform: uppercase;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: 0 4px 12px rgba(242, 17, 44, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.1);
}

/* Dated editions projected from the collaboration portfolio into UPCOMING. A separate class is
   intentional: Samsung's signature-colour lock pins .upcoming-badge red, while these need an
   immediate amber distinction without changing the PIF Party cards. */
.collab-upcoming-badge {
    position: absolute;
    top: -10px;
    left: 12px;
    z-index: 10;
    background: rgba(212, 212, 212, 0.96);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 1.2px;
    padding: 5px 12px;
    border-radius: 4px;
    text-transform: uppercase;
    box-shadow: 0 4px 12px rgba(212, 212, 212, 0.42);
    border: 1px solid rgba(255, 255, 255, 0.14);
}

/* Gig posters idle in mono and come to full colour only on the hovered card — for BOTH the
   Upcoming and Past grids. This used to be scoped to .past-gig-card only, so Upcoming cards had
   no rule at all and hovering them changed nothing.
   .item-media is on the poster <img> AND on the <video> that playHoverVideoInternal injects
   (className = 'item-media party-video-media'), so one selector covers the still and the clip —
   the auto-rotating carousel therefore plays in mono and only the card under the cursor is
   full colour, which is the intended "hovered one wins" read. */
/* UPCOMING cards are always full colour — no filter at all. Only the PAST grid is muted, and it
   comes to full colour on the hovered card. .item-media is on the poster <img> AND on the <video>
   playHoverVideoInternal injects (className 'item-media party-video-media'), so the still and the
   clip are covered by one selector. */
/* ── One source of truth for the muted state ────────────────────────────────────────────────────
   --cvr-mono is declared on the CARD and inherited by everything inside it, so the poster, the
   injected <video> and the blurred wash can never disagree. They did: this rule only ever matched
   `.item-media`, and the wash a rotating card paints behind its poster is not `.item-media` - so a
   PAST card showed a grey poster sitting on a full-colour blurred background. */
/* ⚠️ The muted state goes on the GROUP, never on the wash's own filter.
   Two reasons. First, one filter over the whole group means the blurred surround and the picture it
   belongs to can never disagree - the same principle the opacity grouping already follows. Second,
   measured 2026-09-03: `transition: filter` on a value built from a `var()` inside a LONGER literal
   chain pins the property to the value the transition started from, so a custom-property change
   never lands. The wash filter is `blur() brightness() saturate() contrast() var(--cvr-mono)` and
   simply stopped updating; removing the transition proved it. A filter that is the bare `var()`
   alone - which is what these two rules use - transitions correctly.
   Scoped to PAST cards so an upcoming rotator carries no filter at all and gains no extra
   compositing layer. */
/* ⚠️ LITERAL filters, never `var()`. Measured 2026-09-03 in this engine: `transition: filter` on a
   value that comes from a custom property simply does not update - the property stays pinned to the
   value the transition started from, however long you wait, and removing the transition proves it.
   A `--cvr-mono` variable was tried and had to be abandoned for exactly this.

   The muted state goes on the GROUP so the blurred surround and the picture inside it can never
   disagree - the same principle the opacity grouping follows. Before this, only `.item-media`
   matched, so a PAST card showed a grey poster on a full-colour blurred background.
   `:not(.collab-cover-img)` keeps the second selector off a rotator's own pictures: they are inside
   a group that is already filtered, and filtering twice would compound brightness to 0.64. The
   injected clip lives outside the groups, in .video-placeholder-wrap, so it needs its own rule.
   Scoped to PAST cards, so an upcoming rotator carries no filter and gains no compositing layer. */
.past-gig-card .cvr-layer,
.past-gig-card .item-media:not(.collab-cover-img) {
    filter: grayscale(100%) brightness(0.8);
    transition: filter 0.35s ease;
}

/* Desktop: the hovered card wins (unchanged).
   Touch devices have no hover at all, so a past card could never come to colour there. On those
   devices `.in-view` is toggled by an IntersectionObserver (registerPastCardViewColor in
   script.js) as the card scrolls through the viewport — that is the mobile equivalent of hover.
   The class is only ever added under `(hover: none)`, so desktop behaviour is untouched. */
/* `.media-playing` is set by script.js from the <video>'s own playing/pause events while a past
   card's poster clip is actually rolling — so a moving poster is always in colour, and a still one
   stays mono. Event-driven rather than set alongside the play() call, because play() can be
   refused and a refused clip must not be left sitting in colour. */
/* The card comes to colour as a whole - poster, clip and wash together - because they all read the
   same inherited variable. */
/* ⚠️ `.in-view` 를 다시 넣지 마십시오. 화면에 들어온 카드를 전부 컬러로 만들어서 한 줄이
   통째로 켜집니다(2026-09-19 제거). 컬러가 되는 조건은 두 가지뿐입니다:
   마우스를 올렸을 때, 그리고 그 카드의 클립이 실제로 도는 중일 때. */
.past-gig-card:hover .cvr-layer,
.past-gig-card.media-playing .cvr-layer,
.past-gig-card:hover .item-media:not(.collab-cover-img),
.past-gig-card.media-playing .item-media:not(.collab-cover-img) {
    filter: grayscale(0%) brightness(1);
}

/* Respect reduced-motion: keep the colour change, drop the fade. */
@media (prefers-reduced-motion: reduce) {
    .past-gig-card .cvr-layer,
    .past-gig-card .item-media { transition: none; }
}

/* No-poster placeholder */
.gig-no-poster {
    width: 100%;
    aspect-ratio: 4 / 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: rgba(255, 255, 255, 0.03);
    color: rgba(255, 255, 255, 0.2);
}

.gig-no-poster i {
    font-size: 2rem;
}

.gig-no-poster span {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
}

/* ====== GIG DETAILS MODAL ====== */
.event-modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(4, 4, 4, 0.75);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    z-index: 2000;
    display: flex;
    justify-content: center;
    align-items: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: max(20px, env(safe-area-inset-top, 20px)) 0 max(20px, env(safe-area-inset-bottom, 20px));
    box-sizing: border-box;
}

.event-modal.visible {
    opacity: 1;
    pointer-events: auto;
}

.event-modal-content {
    background: rgba(15, 15, 15, 0.95);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 16px;
    width: 90%;
    max-width: 880px;
    height: 84vh;
    max-height: 88vh;
    overflow-y: auto;
    display: flex;          /* flex column so grid child can stretch */
    flex-direction: column;
    box-shadow: 0 30px 60px rgba(0, 0, 0, 0.8), 0 0 30px rgba(212, 212, 212, 0.08);
    transform: scale(0.93) translateY(20px);
    transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
    position: relative;
    scrollbar-width: thin;
    scrollbar-color: rgba(212, 212, 212, 0.3) rgba(255, 255, 255, 0.02);
}

.event-modal.visible .event-modal-content {
    transform: scale(1) translateY(0);
}

.close-event-modal {
    position: fixed;
    top: 30px;
    right: 30px;
    color: rgba(255, 255, 255, 0.5);
    font-size: 1.5rem;
    cursor: pointer;
    transition: color 0.2s, transform 0.2s;
    z-index: 2001;
    display: none;
    /* Only show in desktop when modal is visible */
}

.event-modal.visible~.close-event-modal,
.event-modal.visible .close-event-modal {
    display: block;
}

.close-event-modal:hover {
    color: #fff;
    transform: rotate(90deg);
}

/* Event detail content grid */
.event-detail-grid {
    display: grid;
    grid-template-columns: 340px 1fr;
    width: 100%;
    flex: 1;            /* stretch to fill flex parent (.event-modal-content) */
    min-height: 0;      /* allow grid to shrink inside flex */
    overflow: hidden;
}

.event-detail-poster-box {
    background: #0d0d0d;
    border-right: 1px solid rgba(255, 255, 255, 0.06);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
    /* height comes from grid row = full grid height */
}

.event-detail-poster-box img,
.event-detail-poster-box video {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain; /* Maintain natural proportions without layout break */
    display: block;
}

.event-detail-info-box {
    padding: 28px 30px 40px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    overflow-y: auto;       /* the whole info-box scrolls if needed */
    box-sizing: border-box;
    scrollbar-width: thin;
    scrollbar-color: rgba(212, 212, 212, 0.2) rgba(255, 255, 255, 0.02);
    -webkit-overflow-scrolling: touch;
}

/* Desc text scroll container — fixed height, text scrolls inside */
.event-detail-scroll-content {
    max-height: 130px;      /* limit text area to ~4 lines visible */
    min-height: 48px;
    overflow-y: auto;
    padding-right: 6px;
    scrollbar-width: thin;
    scrollbar-color: rgba(212, 212, 212, 0.25) rgba(255, 255, 255, 0.02);
    -webkit-overflow-scrolling: touch;
}

.evt-detail-desc-box {
    min-height: 40px;
}

.desc-body {
    font-size: 0.88rem;
    line-height: 1.75;
    color: rgba(255, 255, 255, 0.8);
    white-space: pre-wrap;
    word-break: break-word;
}

.desc-header {
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 1.5px;
    color: rgba(255, 255, 255, 0.35);
    text-transform: uppercase;
}

.event-detail-scroll-content::-webkit-scrollbar {
    width: 6px;
}

.event-detail-scroll-content::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.01);
    border-radius: 3px;
}

.event-detail-scroll-content::-webkit-scrollbar-thumb {
    background: rgba(212, 212, 212, 0.25);
    border-radius: 3px;
    transition: background 0.3s ease;
}

.event-detail-scroll-content::-webkit-scrollbar-thumb:hover {
    background: rgba(212, 212, 212, 0.6);
}

/* Static bottom actions & gallery container inside the info box */
.event-detail-static-bottom {
    flex-shrink: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 14px;
    padding-bottom: 24px;
    margin-top: 6px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Custom premium scrollbar for Event details */
.event-detail-info-box::-webkit-scrollbar {
    width: 6px;
}

.event-detail-info-box::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.01);
    border-radius: 3px;
}

.event-detail-info-box::-webkit-scrollbar-thumb {
    background: rgba(212, 212, 212, 0.2); /* Accent Party Gold tone, subtle */
    border-radius: 3px;
    transition: background 0.3s ease;
}

.event-detail-info-box::-webkit-scrollbar-thumb:hover {
    background: rgba(212, 212, 212, 0.6);
}

.evt-detail-badge {
    font-size: 0.7rem;
    font-weight: 800;
    color: var(--neon-party);
    background: rgba(212, 212, 212, 0.1);
    border: 1px solid rgba(212, 212, 212, 0.3);
    padding: 6px 12px;
    border-radius: 30px;
    align-self: flex-start;
    letter-spacing: 1.5px;
}

.evt-detail-title {
    font-size: 1.8rem;
    font-weight: 800;
    color: #fff;
    line-height: 1.25;
    margin: 0;
}

.evt-detail-meta-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    padding: 12px 0;
}

.evt-detail-meta-item {
    display: flex;
    align-items: center;
    gap: 16px;
}

.evt-detail-meta-item i {
    font-size: 1.2rem;
    color: var(--neon-party);
    width: 24px;
    text-align: center;
}

.evt-detail-meta-item .meta-label {
    display: block;
    font-size: 0.65rem;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.4);
    letter-spacing: 1px;
    margin-bottom: 2px;
}

.evt-detail-meta-item .meta-value {
    font-size: 0.9rem;
    font-weight: 600;
    color: #fff;
}

.evt-detail-desc-box {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.evt-detail-desc-box .desc-header {
    font-size: 0.75rem;
    font-weight: 800;
    color: rgba(255, 255, 255, 0.4);
    letter-spacing: 1px;
}

.evt-detail-desc-box .desc-body {
    font-size: 0.9rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.75);
}

.evt-detail-action {
    margin-top: 10px;
}

.event-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    background: linear-gradient(135deg, #f2112c, #C20E23);
    color: #fff;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 700;
    padding: 14px 28px;
    border-radius: 8px;
    box-shadow: 0 4px 15px rgba(212, 212, 212, 0.35);
    transition: all 0.3s ease;
}

.event-action-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(212, 212, 212, 0.5), 0 0 10px rgba(255, 255, 255, 0.2);
}

/* Responsive */
@media (max-width: 900px) {
    .archive-item {
        flex: 0 0 140px !important;
        min-width: 0 !important;
        max-width: 140px !important;
    }

    .archive-item .archive-thumb-box {
        margin-bottom: 3px;
        border-radius: 4px;
    }

    .archive-item div:last-child {
        font-size: 0.7rem !important;
    }

    .archive-item .fa-play {
        font-size: 0.9rem !important;
    }

    .archive-scroll {
        padding: 10px 0 20px 0;
        gap: 10px;
    }

    .more-box i {
        font-size: 1.1rem !important;
        margin-bottom: 2px !important;
    }

    .onair-container {
        flex-direction: column;
        gap: 30px;
    }

    .onair-info,
    .onair-player {
        flex: none;
        width: 100%;
    }

    .show-title {
        font-size: 1.6rem;
    }

    #countdown {
        font-size: 1.4rem;
    }

    /* Gigs Party Responsive */
    .party-layout {
        flex-direction: column;
        gap: 30px;
    }

    .upcoming-section {
        flex: none;
        width: 100%;
    }

    .past-section {
        flex: none;
        width: 100%;
        border-left: none;
        border-top: 1px solid rgba(255, 255, 255, 0.08);
        padding-left: 0;
        padding-top: 30px;
    }

    .event-detail-grid {
        grid-template-columns: 1fr;
        flex: none;         /* no flex stretch on mobile, natural height */
        min-height: unset;
        overflow: visible;
    }

    .event-detail-poster-box {
        border-right: none;
        border-bottom: 1px solid rgba(255, 255, 255, 0.08);
        max-height: 260px;
        height: 260px;
        position: sticky;
        top: 0;
        z-index: 100;
        background: #0d0d0d;
        width: 100%;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
    }

    .event-detail-info-box {
        padding: 20px;
        gap: 12px;
        min-height: unset;
        overflow: visible;
        box-sizing: border-box;
    }

    .event-detail-scroll-content {
        max-height: 120px;
        min-height: 44px;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        padding-right: 6px;
        margin-top: 4px;
        scrollbar-width: thin;
        scrollbar-color: rgba(212, 212, 212, 0.25) rgba(255, 255, 255, 0.02);
    }

    .event-detail-static-bottom {
        border-top: 1px solid rgba(255, 255, 255, 0.1);
        padding-top: 14px;
        padding-bottom: 36px;
        margin-top: 10px;
        box-sizing: border-box;
    }

    .evt-detail-title {
        font-size: 1.4rem;
    }

    .event-modal-content {
        height: auto;
        max-height: 88dvh;
        width: 92%;
        max-width: 760px;
        display: block;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 36px;
        box-sizing: border-box;
    }

    .close-event-modal {
        top: 20px;
        right: 20px;
        font-size: 1.25rem;
    }
}

/* ===================================================
   [EVENT POPUP MODAL - LANDSCAPE & TABLET SHORT RESPONSIVE ENGINE]
   Prevents bottom cutoff on tablet landscape & awkward/short screens
   =================================================== */
@media (max-height: 850px) and (min-width: 769px) {
    .event-modal-content {
        height: auto !important;
        max-height: 88dvh !important;
        max-height: 88vh !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
    }

    .event-detail-grid {
        grid-template-columns: min(280px, 32vw) 1fr !important;
        height: auto !important;
        min-height: 0 !important;
    }

    .event-detail-poster-box {
        max-height: 75vh !important;
    }

    .event-detail-info-box {
        padding: 20px 24px 30px !important;
    }

    .event-detail-scroll-content {
        max-height: 100px !important;
    }
}

@media (max-height: 660px) {
    .event-modal-content {
        height: auto !important;
        max-height: 92dvh !important;
        max-height: 92vh !important;
        width: 94% !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch !important;
        padding-bottom: 24px !important;
    }

    .event-detail-grid {
        grid-template-columns: min(220px, 28vw) 1fr !important;
    }

    .event-detail-info-box {
        padding: 16px 20px 24px !important;
        gap: 8px !important;
    }

    .event-detail-static-bottom {
        padding-bottom: 16px !important;
    }
}


/* ===================================================
   [MOBILE & TABLET PORTRAIT RESPONSIVE ENGINE]
   Header is position:fixed (~60px tall on mobile).
   Player bar is position:fixed at bottom (~90px).
   Footer copyright sits ~50px above player bar.
   #main-content starts at viewport top (behind header).
   Strategy: make #gateways height:auto, center it in
   #main-content via asymmetric padding that accounts for
   BOTH footer and player bar at the bottom.
   =================================================== */
@media (max-width: 1024px) and (orientation: portrait), (max-width: 960px) {

    #main-content {
        height: 100dvh !important;
        height: 100vh !important;
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: center !important;
        /* top: header clearance (~65px)
           bottom: player bar (~90px) + footer text (~50px) + breathing room (~60px) = ~200px
           → cards naturally sit at optical center of visible usable area */
        padding-top: 65px !important;
        padding-bottom: 210px !important;
        box-sizing: border-box !important;
        overflow: hidden !important;
    }

    #gateways {
        flex-direction: column !important;
        justify-content: center !important;
        align-items: center !important;
        width: 100% !important;
        max-width: 480px !important;
        height: auto !important;           /* KEY FIX: wrap content, don't fill viewport */
        max-height: none !important;
        margin: 0 auto !important;
        padding: 0 16px !important;
        box-sizing: border-box !important;
        overflow: hidden !important;
        gap: 12px !important;
    }

    .gateway-card {
        width: 100% !important;
        flex: 0 0 auto !important;          /* Fixed height, not stretchy */
        height: clamp(90px, 18dvh, 130px) !important;
        max-height: 130px !important;
        min-height: 90px !important;
        padding: 10px 16px !important;
        justify-content: center !important;
        align-items: center !important;
        border-radius: 12px !important;
        box-sizing: border-box !important;
    }

    .gateway-logo {
        height: clamp(30px, 4.5vh, 42px) !important;
        max-height: 42px !important;
        max-width: 65% !important;
        margin-bottom: 4px !important;
        object-fit: contain !important;
    }

    .gateway-title {
        font-size: clamp(1.15rem, 3.5vw, 1.7rem) !important;
        letter-spacing: 2.5px !important;
    }
}


/* [FOOTER] */
footer {
    position: fixed;
    bottom: calc(105px + env(safe-area-inset-bottom, 0px));
    left: 0;
    width: 100%;
    text-align: center;
    padding: 6px 20px;
    font-size: 0.72rem;
    letter-spacing: 1.2px;
    color: rgba(255, 255, 255, 0.45);
    z-index: 100;
    text-transform: uppercase;
    pointer-events: none;
    transition: bottom 0.3s ease, opacity 0.3s ease;
}

footer p {
    margin: 2px 0;
}

.footer-credit, .mobile-footer-credit {
    font-size: 0.68rem;
    color: rgba(255, 255, 255, 0.35);
    letter-spacing: 1.5px;
    margin-top: 2px;
}

.footer-credit a, .mobile-footer-credit a {
    color: rgba(255, 255, 255, 0.7);
    font-weight: 700;
    text-decoration: none;
    pointer-events: auto;
    transition: color 0.3s ease, text-shadow 0.3s ease;
}

.footer-credit a:hover, .mobile-footer-credit a:hover {
    color: #ffffff;
    text-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
}

@media (max-width: 768px) {
    footer {
        bottom: calc(90px + env(safe-area-inset-bottom, 0px));
        font-size: 0.62rem;
        padding: 4px 10px;
    }
    .footer-credit {
        font-size: 0.58rem;
        letter-spacing: 1px;
    }
}

@media (max-height: 500px) {
    footer {
        bottom: calc(75px + env(safe-area-inset-bottom, 0px));
        font-size: 0.58rem;
    }
    .footer-credit {
        font-size: 0.54rem;
    }
}


/* ===================================================
   [MOBILE RESPONSIVE — 768px tablets & large phones]
   =================================================== */
@media (max-width: 768px) {

    /* Header */
    #main-header {
        padding: 14px 20px;
        flex-wrap: nowrap;
    }

    .logo img {
        height: 20px;
    }

    /* nav stays optically centered between logo and social icons */
    #main-header nav {
        position: absolute;
        left: calc(50% - 28px);
        transform: translateX(-50%);
    }

    #crew-btn {
        font-size: 0.65rem;
        letter-spacing: 2.5px;
    }

    .social-links-header {
        font-size: 0.9rem;
        gap: 12px;
    }

    /* Content sections */
    .content-section {
        padding: 70px 16px 240px !important;
        overflow-y: auto;
        overflow-x: hidden;
        max-width: 100vw;
        box-sizing: border-box;
        -webkit-overflow-scrolling: touch;
    }

    .content-section h2 {
        font-size: 1.8rem;
        margin-bottom: 24px;
    }

    #global-section-close-btn {
        top: 68px;
        right: 14px;
        font-size: 0.72rem;
        font-weight: 800;
        padding: 7px 14px;
        border-radius: 20px;
        z-index: 99999;
    }

    /* Archive (Compact 50% Mobile Card Size) */
    .archive-container {
        margin-top: 20px;
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
    }

    .archive-scroll-wrapper {
        position: relative;
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
        overflow: hidden;
    }

    .archive-scroll {
        display: flex;
        gap: 10px;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x mandatory;
        padding: 18px 4px 18px 4px !important;
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
    }

    .archive-item {
        flex: 0 0 100px !important;
        width: 100px !important;
        max-width: 100px !important;
        min-width: 0 !important;
        box-sizing: border-box !important;
    }

    .archive-thumb-box {
        margin-bottom: 4px;
        border-radius: 6px;
    }

    .archive-item div[style*="font-size"],
    .archive-item .archive-title {
        font-size: 0.65rem !important;
        line-height: 1.25 !important;
        font-weight: 700 !important;
    }

    .view-btn {
        display: none;
    }

    /* Grid cards */
    .grid-container {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        gap: 16px;
    }

    /* Footer: keep visible and elevated above bottom bar */
    footer {
        display: block;
    }
}


/* ===================================================
   [MOBILE RESPONSIVE — 480px small phones]
   =================================================== */
@media (max-width: 480px) {

    /* Header tighter */
    #main-header {
        padding: 10px 12px;
    }

    .logo img {
        height: 20px;
    }

    #crew-btn {
        font-size: 0.78rem;
        padding: 6px 8px;
    }

    .social-links-header {
        font-size: 0.85rem;
        gap: 10px;
    }

    /* Content section */
    .content-section {
        padding: 60px 12px 220px !important;
    }

    .content-section h2 {
        font-size: 1.5rem;
        letter-spacing: 1px;
        margin-bottom: 18px;
    }

    .close-btn {
        top: 60px;
        right: 12px;
        font-size: 0.7rem;
        padding: 6px 10px;
        z-index: 300;
    }

    .pif-brand {
        max-width: calc(100% - 75px);
    }

    .show-logo {
        max-height: 22px;
    }

    /* Crew modal: single column on very small screens */
    .crew-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 10px;
    }

    .crew-nickname {
        font-size: 1rem;
    }

    .crew-role {
        font-size: 0.75rem;
    }

    .crew-card-info {
        padding: 8px;
    }

    .crew-card-initials {
        font-size: 3rem;
    }

    /* LIVE */
    .show-title {
        font-size: 1.2rem;
    }

    #countdown {
        font-size: 1.2rem;
        letter-spacing: 1px;
    }

    .show-desc {
        font-size: 0.9rem;
    }

    .timer-box {
        padding: 10px 12px;
    }

    .plat-chip {
        padding: 7px 10px;
        font-size: 0.75rem;
        gap: 5px;
    }

    .plat-head {
        font-size: 0.7rem;
    }

    /* Archive */
    .archive-item {
        flex: 0 0 92px !important;
        width: 92px !important;
        max-width: 92px !important;
        min-width: 0 !important;
    }

    .archive-title {
        font-size: 0.75rem;
    }


    /* Grid */
    .grid-container {
        grid-template-columns: 1fr 1fr;
        gap: 10px;
    }

    /* World time — hide timezone code on tiny screens */
    .w-zone {
        display: none;
    }
}

/* ====== STICKY COMPACT PODCAST & RADIO PLAYER BAR ====== */
.podcast-player {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%) translateY(300px);
    width: 92%;
    max-width: 860px;
    /* The bar is a persistent transport control, so it outranks everything it could be opened
       "behind". At 999 it lost to the ABOUT/DEMOS/CONTACT tab modal (.crew-modal, z-index 1001,
       declared inline in index.php), which is what made it vanish when you opened DEMOS - the
       audio kept playing with no way to pause it. It also lost to .event-modal (2000), the
       gallery lightbox (10000) and the mobile nav overlay (99999).
       100002 clears .mobile-hamburger-btn (100001) and stays below #pif-dev-hud (999999), which
       is a debug overlay and must stay on top of everything.
       Overlays with bottom-anchored content are given clearance below so nothing is trapped
       underneath the bar - search "player bar clearance". */
    z-index: 100002;
    transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
    overflow: visible;
}

.podcast-player.active {
    transform: translateX(-50%) translateY(0);
}

.pcast-compact-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    /* Real glass, restrained. Blur reads as "material" (much less than the 28px this design
       replaced — at that radius everything behind turns to mush and the bar still reads as
       "covering" the site even with light passing through). Dimming via `brightness` does the
       legibility work instead: it preserves shape and motion, where blur destroys them.
       Everything that used to sit on top of this base — a top-edge sheen, a red outer glow, an
       inset highlight/shade pair, a tinted border, saturate(170%) — is deliberately gone; that
       is mid-2000s glass vocabulary (Aero gloss, neon rim, simulated bevel), and it, not the
       transparency itself, is what dated the bar. Colour belongs to the content — red appears
       only on the play button and the tag, never as a ring around the panel.
       Tint 0.38→0.30 and brightness 0.74→0.80 (owner: "조금 더 투명하게") raises the light that
       reaches the eye from ~46% to ~58% (computed: L·brightness·(1−alpha) + tintL·alpha over a
       full-white worst case). The per-title scrim below is widened to compensate so the text
       itself doesn't lose the ground this gains — only the field around it gets more transparent. */
    background: rgba(16, 16, 19, 0.30);
    backdrop-filter: blur(18px) saturate(118%) brightness(0.80);
    -webkit-backdrop-filter: blur(18px) saturate(118%) brightness(0.80);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 999px;
    padding: 8px 18px;
    /* single wide ambient shadow — it seats the bar in the page without the heavy drop that made
       it read as a candy slab pasted on top */
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.36);
    transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}

/* Without backdrop-filter there is no dimming to lean on, so those browsers keep a mostly-opaque
   bar rather than leaving white text floating over raw page content. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .pcast-compact-bar {
        background: rgba(10, 10, 12, 0.94);
    }
}

.pcast-compact-bar:hover {
    border-color: rgba(242, 17, 44, 0.7);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.9), 0 0 25px rgba(242, 17, 44, 0.4);
}

.pcast-thumb-wrap {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    overflow: hidden;
    background: #151515;
    border: 1px solid rgba(255, 255, 255, 0.15);
    flex-shrink: 0;
    cursor: pointer;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
    transition: transform 0.2s ease;
}

.pcast-thumb-wrap:hover {
    transform: scale(1.08);
}

.pcast-thumb-wrap img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.pcast-info-wrap {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Local legibility scrim. The bar is deliberately see-through, which is fine over the site's
       dark chrome but marginal when a bright event poster scrolls behind it. Darkening the whole
       bar would undo the transparency the owner asked for, so only the text strip gets extra
       separation, feathered at both ends so no hard edge shows on the glass.
       Raised 0.22→0.36 when the bar itself went more transparent (0.38→0.30 tint, 0.74→0.80
       brightness): solving L·(1−x) + scrimL·x = L_old for x keeps the luminance actually sitting
       behind the title the same as before the bar-wide change, so the field around the text reads
       more open while the text itself doesn't lose ground. (Computed, not screenshot-verified —
       the preview pane isn't compositing frames this session; worth a real device check.) */
    background: linear-gradient(90deg,
        rgba(10, 10, 13, 0) 0%,
        rgba(10, 10, 13, 0.36) 12%,
        rgba(10, 10, 13, 0.36) 86%,
        rgba(10, 10, 13, 0) 100%);
    /* The scrim needs to reach past the text for the fade to work, but must not cost the title
       any width — the negative margin gives back exactly what the padding takes, so the content
       box is unchanged and only the painted area grows. */
    padding-inline: 10px;
    margin-inline: -10px;
}

.pcast-top-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 2px;
}

.pcast-tag {
    font-size: 0.62rem;
    font-weight: 800;
    color: var(--n-8);
    letter-spacing: 1.5px;
    display: flex;
    align-items: center;
    gap: 5px;
}

.pcast-top-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 3px;
    gap: 10px;
}

.pcast-tag {
    font-size: 0.62rem;
    font-weight: 800;
    color: var(--n-8);
    letter-spacing: 1.5px;
    display: flex;
    align-items: center;
    gap: 5px;
}

.pcast-time-display {
    font-size: 0.68rem;
    color: rgba(255, 255, 255, 0.8);
    /* `tabular-nums` gives fixed-width digits in the body face, so the clock stops jittering as it
       counts without dragging in Courier — which was the one dated element in this bar. */
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    letter-spacing: 0.04em;
    font-weight: 700;
    background: rgba(0, 0, 0, 0.28);
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

.pcast-progress-container {
    width: 100%;
    height: 4px;
    background: rgba(255, 255, 255, 0.15);
    border-radius: 2px;
    margin-top: 5px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: height 0.2s ease, background 0.2s ease;
}

.pcast-progress-container:hover {
    height: 6px;
    background: rgba(255, 255, 255, 0.25);
}

.pcast-progress-bar {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, var(--neon-live), #ff4d6d);
    border-radius: 2px;
    transition: width 0.15s cubic-bezier(0.25, 1, 0.5, 1);
    box-shadow: 0 0 10px rgba(242, 17, 44, 0.75);
    will-change: width;
}

.pcast-marquee-container {
    width: 100%;
    overflow: hidden;
    white-space: nowrap;
    position: relative;
    mask-image: linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0%, black 4%, black 96%, transparent 100%);
}

.pcast-marquee-track {
    display: inline-block;
    /* Track-label treatment, not a headline: small caps size, wide open tracking, the same
       register as .pcast-tag above it ("PIF LIVE PODCAST REPLAYS") so the two lines read as one
       family instead of a heading sitting over a caption. This is the opposite move from the
       tight negative-tracking pass before it — that read as compact and dense, which on
       reflection was the wrong reference (dense/tight is a 2000s media-player habit); a current
       transport label leans spacious and quiet, closer to a boarding-pass field or a fashion
       wordmark than a song title. Weight stays 700 — only Poppins 400/700 are loaded, and the
       text is in constant motion, where 400 would blur softer against a moving backdrop. */
    font-size: 0.8rem;
    font-weight: 700;
    color: #f4f4f6;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    /* the bar is glass now, so the title needs its own contrast floor */
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75);
    white-space: nowrap;
    will-change: transform;
    padding-left: 0; /* Visible title text at start/pause state */
}

.pcast-marquee-track.playing {
    padding-left: 100%;
    animation: pcastMarquee 18s linear infinite;
    animation-play-state: running; /* Run animation ONLY when playing */
}

.pcast-marquee-track:hover {
    animation-play-state: paused;
}

@keyframes pcastMarquee {
    0% { transform: translateX(0); }
    100% { transform: translateX(-100%); }
}

.pcast-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

/* ±5s scrub. The circular arrow carries the direction and the numeral sits inside it, which is the
   convention every podcast app uses — it reads instantly and needs no label. Slightly recessed
   next to the play button so the transport hierarchy stays play > scrub > episode. */
.pcast-seek-btn {
    position: relative;
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.09);
}

/* The arrow is sized so its ring has room for a legible numeral inside — at the original 1rem the
   "5" measured 6.4px tall and 3.6px wide, which is painted but not actually readable. */
.pcast-seek-btn i {
    font-size: 1.15rem;
    line-height: 1;
}

.pcast-seek-num {
    position: absolute;
    top: 50%;
    left: 50%;
    /* optically centred inside the arrow ring, which is not vertically symmetric */
    transform: translate(-50%, -42%);
    font-size: 0.58rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0;
    pointer-events: none;
    /* inherits `color` from .pcast-btn so it always tracks the icon it sits inside */
}

/* Docked (side-rail) mode collapses to just the play button — the scrub pair goes with the
   episode buttons that are already hidden there. */
.podcast-player.docked .pcast-controls .pcast-seek-btn {
    display: none;
}

.pcast-btn {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: #fff;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 0.85rem;
    transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Restrained hover. The old state flipped the button to solid white and bounced it 10% — the
   same era as the gloss and the glow. A translucent control should stay translucent on hover and
   just gain a little presence; the scale is small enough to feel like feedback rather than a pop.
   The play button keeps its solid red because that one IS the primary action. */
.pcast-btn:hover {
    transform: scale(1.04);
    background: rgba(255, 255, 255, 0.17);
    color: #fff;
    border-color: rgba(255, 255, 255, 0.24);
}

.pcast-btn:active {
    transform: scale(0.96);
}

@media (prefers-reduced-motion: reduce) {
    .pcast-btn,
    .pcast-btn:hover,
    .pcast-btn:active {
        transform: none;
    }
}

.pcast-btn.play-main {
    background: var(--neon-live);
    color: #fff;
    width: 38px;
    height: 38px;
    border-color: var(--neon-live);
    box-shadow: 0 0 14px rgba(242, 17, 44, 0.55);
}

.pcast-btn.play-main:hover {
    background: #fff;
    color: var(--neon-live);
    box-shadow: 0 0 20px rgba(255, 255, 255, 0.8);
}

.pcast-volume-wrap {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.pcast-slider {
    width: 65px;
    accent-color: var(--neon-live);
    cursor: pointer;
    height: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.2);
}

.pcast-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.pcast-chip-btn {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.12);
    font-size: 0.9rem;
    text-decoration: none;
    transition: all 0.2s ease;
}

.pcast-chip-btn:hover {
    background: #fff;
    color: #151515;
    transform: scale(1.1);
}

.pcast-chip-btn.yt:hover {
    background: #ff0000;
    color: #fff;
    border-color: #ff0000;
}

.pcast-toggle-btn {
    background: transparent;
    border: none;
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.95rem;
    cursor: pointer;
    padding: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: color 0.2s ease, transform 0.3s ease;
}

.pcast-toggle-btn:hover {
    color: #fff;
}

.pcast-toggle-btn #pcast-chevron {
    transition: transform 0.4s ease;
}

/* Extended Container with Dual Tab Drawer */
.podcast-frame-wrapper {
    /* Flex column so the body is constrained to the wrapper's height instead of sizing to its own
       content and overflowing it. Without this the body grew to its natural 515px inside a 471px
       wrapper and the load-more footer fell outside the clip — the original cut-off. */
    display: flex;
    flex-direction: column;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transition: max-height 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease, margin 0.3s ease;
    border-radius: 14px;
    margin-top: 0;
    /* Same neutral material as the compact bar so the two read as one object — no sheen, no
       bevel here either. A step denser only because the drawer carries a scrolling list of small
       text and thumbnails, which needs more contrast than a single-line title does. */
    background: rgba(14, 14, 17, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.06);
    box-shadow: 0 10px 36px rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(20px) saturate(118%) brightness(0.66);
    -webkit-backdrop-filter: blur(20px) saturate(118%) brightness(0.66);
    position: relative;
}

#apple-podcast-iframe {
    width: 100% !important;
    height: 380px !important;
    border: none;
    display: block;
}

.podcast-player.expanded .podcast-frame-wrapper {
    /* One cap for the whole drawer; the header, the scrolling list and the pinned load-more
       footer divide it with flexbox below. It used to be a hard 400px while the children were
       capped in dvh (55dvh body, 42dvh grid) — on a 812px phone those added up to ~437px, so the
       bottom 37px, which is exactly the load-more bar, was clipped by the wrapper's
       overflow:hidden. The `min()` keeps it from swallowing the screen on a tall phone while
       `100dvh - 190px` reserves room for the compact bar above it and the bottom inset. */
    max-height: min(58dvh, calc(100dvh - 190px), 520px);
    opacity: 1;
    margin-top: 10px;
    border-color: rgba(242, 17, 44, 0.5);
    box-shadow: 0 15px 50px rgba(242, 17, 44, 0.3);
}

.podcast-player.expanded #pcast-chevron {
    transform: rotate(180deg);
}

/* Extremely Compact Docked Square Mini Player Icon (56px x 56px) */
.podcast-player.docked {
    width: 56px !important;
    height: 56px !important;
    max-width: 56px !important;
    left: auto !important;
    right: 18px !important;
    bottom: 20px !important;
    border-radius: 16px !important;
    overflow: hidden !important;
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.95), 0 0 20px rgba(242, 17, 44, 0.6) !important;
    transform: translateY(0) !important;
    border: 1px solid var(--neon-live) !important;
    background: #0a0a0a !important;
}

.podcast-player.docked .pcast-compact-bar {
    padding: 0 !important;
    width: 100% !important;
    height: 100% !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    position: relative !important;
}

.podcast-player.docked .pcast-thumb-wrap {
    position: absolute !important;
    top: 0;
    left: 0;
    width: 100% !important;
    height: 100% !important;
    border-radius: 14px !important;
    border: none !important;
    opacity: 0.35;
}

.podcast-player.docked .pcast-info-wrap,
.podcast-player.docked .pcast-controls #pcast-prev-btn,
.podcast-player.docked .pcast-controls #pcast-next-btn,
.podcast-player.docked .pcast-volume-wrap,
.podcast-player.docked .pcast-actions .pcast-chip-btn:not(.side-dock-btn),
.podcast-player.docked .pcast-actions .pcast-toggle-btn {
    display: none !important; /* Hide title & extra controls completely */
}

.podcast-player.docked .pcast-controls {
    position: relative;
    z-index: 2;
}

.podcast-player.docked .pcast-btn.play-main {
    width: 36px !important;
    height: 36px !important;
    /* Was 0.88rem. In the 56px bubble this glyph IS the whole control - it has to carry the
       play/pause distinction on its own, with no label and no surrounding chrome. */
    font-size: 1.05rem !important;
    background: var(--neon-live) !important;
    /* The second shadow is a dark separation ring. The docked bubble keeps the episode artwork
       behind the button at opacity 0.35, and a red circle straight on top of arbitrary artwork
       loses its edge; the ring restores it without adding a visible border. */
    box-shadow: 0 0 12px rgba(242, 17, 44, 0.8), 0 0 0 2px rgba(0, 0, 0, 0.55) !important;
}

/* -- Docked play/pause glyph must never vanish (fixed 2026-08-06) -------------------------
   The shared `.pcast-btn.play-main:hover` flips the button to a WHITE fill with a RED glyph.
   That reads correctly on the full bar, where the fill really does turn white. In the docked
   bubble it breaks: the rule above pins the fill to red with `!important`, so on hover the fill
   stays RED while the glyph turns RED - the icon disappears the moment the pointer touches it.
   (`-webkit-text-fill-color: var(--neon-live)` in the Part-1 lock reinforces the same red, and
   text-fill-color outranks color, so removing only the `color` line would not have been enough.)

   This is the same !important-shorthand trap already documented further down this file for the
   docked button's background - an !important shorthand beats every non-important longhand
   regardless of source order, so any rule that expects to CHANGE that fill silently loses.

   Fix: pin the glyph white in every interaction state. Specificity here is 0,4,0 / 0,5,0 against
   the shared rules' 0,3,0, so no !important is needed for the colour. */
.podcast-player.docked .pcast-btn.play-main,
.podcast-player.docked .pcast-btn.play-main:hover,
.podcast-player.docked .pcast-btn.play-main:focus,
.podcast-player.docked .pcast-btn.play-main:focus-visible,
.podcast-player.docked .pcast-btn.play-main:active {
    color: #fff;
    -webkit-text-fill-color: #fff;
}

/* Hover feedback that does not rely on a colour swap, since the fill cannot change here.
   box-shadow needs !important only because the rule above declares it !important. */
.podcast-player.docked .pcast-btn.play-main:hover {
    box-shadow: 0 0 20px rgba(242, 17, 44, 1), 0 0 0 2px rgba(0, 0, 0, 0.7) !important;
    transform: scale(1.08);
}

@media (prefers-reduced-motion: reduce) {
    .podcast-player.docked .pcast-btn.play-main:hover { transform: none; }
}

.podcast-player.docked .pcast-actions {
    position: absolute !important;
    top: 3px;
    right: 3px;
    z-index: 3;
}

.podcast-player.docked .pcast-actions .side-dock-btn {
    width: 18px !important;
    height: 18px !important;
    font-size: 0.55rem !important;
    background: rgba(0, 0, 0, 0.85) !important;
    border-color: rgba(255, 255, 255, 0.3) !important;
}

.podcast-player.docked #pcast-dock-icon {
    transform: rotate(180deg);
}

/* Single Episode Browser Drawer Header */
.pcast-drawer-header {
    padding: 12px 16px;
    /* a faint lift off the drawer rather than its own slab of colour — an opaque header would cut
       a hard band across the glass */
    background: rgba(255, 255, 255, 0.032);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 0.78rem;
    font-weight: 800;
    color: var(--n-8);
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.pcast-browser-body {
    display: flex;
    flex-direction: column;
    /* the wrapper above owns the cap now — a second one here is what let the two disagree.
       `flex: 1 1 auto` + `min-height: 0` makes this fill exactly the space the header leaves and
       shrink below its content, which is what keeps the footer inside the clip. */
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.pcast-drawer-header {
    flex: 0 0 auto;
}

.pcast-ep-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 10px;
    padding: 14px;
    /* takes whatever height is left after the header and the pinned footer, and scrolls inside it
       — so "load more" can keep appending without ever pushing anything off the bottom */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* stops the page behind from taking over the scroll when the list hits its end */
    overscroll-behavior: contain;
}

.pcast-load-more-wrap {
    /* pinned footer: never shrinks, never scrolls away */
    flex: 0 0 auto;
    padding: 10px 14px calc(14px + env(safe-area-inset-bottom, 0px)) 14px;
    display: flex;
    justify-content: center;
    background: rgba(255, 255, 255, 0.03);
    border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.pcast-load-more-btn {
    background: rgba(242, 17, 44, 0.12);
    border: 1px solid var(--neon-live);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 800;
    padding: 8px 20px;
    border-radius: 20px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    gap: 8px;
    letter-spacing: 0.8px;
}

.pcast-load-more-btn:hover {
    background: var(--neon-live);
    color: #fff;
    box-shadow: 0 0 16px rgba(242, 17, 44, 0.6);
    transform: translateY(-1px);
}

.pcast-load-more-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    border-color: rgba(255, 255, 255, 0.2);
    background: transparent;
    transform: none;
    box-shadow: none;
}

.pcast-ep-card {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 8px;
    cursor: pointer;
    transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.pcast-ep-card:hover {
    border-color: var(--neon-live);
    background: rgba(242, 17, 44, 0.1);
    transform: translateY(-2px);
}

.pcast-ep-card.active {
    border-color: var(--neon-live);
    background: rgba(242, 17, 44, 0.2);
    box-shadow: 0 0 12px rgba(242, 17, 44, 0.3);
}

.pcast-ep-thumb {
    width: 42px;
    height: 42px;
    border-radius: 6px;
    object-fit: cover;
    flex-shrink: 0;
}

.pcast-ep-info {
    flex: 1;
    min-width: 0;
}

.pcast-ep-title {
    font-size: 0.75rem;
    font-weight: 700;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pcast-ep-sub {
    font-size: 0.65rem;
    color: var(--n-8);
    font-weight: 800;
    margin-top: 2px;
}

/* Mobile Responsive Podcast Bar & Typography */
@media (max-width: 768px) {
    .podcast-player {
        bottom: calc(14px + env(safe-area-inset-bottom, 0px));
        width: 95%;
    }

    .pcast-compact-bar {
        padding: 5px 10px;
        gap: 8px;
    }

    .pcast-top-info {
        gap: 6px;
        margin-bottom: 2px;
    }

    .pcast-tag {
        font-size: 0.55rem;
        letter-spacing: 0.5px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 155px;
    }

    .pcast-time-display {
        font-size: 0.6rem;
        padding: 1px 6px;
        letter-spacing: 0.5px;
        flex-shrink: 0;
    }

    .pcast-marquee-track {
        font-size: 0.78rem;
    }

    .pcast-volume-wrap {
        display: none;
    }

    .pcast-actions .pcast-chip-btn:not(.side-dock-btn) {
        display: none;
    }

    .pcast-btn {
        width: 30px;
        height: 30px;
        font-size: 0.75rem;
    }

    .pcast-btn.play-main {
        width: 34px;
        height: 34px;
    }

    /* Five buttons now share this row. Tightening the gap and trimming the scrub pair keeps the
       transport from eating the space the title needs. */
    .pcast-controls {
        gap: 5px;
    }

    .pcast-seek-btn {
        width: 28px;
        height: 28px;
    }

    .pcast-seek-btn i {
        font-size: 1.02rem;
    }

    .pcast-seek-num {
        font-size: 0.53rem;
    }
}

@media (max-width: 480px) {
    .pcast-tag {
        max-width: 115px;
        font-size: 0.52rem;
    }

    .pcast-time-display {
        font-size: 0.58rem;
        padding: 1px 4px;
    }

}

/* Below ~540px five transport buttons squeeze the title block down to where the tag and the clock
   almost collide (measured: 133.7px of info for 127.8px of content at 481px). Episode stepping is
   what gives way — prev/next are still one tap away in the episode browser drawer, whereas ±5s
   scrubbing has no other home. */
@media (max-width: 540px) {
    #pcast-prev-btn,
    #pcast-next-btn {
        display: none;
    }

    .pcast-controls {
        gap: 6px;
    }
}

/* ====== INSTAGRAM STYLE ALL GALLERIES FEED ====== */
.content-section > *:last-child {
    margin-bottom: 140px !important;
}

.insta-feed-section {
    margin-top: 50px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 30px;
    margin-bottom: 140px !important;
}

.insta-feed-header {
    margin-bottom: 20px;
    text-align: left;
}

.insta-feed-sub {
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.5);
    font-weight: 600;
    margin-top: 4px;
    display: block;
}

.insta-feed-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 12px;
}

.insta-feed-item {
    position: relative;
    aspect-ratio: 1 / 1;
    border-radius: 10px;
    overflow: hidden;
    background: #111;
    border: 1px solid rgba(255, 255, 255, 0.08);
    cursor: pointer;
    contain: content;
    content-visibility: auto;
    contain-intrinsic-size: 160px;
    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s ease, border-color 0.3s ease;
}

.insta-feed-item:hover {
    transform: translateY(-4px) scale(1.02);
    border-color: var(--neon-party);
    box-shadow: 0 10px 30px rgba(235, 94, 40, 0.35);
}

.insta-feed-media {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.insta-feed-item:hover .insta-feed-media {
    transform: scale(1.08);
}

/* Gallery clips joined the shared preview engine on 2026-08-31, which injects its <video> with
   class `item-media party-video-media` (never `.insta-feed-media`). Two consequences to cover:

   1. Stacking. The still is now absolutely positioned at z-index 2 so the engine can fade it out
      over the video at z-index 1. `.feed-video-wrap` is position:relative with z-index:auto, so it
      forms NO stacking context and those numbers would otherwise compete directly with
      `.insta-feed-badge` (z-index 2) and `.insta-feed-overlay` (z-index 3) in the tile. It happens
      to paint correctly today purely because the badge follows the wrap in DOM order — which is
      not something to leave a layout depending on. `isolation` makes the pair local. */
.feed-video-wrap { isolation: isolate; }

/*   2. Hover parity. Without this the still would zoom on hover and the video underneath would
      not, so the moment the engine swapped them the frame would visibly snap back to 1.0. */
.insta-feed-item .party-video-media {
    object-fit: cover;
    transition: transform 0.5s ease;
}

.insta-feed-item:hover .party-video-media {
    transform: scale(1.08);
}

.insta-feed-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    /* Was rgba(0,0,0,0.75) + backdrop-filter: blur(8px). Measured 2026-07-27: this badge renders
       ~30 times in the Instagram feed, and each blur forced its own compositing layer — by far the
       heaviest GPU item on the page, which matters on the low-end phones this site targets.
       The blur is dropped and the fill raised 0.75 → 0.82 to compensate: the small amount of
       backdrop that does show through is now sharp rather than blurred, and the extra opacity
       keeps the icon just as legible over a busy photo. */
    background: rgba(0, 0, 0, 0.82);
    color: #fff;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.2);
    z-index: 2;
}

.insta-feed-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.3) 50%, transparent 100%);
    opacity: 0;
    transition: opacity 0.3s ease;
    padding: 10px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    z-index: 3;
}

.insta-feed-item:hover .insta-feed-overlay {
    opacity: 1;
}

.insta-feed-title {
    font-size: 0.75rem;
    font-weight: 700;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.insta-feed-date {
    font-size: 0.65rem;
    color: var(--neon-party);
    font-weight: 800;
    margin-top: 2px;
}

/* ====== GALLERY LIGHTBOX MODAL (ULTRA FAST LOW-END GPU OPTIMIZED) ====== */
.gallery-lightbox-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgba(5, 5, 5, 0.96);
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.gallery-lightbox-modal.active {
    display: flex;
    opacity: 1;
}

.lightbox-close-btn {
    position: absolute;
    top: 20px;
    right: 25px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: #fff;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    z-index: 10001;
}

.lightbox-close-btn:hover {
    background: #fff;
    color: #111;
    transform: scale(1.1);
}

.lightbox-nav-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(255, 255, 255, 0.12);
    border: 1px solid rgba(255, 255, 255, 0.25);
    color: #fff;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    z-index: 10001;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
}

.lightbox-nav-btn:hover {
    background: var(--neon-party);
    color: #fff;
    border-color: var(--neon-party);
    transform: translateY(-50%) scale(1.1);
    box-shadow: 0 0 20px rgba(235, 94, 40, 0.6);
}

.lightbox-prev-btn {
    left: 20px;
}

.lightbox-next-btn {
    right: 20px;
}

.lightbox-content {
    position: relative;
    max-width: 920px;
    width: 100%;
    max-height: 90vh;
    background: #0c0c0c;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.95), 0 0 40px rgba(235, 94, 40, 0.25);
}

.lightbox-media-container {
    /* `flex: 1 1 auto` + `min-height: 0` lets this shrink to whatever .lightbox-content (90vh)
       has left after the footer. The old `max-height: 70vh` fought the inline `max-height: 75vh`
       the JS used to set on the image, and since this box is overflow:hidden the extra 5vh was
       simply chopped off the bottom - the crop reported on 2026-08-23 for tall posters. */
    flex: 1 1 auto;
    min-height: min(320px, 45vh);
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    touch-action: pan-y;
    position: relative;
}

/* The whole image, always, as the resting state. Zoom only ever scales UP from here. */
.lightbox-media {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    display: block;
    transform-origin: center center;
    will-change: transform;
    user-select: none;
    -webkit-user-drag: none;
}

.lightbox-media-container.zoom-enabled { cursor: zoom-in; }
.lightbox-media-container.is-zoomed { cursor: grab; }
.lightbox-media-container.is-panning { cursor: grabbing; }

/* Zoom controls live in the footer opposite the download button. */
.lightbox-zoom-controls {
    display: none;
    align-items: center;
    gap: 6px;
}

.lightbox-media-container.zoom-enabled ~ .lightbox-footer .lightbox-zoom-controls,
.lightbox-content:has(.zoom-enabled) .lightbox-zoom-controls {
    display: flex;
}

.lightbox-zoom-btn {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.09);
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: #fff;
    font-size: 0.8rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.lightbox-zoom-btn:hover {
    background: rgba(212, 212, 212, 0.2);
    border-color: var(--neon-party);
}

#lightbox-zoom-level {
    min-width: 46px;
    text-align: center;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.5px;
    color: rgba(255, 255, 255, 0.75);
    font-variant-numeric: tabular-nums;
}

@media (max-width: 768px) {
    /* On a phone the pinch gesture is the affordance; the buttons would just eat width next to
       the download button, which is the one thing that needs room. */
    .lightbox-zoom-controls { display: none !important; }
}

.lightbox-media-container img,
.lightbox-media-container video {
    max-width: 100%;
    max-height: 70vh;
    object-fit: contain;
    display: block;
    will-change: transform;
    user-select: none;
    -webkit-user-drag: none;
}

.lightbox-media-container.dragging img,
.lightbox-media-container.dragging video {
    transition: none !important;
}

.lightbox-media-container.snap-anim img,
.lightbox-media-container.snap-anim video {
    transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1) !important;
}

.lightbox-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 24px;
    background: rgba(18, 18, 18, 0.98);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.lightbox-title {
    font-size: 0.95rem;
    font-weight: 800;
    color: #fff;
}

.lightbox-date {
    font-size: 0.75rem;
    color: var(--neon-party);
    font-weight: 700;
    margin-top: 2px;
}

.lightbox-download-btn {
    background: var(--neon-party);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 800;
    padding: 10px 22px;
    border-radius: 30px;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: all 0.2s ease;
    box-shadow: 0 0 16px rgba(235, 94, 40, 0.5);
}

.lightbox-download-btn:hover {
    background: #fff;
    color: var(--neon-party);
    transform: scale(1.05);
    box-shadow: 0 0 24px rgba(255, 255, 255, 0.8);
}

@media (max-width: 768px) {
    .insta-feed-grid {
        grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
        gap: 8px;
    }

    .lightbox-footer {
        flex-direction: column;
        gap: 12px;
        align-items: flex-start;
    }

    .lightbox-download-btn {
        width: 100%;
        justify-content: center;
    }
}

/* ====== HEADER SUB-TAB NAVIGATION ====== */
.header-nav-tabs {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
}

.nav-tab-link {
    font-size: 0.75rem;
    font-weight: 800;
    color: rgba(255, 255, 255, 0.75);
    letter-spacing: 1px;
    padding: 6px 14px;
    border-radius: 20px;
    text-decoration: none;
    transition: all 0.2s ease;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    white-space: nowrap;
}

.nav-tab-link:hover {
    color: #fff;
    background: rgba(242, 17, 44, 0.15);
    border-color: var(--neon-live);
    box-shadow: 0 0 12px rgba(242, 17, 44, 0.3);
}

@media (max-width: 1100px) and (min-width: 769px) {
    .header-nav-tabs {
        gap: 6px !important;
    }

    .nav-tab-link {
        font-size: 0.68rem !important;
        padding: 5px 10px !important;
        letter-spacing: 0.5px !important;
    }
}

/* ====== ABOUT MODAL 4-SUB TABS ====== */
.pif-about-tab-bar {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    margin-bottom: 24px;
    padding-bottom: 14px;
    width: 100%;
    box-sizing: border-box;
}

.pif-about-tab-bar::-webkit-scrollbar {
    display: none;
}

.pif-tab-btn {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.78rem;
    font-weight: 800;
    padding: 8px 16px;
    border-radius: 20px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    transition: all 0.2s ease;
    white-space: nowrap;
    flex: 0 1 auto;
    text-align: center;
}

.pif-tab-btn:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.1);
}

.pif-tab-btn.active {
    background: var(--neon-live);
    color: #fff;
    border-color: var(--neon-live);
    box-shadow: 0 0 14px rgba(242, 17, 44, 0.5);
}

@media (max-width: 600px) {
    .pif-about-tab-bar {
        gap: 8px;
    }

    .pif-tab-btn {
        font-size: 0.72rem;
        padding: 7px 10px;
        flex: 1 1 calc(50% - 8px);
        max-width: calc(50% - 8px);
        box-sizing: border-box;
    }
}

.pif-tab-content {
    display: none;
}

.pif-tab-content.active {
    display: block;
    animation: fadeInTab 0.3s ease;
}

@keyframes fadeInTab {
    from { opacity: 0; transform: translateY(6px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ABOUT INTRO */
.pif-about-intro {
    text-align: center;
    margin-bottom: 24px;
}

.pif-about-logo-2line {
    width: 240px;
    max-width: 85%;
    height: auto;
    margin-bottom: 10px;
    filter: drop-shadow(0 0 12px rgba(242, 17, 44, 0.4));
}

.pif-about-tagline {
    font-size: 0.82rem;
    color: var(--n-8);
    font-weight: 700;
    margin-top: 4px;
}

/* BRAND BIOGRAPHY CARD */
.pif-bio-card {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 14px;
    padding: 22px;
    margin-bottom: 24px;
}

.bio-title {
    font-size: 0.85rem;
    font-weight: 900;
    color: var(--n-8);
    letter-spacing: 1.5px;
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.bio-text {
    font-size: 0.9rem;
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.92);
    margin-bottom: 10px;
}

.bio-subtext {
    font-size: 0.82rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.65);
}

/* CREW & TEAM SECTION */
.crew-section-header {
    margin-bottom: 14px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 20px;
}

.crew-section-header h3 {
    font-size: 0.95rem;
    font-weight: 800;
    color: #fff;
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.crew-sub {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.5);
    display: block;
    margin-top: 2px;
}

/* ROSTER ARTISTS GRID */
.roster-header {
    text-align: center;
    margin-bottom: 20px;
}

.roster-header h2 {
    font-size: 1.2rem;
    font-weight: 800;
    color: #fff;
}

.roster-header p {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.6);
    margin-top: 4px;
}

.roster-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 16px;
    max-height: 480px;
    overflow-y: auto;
    padding: 12px 10px 16px 10px;
}

.roster-card {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    padding: 16px 14px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    transition: all 0.2s ease;
}

.roster-card:hover {
    border-color: var(--neon-live);
    transform: translateY(-4px);
    box-shadow: 0 8px 25px rgba(242, 17, 44, 0.25);
}

.roster-thumb {
    width: 70px;
    height: 70px;
    border-radius: 50%;
    object-fit: cover;
    margin-bottom: 10px;
    border: 2px solid var(--neon-live);
}

.roster-name {
    font-size: 0.9rem;
    font-weight: 800;
    color: #fff;
}

.roster-role {
    font-size: 0.72rem;
    color: var(--n-8);
    font-weight: 700;
    margin-top: 2px;
}

.roster-links {
    display: flex;
    gap: 8px;
    margin-top: 10px;
}

.roster-chip {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.85rem;
    transition: color 0.2s ease;
}

.roster-chip:hover {
    color: var(--neon-live);
}

/* DEMO SUBMISSION CARD */
.demo-sub-card {
    background: rgba(255, 255, 255, 0.03);
    border: 1px dashed rgba(242, 17, 44, 0.4);
    border-radius: 16px;
    padding: 35px 24px;
    text-align: center;
    margin: 10px 0;
}

.demo-icon-wrap {
    font-size: 2.5rem;
    color: var(--neon-live);
    margin-bottom: 12px;
}

.demo-sub-card h2 {
    font-size: 1.3rem;
    font-weight: 900;
    color: #fff;
    letter-spacing: 1.5px;
}

.demo-badge {
    display: inline-block;
    background: rgba(242, 17, 44, 0.15);
    /* Text is WHITE, not the signature red. Red-on-red measured 3.35:1 against this chip's own
       translucent red fill — under the 4.5:1 minimum for 11px text, and reported as literally
       unreadable on a phone. The red still reads loud and clear from the fill and the ring; only
       the lettering moves to white.
       This also fixes the Samsung forced-dark symptom on the same element: forced dark LIGHTENS
       dark text (red -> pink) but leaves already-light text alone, so white survives untouched.
       One change, both problems. */
    color: #fff;
    border: 1px solid var(--neon-live);
    font-size: 0.7rem;
    font-weight: 800;
    padding: 4px 14px;
    border-radius: 20px;
    margin: 10px 0 16px 0;
    letter-spacing: 1.2px;
}

.demo-sub-card p {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.75);
    line-height: 1.6;
    max-width: 500px;
    margin: 0 auto 20px auto;
}

.demo-features {
    display: flex;
    justify-content: center;
    gap: 16px;
    flex-wrap: wrap;
}

.demo-features span {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.8);
    font-weight: 700;
    display: flex;
    align-items: center;
    gap: 6px;
}

.demo-features i {
    color: var(--neon-live);
}

/* CONTACT CARD & ANTI-SPAM FORM */
.contact-card {
    padding: 10px 0;
}

.contact-header {
    text-align: center;
    margin-bottom: 20px;
}

.contact-header h2 {
    font-size: 1.2rem;
    font-weight: 800;
    color: #fff;
}

.contact-header p {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.6);
    margin-top: 4px;
}

.contact-action-box {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    padding: 16px;
    margin-bottom: 20px;
}

.contact-method {
    display: flex;
    align-items: center;
    gap: 14px;
    justify-content: space-between;
    flex-wrap: wrap;
}

.method-icon {
    width: 42px;
    height: 42px;
    background: rgba(242, 17, 44, 0.15);
    border: 1px solid var(--neon-live);
    /* White glyph, same reasoning as .demo-badge: a red icon sitting on this circle's own
       translucent red fill measured 3.84:1. That clears the 3:1 floor for non-text, but it was
       reported as hard to see on a phone, and readability beats bare spec compliance. The circle
       keeps its red fill and red ring, so the accent is unchanged — only the glyph moves to white
       (16.48:1). Also immune to Samsung forced dark, which lightens dark glyphs to pink but leaves
       already-light ones alone. */
    color: #fff;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.1rem;
    flex-shrink: 0;
}

.method-info {
    flex: 1;
    min-width: 180px;
}

.method-title {
    font-size: 0.82rem;
    font-weight: 800;
    color: #fff;
    display: block;
}

.method-email-obfuscated {
    font-size: 0.75rem;
    color: var(--n-8);
    font-family: monospace;
    font-weight: 700;
}

.contact-direct-btn {
    background: var(--neon-live);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 800;
    padding: 8px 18px;
    border-radius: 20px;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 6px;
    transition: all 0.2s ease;
    box-shadow: 0 0 12px rgba(242, 17, 44, 0.4);
}

.contact-direct-btn:hover {
    background: #fff;
    color: var(--neon-live);
    transform: scale(1.04);
}

.contact-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.contact-form .form-row {
    display: flex;
    gap: 12px;
}

.contact-input,
.contact-textarea {
    width: 100%;
    background: rgba(0, 0, 0, 0.5);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    padding: 10px 14px;
    color: #fff;
    font-size: 0.82rem;
    font-family: inherit;
    outline: none;
    transition: border-color 0.2s ease;
}

.contact-input:focus,
.contact-textarea:focus {
    border-color: var(--neon-live);
    box-shadow: 0 0 10px rgba(242, 17, 44, 0.3);
}

.contact-submit-btn {
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: #fff;
    font-size: 0.82rem;
    font-weight: 800;
    padding: 10px;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.contact-submit-btn:hover {
    background: var(--neon-live);
    border-color: var(--neon-live);
    box-shadow: 0 0 16px rgba(242, 17, 44, 0.5);
}

/* ====== HEADER RIGHT ZONE LAYOUT (PREVENT OVERLAPPING) ====== */
.header-right-zone {
    display: flex;
    align-items: center;
    gap: 20px;
}

.social-links-header {
    display: flex;
    gap: 14px;
    align-items: center;
}

/* ====== ASOT STYLE HAMBURGER & FLOATING RIGHT POPUP PANEL ====== */
.mobile-hamburger-btn {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 28px;
    height: 19px;
    background: transparent;
    border: none;
    cursor: pointer;
    z-index: 100001;
    padding: 0;
    margin-left: 6px;
}

.mobile-hamburger-btn span {
    width: 100%;
    height: 2.5px;
    background: #fff;
    border-radius: 4px;
    transition: all 0.3s ease;
    transform-origin: center;
}

.mobile-hamburger-btn:hover span {
    /* colour change only, no glow — matches the player bar's restraint: the red marks the
       interactive state, it doesn't need a halo to prove it */
    background: var(--neon-live);
}

.mobile-hamburger-btn.active span:nth-child(1) {
    transform: translateY(8px) rotate(45deg);
    background: var(--neon-live);
}

.mobile-hamburger-btn.active span:nth-child(2) {
    opacity: 0;
}

.mobile-hamburger-btn.active span:nth-child(3) {
    transform: translateY(-8px) rotate(-45deg);
    background: var(--neon-live);
}

/* FLOATING RIGHT-SIDE ASOT POPUP DRAWER
   Same glass material as the podcast player (see .pcast-compact-bar / .podcast-frame-wrapper):
   near-opaque 0.96 slab + a red glow ring replaced with a neutral translucent tint, restrained
   hairline border, and one soft ambient shadow — no colour halo. Density matches the podcast
   drawer rather than the leaner compact bar, since this panel is also mostly text (nav links)
   that benefits from the same higher contrast floor. */
.mobile-nav-overlay {
    position: fixed;
    top: 75px;
    right: 25px;
    width: 340px;
    max-width: 92vw;
    max-height: 82vh;
    z-index: 99999;
    background: rgba(14, 14, 17, 0.55);
    backdrop-filter: blur(20px) saturate(118%) brightness(0.66);
    -webkit-backdrop-filter: blur(20px) saturate(118%) brightness(0.66);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 18px;
    box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
    display: none;
    flex-direction: column;
    padding: 22px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(-10px) scale(0.96);
    transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
    overflow: hidden; /* border-radius 내부 클리핑 보장 */
    box-sizing: border-box;
}

/* Without backdrop-filter, fall back to a mostly-opaque panel rather than leaving nav text
   floating over raw page content — same reasoning as the podcast player's fallback. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .mobile-nav-overlay {
        background: rgba(10, 10, 10, 0.96);
    }
}

.mobile-nav-overlay.active {
    display: flex;
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0) scale(1);
}

.mobile-nav-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* One size for BOTH layers of the Samsung sigfix build (tile + letter overlay below). The overlay
   used to hardcode 42px; when the phone tile shrank to 36px the white letters overshot to the
   right (owner screenshot, Samsung Internet only - other browsers draw the SVG as one image). */
.mobile-nav-header {
    --pif-navlogo: 42px;
    --pif-navpad: 20px;
    padding-bottom: var(--pif-navpad);
}
.mobile-nav-logo {
    width: var(--pif-navlogo);
    height: var(--pif-navlogo);
    flex: none;
}

.mobile-nav-close {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: #fff;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.mobile-nav-body {
    flex: 1;
    overflow-y: auto;
    padding: 25px 0;
    display: flex;
    flex-direction: column;
    gap: 28px;
    /* Firefox */
    scrollbar-width: thin;
    scrollbar-color: #f2112c rgba(255, 255, 255, 0.04);
}

/* Webkit (Chrome, Safari, iOS) */
.mobile-nav-body::-webkit-scrollbar {
    width: 4px;
}

.mobile-nav-body::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.04);
    border-radius: 10px;
}

.mobile-nav-body::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, #f2112c 0%, #ff4d6d 100%);
    border-radius: 10px;
}

.mobile-nav-body::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(180deg, #ff2a45 0%, #ff6b85 100%);
}

.mobile-nav-group {
    display: flex;
    flex-direction: column;
}

.mobile-group-label {
    font-size: 0.68rem;
    font-weight: 900;
    color: var(--n-8);
    letter-spacing: 2px;
    margin-bottom: 12px;
}

.mobile-nav-item {
    font-size: 1.15rem;
    font-weight: 800;
    color: rgba(255, 255, 255, 0.88);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
    transition: all 0.2s ease;
}

.mobile-nav-item i {
    font-size: 1.1rem;
    color: var(--neon-live);
    width: 24px;
    text-align: center;
}

.mobile-nav-item:hover,
.mobile-nav-item:active {
    color: var(--neon-live);
    padding-left: 8px;
}

.mobile-nav-footer {
    padding-top: 25px;
    padding-bottom: 15px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    text-align: center;
}

.mobile-social-row {
    display: flex;
    justify-content: center;
    gap: 24px;
    margin-bottom: 18px;
}

.mobile-social-row a {
    color: rgba(255, 255, 255, 0.7);
    font-size: 1.3rem;
    transition: color 0.2s ease;
}

.mobile-social-row a:hover {
    color: var(--neon-live);
}

.mobile-nav-footer p {
    font-size: 0.65rem;
    color: rgba(255, 255, 255, 0.4);
    letter-spacing: 1px;
    margin: 4px 0;
}

@media (max-width: 768px) {
    .desktop-only {
        display: none !important;
    }

    .mobile-hamburger-btn {
        display: flex;
    }

    /* 모바일: top~bottom 제약으로 플레이어바 위까지만, 테두리 잘림 방지 */
    .mobile-nav-overlay {
        top: 68px;
        bottom: calc(100px + env(safe-area-inset-bottom, 0px));
        right: 12px;
        width: calc(100% - 24px);
        max-width: 390px;
        max-height: none; /* top+bottom이 높이 결정 */
        overflow: hidden;
        box-sizing: border-box;
    }

    /* 내부 스크롤 영역이 남은 공간 다 채우도록 */
    .mobile-nav-overlay .mobile-nav-body {
        flex: 1;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        min-height: 0; /* flexbox에서 flex:1 overflow 올바르게 동작 */
    }

    /* 모바일: 드로어 내 하단 푸터 — 슬림 & 초미니멀 설계로 본문 스크롤 영역 극대화 */
    .mobile-nav-footer {
        padding-top: 8px !important;
        padding-bottom: 6px !important;
        border-top: 1px solid rgba(255, 255, 255, 0.08) !important;
        flex-shrink: 0 !important; /* 축소되지 않고 내용만큼만 최소 차지 */
    }

    .mobile-social-row {
        gap: 16px !important;
        margin-bottom: 4px !important;
    }

    .mobile-social-row a {
        font-size: 0.95rem !important;
    }

    .mobile-nav-footer p {
        font-size: 0.55rem !important;
        letter-spacing: 0.5px !important;
        margin: 1px 0 !important;
        line-height: 1.2 !important;
        opacity: 0.6;
    }

    .mobile-nav-footer .mobile-footer-credit {
        font-size: 0.52rem !important;
        margin-top: 0 !important;
        opacity: 0.5;
    }
}

/* ====== PIF DEV MONITOR HUD (SECRET LIVE NETWORK & MEDIA INSPECTOR) ====== */
#pif-dev-hud {
    position: fixed;
    bottom: 20px;
    left: 20px;
    z-index: 999999;
    width: 320px;
    background: rgba(10, 12, 18, 0.92);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid rgba(0, 242, 254, 0.4);
    box-shadow: 0 12px 35px rgba(0, 0, 0, 0.8), 0 0 20px rgba(0, 242, 254, 0.25);
    border-radius: 14px;
    color: #fff;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: 0.78rem;
    overflow: hidden;
    transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    user-select: none;
}

#pif-dev-hud.collapsed {
    width: 180px;
}

#pif-dev-hud.collapsed .pif-hud-body {
    display: none;
}

.pif-hud-header {
    background: linear-gradient(90deg, rgba(0, 242, 254, 0.2), rgba(79, 172, 254, 0.1));
    padding: 10px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid rgba(0, 242, 254, 0.25);
    cursor: pointer;
}

.pif-hud-title {
    font-weight: 800;
    font-size: 0.75rem;
    letter-spacing: 1px;
    color: var(--n-8);
    display: flex;
    align-items: center;
    gap: 8px;
}

.pif-hud-controls {
    display: flex;
    gap: 6px;
}

.pif-hud-btn {
    background: rgba(255, 255, 255, 0.1);
    border: none;
    color: #ccc;
    width: 22px;
    height: 22px;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 0.7rem;
    transition: all 0.2s;
}

.pif-hud-btn:hover {
    background: rgba(0, 242, 254, 0.4);
    color: #fff;
}

.pif-hud-body {
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 440px;
    overflow-y: auto;
}

.pif-hud-stat-box {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 10px;
}

.pif-hud-stat-label {
    font-size: 0.66rem;
    color: rgba(255, 255, 255, 0.55);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    margin-bottom: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.pif-hud-stat-value {
    font-size: 1.15rem;
    font-weight: 900;
    color: #fff;
    font-family: 'Courier New', monospace;
}

.pif-hud-savings-badge {
    display: inline-block;
    background: rgba(212, 212, 212, 0.2);
    color: var(--n-8);
    border: 1px solid rgba(212, 212, 212, 0.4);
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.65rem;
    font-weight: 800;
}

.pif-hud-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.pif-hud-mini-stat {
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 6px;
    padding: 8px;
}

.pif-hud-mini-val {
    font-size: 0.95rem;
    font-weight: 800;
    color: var(--n-8);
    font-family: 'Courier New', monospace;
}

.pif-hud-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: rgba(0, 0, 0, 0.35);
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 0.72rem;
    font-weight: 700;
}

.pif-media-inspect-badge {
    position: absolute;
    top: 6px;
    left: 6px;
    z-index: 99999;
    background: rgba(10, 12, 18, 0.92);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(0, 242, 254, 0.6);
    color: var(--n-8);
    padding: 3px 7px;
    border-radius: 4px;
    font-family: 'Courier New', monospace;
    font-size: 0.65rem;
    font-weight: 800;
    pointer-events: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
}

/* ====== MERCHANDISE & STORE STYLING ====== */
.merch-hero-banner {
    text-align: center;
    background: linear-gradient(135deg, rgba(212, 212, 212, 0.08) 0%, rgba(242, 17, 44, 0.05) 100%);
    border: 1px solid rgba(212, 212, 212, 0.2);
    border-radius: 16px;
    padding: 24px 20px;
    margin-bottom: 24px;
    position: relative;
    overflow: hidden;
}

.merch-hero-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.68rem;
    font-weight: 900;
    letter-spacing: 2px;
    color: #D4D4D4;
    background: rgba(212, 212, 212, 0.15);
    border: 1px solid rgba(212, 212, 212, 0.3);
    padding: 4px 12px;
    border-radius: 20px;
    margin-bottom: 10px;
    text-transform: uppercase;
}

.merch-hero-banner h2 {
    font-size: 1.4rem;
    font-weight: 900;
    letter-spacing: 2px;
    color: #fff;
    margin-bottom: 4px;
    text-transform: uppercase;
}

.merch-hero-sub-en {
    font-size: 0.85rem;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.8);
    margin-bottom: 2px;
}

.merch-hero-sub-kr {
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.55);
    margin-bottom: 16px;
}

.merch-notice-box {
    background: rgba(0, 0, 0, 0.4);
    border: 1px dashed rgba(212, 212, 212, 0.3);
    border-radius: 12px;
    padding: 14px 18px;
    max-width: 680px;
    margin: 0 auto;
    text-align: center;
}

.notice-tag {
    font-size: 0.72rem;
    font-weight: 800;
    color: #D4D4D4;
    letter-spacing: 1px;
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.notice-desc-kr {
    font-size: 0.82rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.85);
    margin: 0 0 6px 0;
    word-break: keep-all;
    overflow-wrap: break-word;
}

.notice-desc-en {
    font-size: 0.72rem;
    line-height: 1.4;
    color: rgba(255, 255, 255, 0.5);
    margin: 0;
    word-break: keep-all;
    overflow-wrap: break-word;
}

/* Merch Cards Grid */
.merch-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 20px;
    width: 100%;
}

.merch-card {
    background: rgba(20, 20, 20, 0.85);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 14px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    position: relative;
    transition: all 0.3s ease;
}

.merch-card:hover {
    transform: translateY(-4px);
    border-color: rgba(212, 212, 212, 0.4);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.6), 0 0 20px rgba(212, 212, 212, 0.12);
}

.merch-card-badge {
    position: absolute;
    top: 12px;
    left: 12px;
    z-index: 2;
    background: rgba(212, 212, 212, 0.9);
    color: #000;
    font-size: 0.6rem;
    font-weight: 900;
    letter-spacing: 1px;
    padding: 3px 8px;
    border-radius: 4px;
    text-transform: uppercase;
}

.merch-img-box {
    width: 100%;
    height: 170px;
    background: rgba(10, 10, 10, 0.9);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    box-sizing: border-box;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.merch-icon-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
}

.merch-icon-svg {
    height: 44px;
    opacity: 0.85;
    filter: drop-shadow(0 0 10px rgba(212, 212, 212, 0.4));
    transition: transform 0.3s ease;
}

.merch-card:hover .merch-icon-svg {
    transform: scale(1.1) rotate(5deg);
    opacity: 1;
}

.merch-cat-icon {
    font-size: 1.2rem;
    color: rgba(255, 255, 255, 0.4);
}

.merch-info {
    padding: 18px;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.merch-category {
    font-size: 0.65rem;
    font-weight: 800;
    letter-spacing: 1.5px;
    color: var(--neon-party, #D4D4D4);
    text-transform: uppercase;
    margin-bottom: 6px;
    display: block;
}

.merch-title {
    font-size: 1.02rem;
    font-weight: 800;
    color: #fff;
    line-height: 1.25;
    margin: 0 0 2px 0;
    letter-spacing: 0.5px;
}

.merch-title-kr {
    font-size: 0.8rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.65);
    margin-bottom: 12px;
    display: block;
}

.merch-desc {
    font-size: 0.78rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.8);
    margin: 0 0 16px 0;
    background: rgba(0, 0, 0, 0.25);
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid rgba(255, 255, 255, 0.04);
    flex: 1;
}

.desc-en {
    display: block;
    font-size: 0.7rem;
    color: rgba(255, 255, 255, 0.45);
    margin-top: 4px;
    font-style: italic;
}

.merch-price-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.06);
    padding: 10px 12px;
    border-radius: 8px;
    margin-bottom: 14px;
}

.price-box {
    display: flex;
    flex-direction: column;
}

.price-label {
    font-size: 0.55rem;
    font-weight: 800;
    letter-spacing: 1.5px;
    color: rgba(255, 255, 255, 0.4);
}

.merch-price {
    font-size: 1.15rem;
    font-weight: 900;
    color: #D4D4D4;
    text-shadow: 0 0 10px rgba(212, 212, 212, 0.3);
}

.merch-status-pill {
    font-size: 0.58rem;
    font-weight: 800;
    letter-spacing: 0.5px;
    color: rgba(255, 255, 255, 0.5);
    background: rgba(255, 255, 255, 0.06);
    padding: 4px 8px;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.08);
}

.merch-buy-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    background: linear-gradient(135deg, rgba(212, 212, 212, 0.15), rgba(242, 17, 44, 0.1));
    border: 1px solid rgba(212, 212, 212, 0.4);
    color: #D4D4D4;
    font-size: 0.74rem;
    font-weight: 800;
    letter-spacing: 1px;
    padding: 11px;
    border-radius: 10px;
    text-decoration: none;
    transition: all 0.25s ease;
    box-sizing: border-box;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

.merch-buy-btn:hover {
    background: #D4D4D4;
    color: #000;
    box-shadow: 0 0 16px rgba(212, 212, 212, 0.5);
    border-color: #D4D4D4;
}

/* ==========================================================================
   ULTRA-FLUID APPLE IOS HARDWARE GPU ACCELERATION FOR VIDEO & CARDS
   ========================================================================== */
.gig-card,
.gateway-card,
.media-preview,
.gig-card video,
.gateway-card video,
.media-preview video {
    will-change: transform, opacity;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000px;
    perspective: 1000px;
    -webkit-transform-style: preserve-3d;
    transform-style: preserve-3d;
}

.gig-card video,
.gateway-card video,
.media-preview video {
    object-fit: cover;
    pointer-events: none;
    -webkit-user-select: none;
    user-select: none;
}

/* PREVENT HOVER FLICKERING ON CARD INNER ELEMENTS */
.gateway-card > *:not(.gateway-bg),
.gig-card > *:not(.gig-card-media) {
    pointer-events: none;
}
.gateway-card,
.gig-card {
    pointer-events: auto;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   SIGNATURE COLOUR LOCK — Samsung Internet "어둡게 보기"
   Samsung runs a forced-dark pass AFTER the cascade, so `color-scheme: only dark` alone does not
   always stop it: it recomputes `color` and `background-color` and washes the PIF red out.
   That pass does NOT recompute `-webkit-text-fill-color`, and it skips `background-image`, so we
   restate the identical colour through those two properties to pin it.
   Rendering is unchanged everywhere else — each gradient is a single flat colour.
   OPAQUE fills only: layering a translucent red over the same background-color would compound
   the alpha and darken the tint, so rgba()/shadow-var backgrounds are intentionally left alone.
   Regenerate if the signature colours change.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

/* text */
#live h2 { -webkit-text-fill-color: var(--neon-live); }
.show-title { -webkit-text-fill-color: var(--neon-live); }
/* .pif-tag — 미러에서 제외. 글자를 #fff 로 바꿨는데(빨강 틴트 위 빨강이었음 (Lc 31.6)) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
/* .pif-schedule-tag.regular — 미러에서 제외. 글자를 #fff 로 바꿨는데(빨강 틴트 위 빨강이었음 (Lc 31.3)) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
.date-main-text i { -webkit-text-fill-color: var(--neon-live); }
/* .day-highlight — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(14px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
#countdown { -webkit-text-fill-color: var(--neon-live); }
/* .world-row.highlight — 미러에서 제외. 밝기 강조로 바꿨습니다. */
/* .world-row.highlight .w-time — 미러에서 제외. 밝기 강조로 바꿨습니다. */
/* .pcast-tag — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(10px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
.pcast-btn.play-main:hover { -webkit-text-fill-color: var(--neon-live); }
/* .pcast-drawer-header — 미러에서 제외. 12.5px 라 글자를 var(--n-8) 로 바꿨는데,
   -webkit-text-fill-color 가 color 보다 우선해 여기 남기면 삼성에서만 빨강이 남습니다. */
/* .pcast-ep-sub — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(10px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
/* .pif-about-tagline — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(13px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
/* .bio-title — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(13.6px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
/* .roster-role — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(12px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
.roster-chip:hover { -webkit-text-fill-color: var(--neon-live); }
.demo-icon-wrap { -webkit-text-fill-color: var(--neon-live); }
/* .demo-badge intentionally NOT listed here — its lettering is white now (see the rule above,
   changed for contrast: red-on-red measured 3.35:1 and was unreadable on a phone). Pinning the red
   fill here would win over `color: #fff`, because -webkit-text-fill-color takes precedence over
   color, and would silently undo that fix. */
.demo-features i { -webkit-text-fill-color: var(--neon-live); }
/* .method-icon intentionally NOT listed here — its glyph is white now (contrast fix above).
   -webkit-text-fill-color outranks color, so pinning red here would silently undo it. */
/* .method-email-obfuscated — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(12px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
.contact-direct-btn:hover { -webkit-text-fill-color: var(--neon-live); }
/* .mobile-group-label — 미러에서 제외. 글자를 var(--n-8) 로 바꿨는데(11px) -webkit-text-fill-color 가
   color 보다 우선해서, 여기 남겨 두면 삼성 인터넷에서만 빨강이 되살아납니다. */
.mobile-nav-item i { -webkit-text-fill-color: var(--neon-live); }
.mobile-nav-item:hover, .mobile-nav-item:active { -webkit-text-fill-color: var(--neon-live); }
.mobile-social-row a:hover { -webkit-text-fill-color: var(--neon-live); }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   PART 1b — THE TEXT RULES ABOVE DO NOT WORK. MEASURED 2026-07-26.

   On device (Samsung Internet 30.0 / Chrome 143) `-webkit-text-fill-color` renders IDENTICALLY to
   plain `color`: both wash out to pale pink. The diagnostic page put the two side by side and they
   matched exactly, which is why the mobile menu's icons and labels stayed pink through several
   rounds of "fixes" — the entire list above was a no-op the whole time. It is left in place only
   because it is harmless (the value equals the `color` it sits beside) and may still do something
   on older Samsung builds; nothing new should be added to it.

   What forced dark actually does: it LIGHTENS dark text (red #f2112c → pink) and DARKENS light
   backgrounds. Red *backgrounds* are dark enough to pass untouched — which is the escape route.
   `background-clip: text` paints the glyphs from a background layer, so the text inherits the
   background pipeline's immunity. Measured: the only text technique that survives.

   Applied only to text that owns NO background of its own. `background-clip: text` clips the
   element's whole background to the glyphs, so running it on a chip/badge/button would delete that
   chip's fill. Those are listed as exclusions further down.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

html[data-sigfix="1"] #live h2,
html[data-sigfix="1"] .show-title,
html[data-sigfix="1"] .date-main-text i,
html[data-sigfix="1"] .day-highlight,
html[data-sigfix="1"] .world-row.highlight,
html[data-sigfix="1"] .world-row.highlight .w-time,
html[data-sigfix="1"] .pcast-tag,
html[data-sigfix="1"] .pif-about-tagline,
html[data-sigfix="1"] .bio-title,
html[data-sigfix="1"] .roster-role,
html[data-sigfix="1"] .roster-chip:hover,
html[data-sigfix="1"] .demo-features i,
html[data-sigfix="1"] .method-email-obfuscated,
html[data-sigfix="1"] .mobile-group-label,
html[data-sigfix="1"] .mobile-nav-item i,
html[data-sigfix="1"] .mobile-nav-item:hover,
html[data-sigfix="1"] .mobile-nav-item:active,
html[data-sigfix="1"] .mobile-social-row a:hover {
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

/* `.pcast-drawer-header` also carries a 3.2% white wash, which the clip would eat. The wash is
   re-delivered as a spread inset shadow — shadows are painted outside the background pipeline the
   clip consumes, so both survive together. */
html[data-sigfix="1"] .pcast-drawer-header {
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    box-shadow: inset 0 0 0 100vmax rgba(255, 255, 255, 0.032);
}

/* These two hover states flip to a WHITE fill with red text. Forced dark darkens white, so on
   device that lands as pink text on a grey pill — worse than not flipping at all. Under Samsung
   only, keep the red fill and use white text, which forced dark leaves alone because it is already
   light. Other browsers keep the original white-flip untouched. */
html[data-sigfix="1"] .pcast-btn.play-main:hover,
html[data-sigfix="1"] .contact-direct-btn:hover {
    background-color: transparent;
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    color: #fff;
    -webkit-text-fill-color: #fff;
}

/* DELIBERATELY NOT CONVERTED — each would break if clipped, verified individually:
     #countdown          — script.js injects <span style="color:…" + a blink keyframe> for the ON
                           AIR state; the animated span forms its own painting group and would
                           inherit `-webkit-text-fill-color: transparent`, i.e. go invisible while
                           actually live. Worst possible moment to fail.
     .demo-icon-wrap     — its only glyph is a permanently `fa-spin` rotated child; a clipped
                           gradient is painted in the parent's static space and cannot follow it.
     .pcast-ep-sub       — script.js injects `style="color:#fff"` on this label; the clip would
                           override that inline white and repaint a deliberately white string red.
     .pif-tag, .pif-schedule-tag.regular, .demo-badge, .method-icon
                         — real chips/pills whose translucent red fill and red ring are load-bearing
                           (already delivered as padding-box/border-box gradients by PART 4, which
                           DOES survive). Their text stays pink under forced dark; on a red chip at
                           these small sizes that is far less conspicuous than pink on near-black.
                           An inset-box-shadow swap could free the background for a text clip, but
                           that is unverified on device — do not ship it without measuring first. */

/* opaque fills */
#global-section-close-btn.theme-live:hover { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.live-dot { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.plat-chip:hover { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.video-badge { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.archive-scroll::-webkit-scrollbar-thumb:hover { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.pcast-btn.play-main { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.podcast-player.docked .pcast-btn.play-main { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.pcast-load-more-btn:hover { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.pif-tab-btn.active { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.contact-direct-btn { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.contact-submit-btn:hover { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.mobile-hamburger-btn:hover span { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.mobile-hamburger-btn.active span:nth-child(1) { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }
.mobile-hamburger-btn.active span:nth-child(3) { background-image: linear-gradient(var(--neon-live), var(--neon-live)); }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   SIGNATURE COLOUR LOCK — PART 2: IMAGES (Samsung Internet only)

   The block above pins TEXT (`-webkit-text-fill-color`) and BACKGROUNDS (flat `linear-gradient`).
   Neither reaches an `<img>`, which is why the red logos still washed out to pale pink in Samsung's
   "어둡게 보기" while everything else held — Samsung's forced-dark pass re-tints image PIXELS.

   Fix: stop the colour coming from pixels. Every PIF logo is a single flat #F2112C SVG with no
   gradient and no raster (verified across all 9 files), so the shape can be carried by a `mask`
   while the colour is painted by the same flat `linear-gradient` that is already proven immune.
   `content:` swaps the element's own bitmap for a 1×1 transparent GIF so no image pixel is painted
   at all; `aspect-ratio` (taken from each SVG's viewBox) replaces the intrinsic ratio that the
   swap discards, so the layout box is unchanged.

   Gated on `html[data-sigfix="1"]` (set by a UA check in index.php's <head>) because the repaint
   costs the `drop-shadow` glow — a filter is applied before the mask, so a glow cast by the
   gradient RECTANGLE gets clipped away to nothing. Correct colour beats the glow under forced
   dark; every other browser renders these logos correctly today and is left completely alone.

   On-device A/B without a rebuild:  ?sigfix=1  forces on,  ?sigfix=0  forces off.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

/* Shared: stop the element painting its own bitmap. `content:` swaps the <img>'s raster for a
   1×1 transparent GIF, so nothing goes through the image pipeline Samsung re-tints. The colour is
   then supplied by CSS below — either a flat gradient through a mask (single-colour art) or the
   SVG itself as a background (multi-colour art). */
html[data-sigfix="1"] .logo img,
html[data-sigfix="1"] .mobile-nav-logo,
html[data-sigfix="1"] .merch-icon-svg,
html[data-sigfix="1"] .gateway-logo,
html[data-sigfix="1"] .show-logo,
html[data-sigfix="1"] .pif-about-logo-2line {
    content: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
    /* a filter is applied before the mask, so a glow cast by the gradient rectangle is clipped
       off to nothing — drop it rather than pay for something that cannot be seen */
    filter: none;
}

/* ── Single-colour art: flat gradient + SVG as mask ──────────────────────────────────────────
   These files are pure #F2112C with no second colour, so a mask loses nothing and the colour
   comes from the gradient, which Samsung's pass does not recompute. */
html[data-sigfix="1"] .gateway-logo[src*="PIF_Music"],
html[data-sigfix="1"] .gateway-logo[src*="PIF_Live"],
html[data-sigfix="1"] .pif-about-logo-2line,
html[data-sigfix="1"] .show-logo {
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
}

html[data-sigfix="1"] .gateway-logo[src*="PIF_Music"],
html[data-sigfix="1"] .pif-about-logo-2line {
    -webkit-mask-image: url("../images/pif-logos/PIF_Music/PIF_Music.svg");
            mask-image: url("../images/pif-logos/PIF_Music/PIF_Music.svg");
}
html[data-sigfix="1"] .gateway-logo[src*="PIF_Live"] {
    -webkit-mask-image: url("../images/pif-logos/PIF_Live/PIF_Live.svg");
            mask-image: url("../images/pif-logos/PIF_Live/PIF_Live.svg");
}
html[data-sigfix="1"] .show-logo {
    -webkit-mask-image: url("../images/pif-logos/PIF_Live/PIF_1LINE_Live.svg");
            mask-image: url("../images/pif-logos/PIF_Live/PIF_1LINE_Live.svg");
    aspect-ratio: 4026.93 / 260;
}
html[data-sigfix="1"] .gateway-logo,
html[data-sigfix="1"] .pif-about-logo-2line {
    aspect-ratio: 1892.87 / 665.16;
}

/* ── Multi-colour art: TWO STACKED MASK LAYERS ───────────────────────────────────────────────
   MEASURED ON DEVICE 2026-07-26 (Samsung Internet 30.0 / Chrome 143), via darkmode-test.html.
   That test settled what four rounds of guessing could not:

     survives  →  every CSS colour property tested: color, background-color, border-color,
                  box-shadow, outline-color, accent-color, and mask+gradient on SVG
     REPAINTED →  <img src=svg>, background-image:url(svg), and inline <svg fill="…">

   So the ONLY thing Samsung re-tints on this site is artwork delivered as image pixels, and the
   only technique that survives for artwork is mask + flat gradient (the colour then comes from
   CSS, and the SVG contributes shape/alpha only).

   The previous attempt here used `background-image: url(svg)` for the two-colour art. The test
   proves that FAILS — it fixed the earlier "뭉개짐" (the icon collapsing to a plain red square)
   but never stopped the wash-out, which is why the top-left icon still looked wrong.

   A single mask can only carry one colour, so the two-colour icon is rebuilt as two registered
   layers: the tile (red) painted by the element itself, and the "PIF" letterforms (white) painted
   by a pseudo-element on its PARENT — pseudo-elements do not render on a replaced <img>, so they
   have to hang off the container. Both masks share PIF_Icon.svg's 100×100 viewBox, so they line
   up exactly at any size. Split shapes: PIF_Icon_square.svg + PIF_Icon_letters.svg. */

html[data-sigfix="1"] .logo img,
html[data-sigfix="1"] .mobile-nav-logo,
html[data-sigfix="1"] .merch-icon-svg {
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    -webkit-mask-image: url("../images/pif-logos/PIF_Icon_square.svg");
            mask-image: url("../images/pif-logos/PIF_Icon_square.svg");
    aspect-ratio: 1 / 1;
}

/* letter layer — white, registered on top of the tile above */
html[data-sigfix="1"] .logo,
html[data-sigfix="1"] .mobile-nav-header,
html[data-sigfix="1"] .merch-icon-placeholder {
    position: relative;
}

/* `.logo` is an inline <a>, so its line box is taller than the 20/26px icon inside it (measured
   27.9px vs 20px) — `inset: 0` would then stretch the letter layer over that extra descender space
   and slide it out of register with the tile. inline-flex makes the anchor shrink-wrap the icon
   exactly, at every breakpoint, without touching the header's own flex layout. */
html[data-sigfix="1"] .logo {
    display: inline-flex;
    align-items: center;
}

html[data-sigfix="1"] .logo::after,
html[data-sigfix="1"] .mobile-nav-header::after,
html[data-sigfix="1"] .merch-icon-placeholder::after {
    content: '';
    position: absolute;
    pointer-events: none;
    background-image: linear-gradient(#fff, #fff);
    -webkit-mask: url("../images/pif-logos/PIF_Icon_letters.svg") center / contain no-repeat;
            mask: url("../images/pif-logos/PIF_Icon_letters.svg") center / contain no-repeat;
}

/* `.logo` is an <a> wrapping nothing but the icon, so it shrink-wraps and inset:0 registers
   exactly — no hardcoded size needed, and it stays correct at the 26px/20px breakpoints.
   The opacity mirrors `.logo img`'s own 0.7 (and its 1 on hover) — without this the letter layer
   would sit at full strength over a dimmed tile and the two halves of one logo would disagree. */
html[data-sigfix="1"] .logo::after {
    inset: 0;
    opacity: 0.7;
    transition: opacity 0.3s ease;
}

html[data-sigfix="1"] .logo:hover::after {
    opacity: 1;
}

/* These two containers hold siblings besides the icon, so the overlay is pinned to the icon's own
   box instead of the container's: the nav logo is the first flex item on the left (42×42), and the
   merch icon is the first item in a centred flex column (44×44, with a <i> below it). */
html[data-sigfix="1"] .mobile-nav-header::after {
    /* Registered to the tile: same size variable, and centred the way the flex row centres the
       tile (the close button is taller, so the tile does not sit at top:0). */
    top: 0;
    bottom: var(--pif-navpad);
    margin-block: auto;
    left: 0;
    width: var(--pif-navlogo);
    height: var(--pif-navlogo);
}

html[data-sigfix="1"] .merch-icon-placeholder::after {
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 44px;
    height: 44px;
    opacity: 0.85; /* matches .merch-icon-svg's own opacity so both layers stay in step */
}

/* PIF_Party.svg also carries white sub-text, but splitting it would mean regenerating a 6 KB
   multi-path wordmark. Masking it whole keeps the signature red correct — the trade is that the
   small white sub-text renders red too. Correct colour on the main wordmark is worth more than the
   sub-text's tint here; revisit with a split file if it reads badly on device. */
html[data-sigfix="1"] .gateway-logo[src*="PIF_Party"] {
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    -webkit-mask-image: url("../images/pif-logos/PIF_Party/PIF_Party.svg");
            mask-image: url("../images/pif-logos/PIF_Party/PIF_Party.svg");
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   SIGNATURE COLOUR LOCK — PART 3: TRANSLUCENT FILLS AND BORDERS
   (kept where it was; PART 4 below is the sitewide sweep for the same pattern)

   Part 1 pinned opaque fills and text; it deliberately skipped `rgba()` fills, because layering a
   translucent gradient on top of the same `background-color` compounds the alpha and darkens the
   tint. It also left `border-color` alone entirely. Those two gaps are most of the red in the PIF
   LIVE tab — episode cards, the load-more button and the active tab all colour themselves with a
   translucent red fill and a red border — which is why that tab still shifted under forced dark
   after the rest of the site held.

   Both are fixed with one declaration per element: paint the fill into the *padding box* and the
   border colour into the *border box*, and set the real `background-color`/`border-color` to
   transparent so nothing compounds. Everything visible is then a `background-image`, which the
   forced-dark pass does not recompute — and unlike `border-image`, this technique still follows
   `border-radius`.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

html[data-sigfix="1"] .pcast-ep-card:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.10), rgba(242, 17, 44, 0.10)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

html[data-sigfix="1"] .pcast-ep-card.active {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.20), rgba(242, 17, 44, 0.20)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

html[data-sigfix="1"] .pcast-load-more-btn {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.12), rgba(242, 17, 44, 0.12)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* hover swaps to the solid fill; the border stays pinned the same way */
html[data-sigfix="1"] .pcast-load-more-btn:hover,
html[data-sigfix="1"] .pif-tab-btn.active {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* the play button is the one red control that sits outside the LIVE tab too */
html[data-sigfix="1"] .pcast-btn.play-main {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   SIGNATURE COLOUR LOCK — PART 4: SITEWIDE BORDER SWEEP (2026-07-26)

   Owner's actual ask: stop chasing individual reports one at a time — the site is fully dark
   already, forced-dark shouldn't be touching it at all. `color-scheme: only dark` is set both as
   a <meta> tag AND as CSS on two roots already (see index.php, :root) and still isn't enough on
   Samsung Internet on its own — that was established before this block existed, which is WHY the
   per-property workarounds in Parts 1–3 exist at all. There is no known single switch that fully
   disables Samsung's pass, so "as complete as possible" means finding every remaining place a red
   `border-color` (solid or translucent) is still expressed as a plain colour value, and moving it
   to the same `background: … padding-box, … border-box` technique Part 3 uses — a family of
   properties (`background-image`) Samsung's pass does not recompute, verified across Parts 1–3.

   Rule for every entry below: the padding-box layer reproduces EXACTLY the element's real fill —
   copied from its own CSS, not simplified or assumed — so nothing about the non-Samsung look
   changes and no other browser's background is touched (`html[data-sigfix]` never matches without
   the Samsung UA check in index.php's <head>). Where the original rule uses `!important` (the two
   `.podcast-player.docked` cases), the override matches it, because forced-dark repaints whatever
   colour actually WINS the normal cascade — a non-important override can never take effect against
   an !important original regardless of source order (confirmed one live instance of exactly this:
   `.podcast-player.docked .pcast-btn.play-main`'s Part-2 fix, added earlier, was silently losing to
   the original's `!important` the entire time).

   Scope: the LIVE tab (the one still reported as drifting) plus the podcast player and the home
   gateway cards (global chrome, visible regardless of tab). NOT yet extended to every red border
   sitewide (party grid, merch, contact, roster/crew each still have their own untouched instances,
   e.g. `.method-icon`, `.contact-textarea:focus`, `.roster-card:hover`) — same technique, just not
   applied yet. `box-shadow` (glow) has no equivalent fix: there is no background/border-adjacent
   property a glow's colour can hide behind, so every red glow sitewide is still whatever Samsung's
   pass decides to do with it. That is a real, currently-open gap, not something this pass hides.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

/* Home gateway card — mobile touch-active red ring (::before is a pure border overlay, no fill) */
html[data-sigfix="1"] #gateway-live:hover::before,
html[data-sigfix="1"] #gateway-live.touch-active::before {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(212, 212, 212, 0), rgba(212, 212, 212, 0)) padding-box,
        linear-gradient(rgba(212, 212, 212, 0.7), rgba(212, 212, 212, 0.7)) border-box;
}

/* LIVE tab: schedule tag, countdown box, "regular" broadcast pill */
html[data-sigfix="1"] .pif-tag {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.12), rgba(242, 17, 44, 0.12)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.35), rgba(242, 17, 44, 0.35)) border-box;
}

html[data-sigfix="1"] .timer-box {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.05), rgba(242, 17, 44, 0.05)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.25), rgba(242, 17, 44, 0.25)) border-box;
}

html[data-sigfix="1"] .pif-schedule-tag.regular {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.15), rgba(242, 17, 44, 0.15)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.45), rgba(242, 17, 44, 0.45)) border-box;
}

/* LIVE tab: replay archive — card hover ring, thumbnail hover ring */
html[data-sigfix="1"] .video-card:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(#111, #111) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.3), rgba(242, 17, 44, 0.3)) border-box;
}

html[data-sigfix="1"] .view-btn:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(10, 10, 10, 0.55), rgba(10, 10, 10, 0.55)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.55), rgba(242, 17, 44, 0.55)) border-box;
}

html[data-sigfix="1"] .archive-item:hover .archive-thumb-box {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(#151515, #151515) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* the fill here is already a two-stop gradient (background-image family, already immune) —
   reproduced verbatim so only the border-color half of this rule actually changes */
html[data-sigfix="1"] .archive-item:hover .more-box {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(135deg, var(--neon-pif-red), rgba(242, 17, 44, 0.6)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Global close button, LIVE theme hover (fill was already correct via Part 2 — consolidated here
   so fill and border are declared together instead of split across two blocks) */
html[data-sigfix="1"] #global-section-close-btn.theme-live:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Podcast player — visible on every tab, not just LIVE */
html[data-sigfix="1"] .pcast-compact-bar:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(16, 16, 19, 0.30), rgba(16, 16, 19, 0.30)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.7), rgba(242, 17, 44, 0.7)) border-box;
}

html[data-sigfix="1"] .podcast-player.expanded .podcast-frame-wrapper {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(14, 14, 17, 0.55), rgba(14, 14, 17, 0.55)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.5), rgba(242, 17, 44, 0.5)) border-box;
}

/* Docked mini-player: the original rule is `!important` (it has to win over the expanded-state
   layout rules), so the override has to be too, plus higher specificity — a bare, non-important
   override here would silently lose, same as the bug this pass found and fixed below. */
html[data-sigfix="1"] .podcast-player.docked {
    background-color: transparent !important;
    border-color: transparent !important;
    background:
        linear-gradient(#0a0a0a, #0a0a0a) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box !important;
}

/* BUGFIX: Part 2's `.podcast-player.docked .pcast-btn.play-main { background-image: … }` (below,
   unchanged) has been dead code since it was written — the original docked play-button rule sets
   `background: var(--neon-live) !important`, and an !important shorthand always beats a
   non-important longhand regardless of source order. This is the only override in the file that
   actually needs `!important` to take effect at all. */
html[data-sigfix="1"] .podcast-player.docked .pcast-btn.play-main {
    background-color: transparent !important;
    border-color: transparent !important;
    background:
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box !important;
}

/* REMOVED 2026-07-27 — `html { forced-color-adjust: none; }` used to sit here as a speculative
   belt-and-suspenders guess. Three reasons it is gone and must not come back:
     1. It was the only rule in this entire lock that was NOT scoped to html[data-sigfix="1"], so it
        applied to Chrome, Safari, iOS — browsers that never had a problem.
     2. It never worked. Forced Colors Mode (Windows High Contrast) is a different mechanism from
        Samsung's forced-dark repaint; the on-device measurement later found the real fix
        (background-clip:text), which had nothing to do with this.
     3. It is actively harmful: it disables High Contrast Mode site-wide for the users who
        deliberately turned that accessibility feature on. */

/* ═══════════════════════════════════════════════════════════════════════════════════════
   SIGNATURE COLOUR LOCK — PART 5: REMAINING TABS (2026-07-26, second pass)

   Part 4 confirmed the mechanism actually works on a real Samsung Internet phone
   (owner: `?sigfix=1` visibly held more red than `?sigfix=0`) — the remaining drift the owner
   was still seeing wasn't the technique failing, it was scope: Part 4 only covered the LIVE tab
   and the podcast player. Every border/translucent-fill on the About/Roster/Demo/Contact/Merch
   tabs was still untouched. This closes that gap using the same proven pattern.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

html[data-sigfix="1"] .nav-tab-link:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.15), rgba(242, 17, 44, 0.15)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

html[data-sigfix="1"] .roster-card:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.04)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Always-on, not a hover state — the roster avatar ring. Works the same way on an <img>: the fill
   layer just has nothing to paint (transparent), only the border ring carries colour. */
html[data-sigfix="1"] .roster-thumb {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Always-on dashed border. Known trade-off: painting the border-box as a filled ring means this
   renders as a SOLID red ring under sigfix instead of a dashed one — there is no equivalent
   background-property escape hatch for `border-style`. A solid ring in the right colour reads
   closer to intent than a dashed ring in the wrong (washed-out) one, so this is taken as an
   acceptable trade rather than left broken. */
html[data-sigfix="1"] .demo-sub-card {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.03)) padding-box,
        linear-gradient(rgba(242, 17, 44, 0.4), rgba(242, 17, 44, 0.4)) border-box;
}

html[data-sigfix="1"] .demo-badge {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.15), rgba(242, 17, 44, 0.15)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

html[data-sigfix="1"] .method-icon {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(242, 17, 44, 0.15), rgba(242, 17, 44, 0.15)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Focus ring on both contact inputs (grouped in the original rule too) */
html[data-sigfix="1"] .contact-input:focus,
html[data-sigfix="1"] .contact-textarea:focus {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Fill was already correct via Part 2 (background-image, opaque) — consolidated here so the
   border-color half isn't left exposed on its own. */
html[data-sigfix="1"] .contact-submit-btn:hover {
    background-color: transparent;
    border-color: transparent;
    background:
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) padding-box,
        linear-gradient(var(--neon-pif-red), var(--neon-pif-red)) border-box;
}

/* Party "UPCOMING" ribbon — near-opaque translucent fill (0.95), no red border to preserve
   (its border is neutral white), so this is a single-layer background-image fix, not the
   double padding-box/border-box form. */
html[data-sigfix="1"] .upcoming-badge {
    background-color: transparent;
    background-image: linear-gradient(rgba(242, 17, 44, 0.95), rgba(242, 17, 44, 0.95));
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   PLAYER BAR CLEARANCE (2026-08-06)
   The transport bar now floats above every user-facing overlay (see the z-index note on
   `.podcast-player`). That is what was asked for — it must never be hidden — but it means any
   overlay with BOTTOM-anchored content would have that content sitting underneath it. These two
   are the only ones: everything else is either top-anchored or centred.

   Declared at the end of the file on purpose: `.mobile-nav-overlay` sets its own `padding` far
   below the player rules, so an override placed next to them would lose on source order.
   Kept unconditional rather than gated on `body:has(.podcast-player.active:not(.docked))` —
   a slightly larger gap when the bar is docked is cosmetic, whereas content trapped under the
   bar is functional, and `:has()` failing on an old Samsung build would fail toward the latter.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

/* ⚠️ Rewritten 2026-09-24. This used to be `padding-bottom: 22px + 104px`, i.e. clearance for the
   player bar INSIDE the panel. On phones the panel is already cut off above the bar with `bottom`,
   so it cleared the bar twice: a 126px empty band at the bottom, and the list squeezed until the
   LABEL & INFO items were clipped out of view (owner screenshot). Now the panel keeps its own
   padding and its HEIGHT is what stops above the bar, so it is exactly as tall as its content,
   never taller than the space above the bar, and scrolls inside when a short screen needs it. */
.mobile-nav-overlay {
    padding-bottom: 18px;
    max-height: calc(100vh - 75px - 112px - env(safe-area-inset-bottom, 0px));
    max-height: calc(100dvh - 75px - 112px - env(safe-area-inset-bottom, 0px));
}
.mobile-nav-overlay .mobile-nav-body {
    min-height: 0;
}
@media (max-width: 768px) {
    /* Measured on a 384x650 phone viewport: header ends at 40, the player bar starts at 567.
       The panel runs 50 -> 555, i.e. 10px under the header and 12px above the bar. */
    .mobile-nav-overlay {
        top: 50px;
        bottom: auto;
        padding: 14px 18px 10px;
        max-height: calc(100vh - 50px - 95px - env(safe-area-inset-bottom, 0px));
        max-height: calc(100dvh - 50px - 95px - env(safe-area-inset-bottom, 0px));
    }
    .mobile-nav-header {
        --pif-navlogo: 36px;
        --pif-navpad: 10px;
    }
    .mobile-nav-close {
        width: 38px;
        height: 38px;
        flex: none;
    }
    .mobile-nav-body {
        padding: 6px 0 !important;
        gap: 10px !important;
    }
    .mobile-group-label {
        margin-bottom: 6px;
    }
    .mobile-nav-item {
        font-size: 1rem;
        padding: 7px 0;
        min-height: 40px;           /* full-width rows, well above the 24px target floor */
        box-sizing: border-box;
    }
    /* Footer on one row: icons left, the two copyright lines right. Same content as before at
       about half the height (the lines themselves must stay - owner). */
    .mobile-nav-footer {
        display: grid;
        grid-template-columns: auto 1fr;
        align-items: center;
        column-gap: 14px;
        padding-top: 10px !important;
        padding-bottom: 0 !important;
        text-align: right;
    }
    .mobile-nav-footer .mobile-social-row {
        grid-row: 1 / span 2;
        margin-bottom: 0 !important;
    }
    .mobile-nav-footer p {
        font-size: 0.56rem !important;
        opacity: 0.75;
    }
}

/* The lightbox thumbnail strip is click-to-switch, so it has to stay reachable. */
#lightbox-thumbs-container {
    margin-bottom: 96px;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   AFTERMOVIES + COLLABORATIONS (2026-08-06)
   Both live inside #party, between PAST EVENTS and GALLERIES. They borrow the Party accent
   (--neon-party) and the same 50px/border-top rhythm as .insta-feed-section so the tab still
   reads as one column rather than three unrelated widgets.
   Colours are declared as plain CSS `color` / `background-color` on purpose: measured 2026-07-26,
   Samsung's forced-dark pass leaves every CSS colour alone and only repaints image pixels, so
   none of this needs the signature-colour lock treatment.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

.aftermovie-section,
.collab-section {
    margin-top: 50px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding-top: 30px;
}

/* `hidden` has to be forced: the sections are display:flex/grid containers, and a bare [hidden]
   loses to any display rule. Both start hidden in the markup and JS reveals them only when there
   is something to show, so an empty heading never ships. */
.aftermovie-section[hidden],
.collab-section[hidden] {
    display: none !important;
}

/* ── Aftermovie strip ─────────────────────────────────────────────────────────────────── */
.aftermovie-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 22px;
}

.aftermovie-card {
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.25s ease;
    /* Same reasoning as .collab-session. Each card carries a 1280x720 YouTube still, so the
       decode is worth deferring until the strip is actually reached. */
    content-visibility: auto;
    contain-intrinsic-size: auto 260px;
}

.aftermovie-card:hover,
.aftermovie-card:focus-visible {
    transform: translateY(-3px);
    border-color: var(--neon-party);
    outline: none;
}

.aftermovie-media {
    position: relative;
    aspect-ratio: 16 / 9;
    background: #0d0d0d;
    overflow: hidden;
}

.aftermovie-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* The source is maxresdefault, a true 1280x720, and the box above is 16/9 - so cover is an
       exact fit and crops nothing. It stays `cover` rather than `contain` only to absorb sub-pixel
       rounding; a genuinely off-ratio still (a file recap's own poster) is centred and filled. */
}

.aftermovie-thumb-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255, 255, 255, 0.25);
    font-size: 1.6rem;
}

.aftermovie-play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.62);
    border: 1px solid var(--neon-party);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.9rem;
    padding-left: 3px; /* optical centring of the triangle */
    pointer-events: none;
    transition: background 0.25s ease, transform 0.25s ease;
}

.aftermovie-card:hover .aftermovie-play,
.collab-cover:hover .aftermovie-play {
    background: var(--neon-party);
    color: #151515;
    transform: translate(-50%, -50%) scale(1.06);
}

.aftermovie-year,
.aftermovie-src {
    position: absolute;
    top: 8px;
    font-size: 0.66rem;
    font-weight: 800;
    letter-spacing: 1px;
    padding: 3px 8px;
    border-radius: 5px;
    background: rgba(0, 0, 0, 0.72);
    /* White, not amber-on-dark-amber: this is a small label over arbitrary artwork, and white
       measures far above the 4.5:1 AA floor on every frame it can land on. */
    color: #fff;
}

.aftermovie-year { left: 8px; }
.aftermovie-src  { right: 8px; padding: 3px 7px; }

/* 종류 배지. 이 스트립에는 이제 아프터무비와 풀믹스가 섞여 있으므로, 카드 자체가 어느 쪽인지
   말해야 합니다. 연도 배지 바로 아래에 붙습니다.
   위 규칙과 같은 이유로 흰 글자입니다 - 임의의 아트워크 위에 얹히는 작은 라벨이라, 틴트 위에
   강조색을 올리면 읽히지 않습니다(CLAUDE.md 의 red-on-red 항목). 풀믹스만 앰버 테두리로
   구분해, 정보가 색 하나에만 실리지 않게 글자도 함께 다릅니다. */
.aftermovie-kind {
    position: absolute;
    /* ⚠️ rem 입니다. 33px 로 고정하면 브라우저 글자 크기를 키웠을 때 위의 .aftermovie-year 만
       자라 두 배지가 겹칩니다(WCAG 1.4.4 resize text). 두 배지 모두 rem 으로 크기가 정해지므로
       이 좌표도 rem 이어야 함께 움직입니다. 기본 16px 기준 2.05rem = 32.8px 라 지금 보이는
       모습은 그대로입니다. */
    top: 2.05rem;
    left: 8px;
    font-size: 0.6rem;
    font-weight: 800;
    letter-spacing: 0.8px;
    padding: 3px 7px;
    border-radius: 5px;
    background: rgba(0, 0, 0, 0.72);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.16);
    pointer-events: none;
}

.aftermovie-card[data-recap="fullmix"] .aftermovie-kind {
    border-color: rgba(212, 212, 212, 0.7);
    background: rgba(31, 31, 31, 0.82);
}

.aftermovie-info { padding: 12px 14px 14px; }

.aftermovie-title {
    font-size: 0.92rem;
    font-weight: 800;
    color: #f0f0f0;
    margin: 0;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.aftermovie-caption {
    margin: 6px 0 0;
    font-size: 0.76rem;
    color: rgba(255, 255, 255, 0.55);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* 세트 개수. 배지 안에 붙어서 "FULL MIX 4" 로 읽힙니다. 배지를 하나 더 세우지 않는 이유는
   자리입니다 - 연도, 종류, 유튜브 표시가 이미 포스터 위에 얹혀 있고, 네 번째가 붙으면 아트워크가
   배지밭이 됩니다.
   #151515 글자를 #D4D4D4 위에 얹어 9.9:1 입니다. 앰버 위에 흰 글자였다면 2.1:1 로 못 읽습니다. */
.aftermovie-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 15px;
    height: 15px;
    margin-left: 6px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--neon-party);
    color: #151515;
    font-size: 0.56rem;
    font-weight: 900;
    letter-spacing: 0;
    line-height: 1;
    vertical-align: middle;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   FULL MIX 세트 고르기 시트 (2026-09-11)

   한 밤의 풀믹스가 여러 개일 때만 열립니다. 하나뿐인 밤은 이 면을 지나지 않고 바로 재생되므로,
   흔한 경우에는 클릭이 늘어나지 않습니다.

   ── 왜 이벤트 모달이 아닌가
   이벤트 모달(z-index 2000)은 포스터, 설명, 갤러리, 가격까지 실린 큰 면입니다. 여기서 정할 것은
   "누구 셋을 볼까" 하나뿐이라, 같은 크기의 면을 하나 더 얹으면 어디로 돌아가는 면인지 알 수
   없어집니다.

   ── 재생 바 아래, 라이트박스 위
   .podcast-player 는 100002 이고 어떤 오버레이에도 가려지지 않는 것이 이 사이트의 규칙입니다
   (위의 PLAYER BAR CLEARANCE). 그래서 시트는 100001 이고, 갤러리 라이트박스(10000)와 이벤트
   모달(2000)보다는 위에 옵니다. 아래쪽 96px 여백은 바에 목록이 깔리지 않게 하는 값이며,
   #lightbox-thumbs-container 가 쓰는 값과 같습니다.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.fmx-sheet {
    position: fixed;
    inset: 0;
    z-index: 100001;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px 20px calc(24px + 96px);
}

/* ⚠️ 쉬는 상태가 "보이는" 쪽입니다. 들어오는 한 프레임만 :not(.is-open) 으로 투명하고, 그
   클래스는 강제 리플로우 직후 동기적으로 붙습니다. 반대로 짜면(기본 투명 + .is-open 에서
   보임) 전환이 한 발짝도 나아가지 않는 환경에서 시트가 영원히 opacity 0 으로 남고, 화면에는
   아무것도 없는데 포커스만 그 안에 갇힙니다. 이 프로젝트의 브라우저 패인에서 실제로 그렇게
   측정되었습니다(CLAUDE.md 의 "CSS transitions never advance"). 지금 형태는 전환이 죽어도
   보이는 쪽으로 실패합니다. */
.fmx-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(6, 6, 8, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 1;
    transition: opacity 220ms ease;
}

.fmx-sheet:not(.is-open) .fmx-backdrop { opacity: 0; }

.fmx-panel {
    /* 아래 여백은 한 군데서만 정합니다. 좌표가 두 곳에 적히면 모바일에서 흐림이 목록 끝이
       아닌 엉뚱한 자리에 드립니다. */
    --fmx-foot: 6px;
    position: relative;
    width: min(440px, 100%);
    max-height: 100%;
    display: flex;
    flex-direction: column;
    background: #17171a;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 16px;
    padding: 22px 20px var(--fmx-foot);
    box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
    opacity: 1;
    transition: opacity 240ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 240ms cubic-bezier(0.16, 1, 0.3, 1);
}

.fmx-sheet:not(.is-open) .fmx-panel {
    opacity: 0;
    transform: translateY(10px) scale(0.985);
}

/* 목록이 넘칠 때만 나타나는 흐림. 끝까지 내려가면 사라집니다 - 남아 있으면 마지막 출연자가
   영원히 반쯤 가려진 것처럼 보입니다. */
.fmx-panel.is-scrollable::after {
    content: '';
    position: absolute;
    left: 1px;
    right: 1px;
    bottom: var(--fmx-foot);
    height: 38px;
    background: linear-gradient(to top, #17171a, rgba(23, 23, 26, 0));
    pointer-events: none;
    opacity: 1;
    transition: opacity 180ms ease;
}

.fmx-panel.is-scrollable.is-end::after { opacity: 0; }

.fmx-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin: 0 0 7px;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 1.4px;
    color: var(--neon-party);
}

.fmx-eyebrow b {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--neon-party);
    color: #151515;
    font-size: 0.58rem;
    font-weight: 900;
    letter-spacing: 0;
    line-height: 1;
}

.fmx-title {
    margin: 0;
    padding-right: 34px;   /* 닫기 버튼 자리 */
    font-size: 1.02rem;
    font-weight: 800;
    line-height: 1.3;
    color: #f2f2f2;
}

.fmx-sub {
    margin: 5px 0 0;
    font-size: 0.76rem;
    color: rgba(255, 255, 255, 0.5);
}

.fmx-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.05);
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.85rem;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease;
}

.fmx-close:hover,
.fmx-close:focus-visible {
    background: rgba(255, 255, 255, 0.13);
    color: #fff;
}

/* 목록만 스크롤합니다. overscroll-behavior 가 없으면 목록 끝에서 계속 밀었을 때 뒤의 #party 가
   따라 움직여, 닫고 나면 다른 자리에 가 있습니다. */
.fmx-list {
    list-style: none;
    margin: 16px -6px 0;
    padding: 0 6px 16px;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.fmx-row + .fmx-row { margin-top: 6px; }

.fmx-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 8px;
    border: 1px solid transparent;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.035);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease;
}

/* ⚠️ :focus-visible 이 아니라 :focus 입니다. 이 시트는 화살표 키 핸들러가 focus() 를 직접
   호출해 행 사이를 옮겨 다니는데, 브라우저의 :focus-visible 판정은 '마지막 입력이 키보드였나'
   라는 휴리스틱이라 프로그램이 옮긴 포커스에는 붙지 않습니다. 실측: 시트를 연 직후 첫 행이
   focus 를 쥐고 있는데 matches(':focus-visible') 가 false 라, 표시가 아무것도 없었습니다.
   즉 어디서 시작하는지 모른 채 화살표를 누르게 됩니다.
   열려 있는 모달 안에서 '지금 여기' 표시는 마우스로 왔든 키보드로 왔든 항상 옳으므로
   :focus 로 둡니다. 이것이 roving-focus 목록 위젯의 표준 처리이기도 합니다. */
.fmx-item:hover,
.fmx-item:focus {
    background: rgba(212, 212, 212, 0.10);
    border-color: rgba(212, 212, 212, 0.45);
}

/* ⚠️ outline 을 지우고 배경·테두리로 대신하던 것을 되돌립니다. 실측한 대비가 전부 3:1 미만이라
   WCAG 2.4.11(비텍스트 대비)에 못 미쳤습니다:
     테두리 vs 패널 2.65:1 · 테두리 vs 행 바탕 2.56:1 · 행 바탕 변화 1.08:1
     .fmx-close 배경 변화 1.29:1
   통과하던 것은 행 오른쪽 끝 28px 짜리 재생 알약(7.13:1) 하나뿐이고, 닫기 버튼에는 그마저도
   없었습니다. 이 시트는 화살표 키로 오르내리게 만들어 두었으므로 포커스 링이 "지금 어디"를
   말하는 유일한 수단입니다.
   ⚠️ outline 이어야 하는 두 번째 이유: Windows 고대비 모드는 작성자 배경색을 시스템 색으로
      갈아끼우므로 배경·테두리로만 만든 표시는 통째로 사라집니다. outline 은 살아남습니다.
   앰버 #D4D4D4 는 패널 #17171a 에서 8.33:1 입니다. hover 와도 갈라져, 마우스가 짚은 행과
   키보드가 서 있는 행이 구별됩니다. */
.fmx-item:focus,
.fmx-close:focus-visible {
    outline: 2px solid var(--neon-party, #D4D4D4);
    outline-offset: 2px;
}

.fmx-thumb {
    position: relative;
    flex: 0 0 auto;
    width: 68px;
    aspect-ratio: 16 / 9;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    overflow: hidden;
    background: #0d0d0d;
    color: rgba(255, 255, 255, 0.25);
    font-size: 0.78rem;
}

/* 68px 박스라 mqdefault(320x180)면 충분합니다. 카드가 쓰는 maxresdefault(1280x720)를 여기까지
   끌고 오면 한 밤에 썸네일 다섯 장이 1280 폭으로 들어옵니다. */
.fmx-thumb img {
    /* 아이콘이 밑에 깔리고 그림이 그 위를 덮습니다. 그림이 실패해 지워지면 아이콘이 남습니다. */
    position: relative;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.fmx-thumb i {
    position: absolute;
}

.fmx-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.86rem;
    font-weight: 700;
    color: #ededed;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fmx-go {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.06);
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.66rem;
    padding-left: 2px;   /* 삼각형 광학 중심 */
    transition: background 0.18s ease, color 0.18s ease;
}

.fmx-item:hover .fmx-go,
.fmx-item:focus .fmx-go {
    background: var(--neon-party);
    color: #151515;
}

/* 좁은 화면에서는 바텀 시트입니다. 목록이 엄지 가까이 오고, 화면 한가운데 뜬 작은 상자보다
   닫는 방법이 분명합니다. 아래 여백은 재생 바 높이만큼입니다. */
@media (max-width: 520px) {
    .fmx-sheet {
        align-items: flex-end;
        padding: 0;
    }

    .fmx-panel {
        --fmx-foot: calc(16px + 96px + env(safe-area-inset-bottom, 0px));
        width: 100%;
        max-height: 82vh;
        border-radius: 18px 18px 0 0;
        border-bottom: 0;
        padding: 20px 16px var(--fmx-foot);
    }

    /* 좁은 화면에서는 아래에서 올라옵니다. 위의 뒤집힌 형태와 같은 이유로 :not(.is-open) 쪽에
       적습니다. */
    .fmx-sheet:not(.is-open) .fmx-panel {
        transform: translateY(18px);
    }
}

/* ⚠️ 이 블록은 반드시 위의 @media (max-width: 520px) **뒤**에 있어야 합니다. 둘 다
   .fmx-panel 에 --fmx-foot / max-height 를 (0,1,0) 명시도로 적으므로 동점이고, 미디어
   쿼리는 명시도를 더하지 않아 소스 순서가 승부를 가릅니다. 처음에 이 블록을 파일 앞쪽
   (1502행, 전혀 다른 섹션)에 넣었더니 경쟁자가 없는 .fmx-sub 만 먹히고 나머지는 전부
   졌습니다 - 가로 폰에서 여전히 1행이었습니다. */
/* 세로가 짧으면 재생 바 여백이 목록을 굶깁니다. 실측한 '보이는 행' 개수:
     375x812 폰 세로 8행 · 360x640 5행 · 320x480 3행
     480x360 가로 **1행** · 740x360 가로 **1행**
   바 높이는 화면이 작아져도 그대로라 짧은 화면에서 비율이 무너집니다. 가로로 돌린 폰에서
   목록이 한 줄만 보이면 고를 수가 없습니다.
   여백을 0 으로 만들지는 않습니다 - 이 사이트의 규칙은 '재생 바에 깔린 콘텐츠는 기능 문제,
   여백이 조금 큰 것은 미관 문제' 이고, 짧은 화면에서도 바는 그대로 떠 있습니다. 대신 바 높이
   만큼만 남기고, 부제를 접고, 패널이 화면을 더 쓰게 합니다. 같은 480x360 에서 3행이 됩니다. */
@media (max-height: 520px) {
    .fmx-sheet {
        padding-top: 10px;
        padding-bottom: 10px;
    }

    .fmx-panel {
        --fmx-foot: calc(8px + 72px + env(safe-area-inset-bottom, 0px));
        max-height: 100%;
        padding-top: 12px;
    }

    /* 날짜·장소는 카드에도 있습니다. 여기서는 고르는 일이 먼저입니다. */
    .fmx-sub { display: none; }

    .fmx-title { font-size: 0.94rem; }

    .fmx-list { margin-top: 10px; }
}

@media (prefers-reduced-motion: reduce) {

    /* 전환만 끕니다. 쉬는 상태가 이미 보이는 쪽이라, 전환이 없으면 시트는 그냥 나타납니다 -
       여기서 transform 을 따로 지울 필요가 없습니다. */
    .fmx-backdrop,
    .fmx-panel,
    .fmx-item,
    .fmx-go,
    .fmx-close { transition: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════════════════
   HERO / 간판 영상 (2026-09-18)
   랜딩과 파티 탭 맨 위에서 후보 중 한 편을 무작위로 틉니다.

   가로 영상을 좁은 화면에 그대로 두면 띠가 너무 얇아져 아무것도 안 보입니다. 그래서 비율을
   고정하지 않고 **높이를 정한 뒤 cover 로 채웁니다** - 넓은 화면에서는 시원한 띠, 좁은
   화면에서는 적당한 높이의 창이 됩니다. 원본이 16:9 라 좌우가 잘리지만, 간판은 분위기를
   전하는 것이지 전체 구도를 보여주는 것이 아닙니다.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
.pif-hero[hidden] { display: none !important; }

.pif-hero {
    width: 100%;
    margin: 0 0 28px;
}

.pif-hero-frame {
    position: relative;
    width: 100%;
    height: clamp(180px, 26vw, 420px);
    overflow: hidden;
    border-radius: 12px;
    background: #0d0d0d;
    /* 포스터가 오기 전 검은 칸이 번쩍이지 않게 살짝 가라앉힌 바탕 */
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.45);
}

.pif-hero-video {
    /* ══════════════════════════════════════════════════════════════════════════════════
       자리·확대 모델: object-fit: cover + object-position + scale

       ⚠️ 처음에는 배경(backgrounds)의 "폭 = zoom×100%, height:auto" 수식을 그대로 가져왔는데
          **틀렸습니다**. 그 수식은 프레임이 원본보다 홀쭉할 때만 덮습니다. 간판 띠는 반대로
          납작한데, 모바일에서 프레임 377x226(비 1.67)에 16:9 영상을 폭 100% 로 넣으니 높이가
          212 밖에 안 나와 위아래로 14px 검은 띠가 남았습니다(실측).

          cover 는 프레임 비율이 무엇이든 항상 덮습니다. 그래서 zoom 1 배가 "빈틈 없음"의
          보장선이 되고, 관리자 슬라이더의 최소값도 1 입니다 - 1 미만은 검은 띠를 만드는
          값이라 애초에 고를 수 없게 막았습니다.

       자리 값은 js 의 pifHeroFrame() 이 인라인 변수로 넣습니다. transform-origin 을 초점과
       같게 두는 것이 중요합니다. 그래야 확대할 때 고른 지점이 제자리에 머뭅니다.
       ══════════════════════════════════════════════════════════════════════════════════ */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    object-position: var(--pif-hero-pos, 50% 50%);
    transform: scale(var(--pif-hero-zoom, 1));
    transform-origin: var(--pif-hero-pos, 50% 50%);
    /* 화면 두 장을 겹쳐 두고 번갈아 씁니다(더블 버퍼). 다음 편을 미리 받아 둔 쪽으로 넘기면
       전환에 검은 칸이나 포스터가 번쩍이지 않습니다. 자세한 이유는 js 의 pifRenderHero 주석. */
    opacity: 0;
    z-index: 1;
    transition: opacity 0.5s ease;
}

/* ⚠️ 첫 화면에는 마크업에 is-live 가 박혀 있습니다. js 가 손대기 전에도 한 장은 보입니다 -
   전환이 씹혀도 빈 칸이 남지 않게 하려는 것입니다. */
.pif-hero-video.is-live {
    opacity: 1;
    z-index: 2;
}

/* 아래 콘텐츠와 맞닿는 가장자리를 눌러 줍니다. 간판이 '붙인 배너' 가 아니라 페이지의 일부로
   읽히게 하는 장치입니다. 영상 위에 글자가 올라가지 않으므로 대비 문제는 없습니다. */
.pif-hero-veil {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(to bottom,
                rgba(0, 0, 0, 0.28) 0%,
                rgba(0, 0, 0, 0) 32%,
                rgba(0, 0, 0, 0) 68%,
                rgba(21, 21, 21, 0.72) 100%);
}

/* 랜딩은 게이트웨이 카드 위에 앉습니다. 첫 화면이라 조금 더 낮게 잡아 카드가 접히지 않게
   합니다 - 간판이 게이트웨이를 화면 밖으로 밀어내면 간판이 아니라 방해물이 됩니다. */

@media (max-width: 900px) {
    .pif-hero { margin-bottom: 22px; }
    .pif-hero-frame { height: clamp(180px, 46vw, 280px); border-radius: 10px; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   모바일에서는 화면 끝까지 채웁니다

   좁은 화면에서 좌우에 여백을 두면 띠가 "페이지에 얹은 카드" 로 보여서 붕 뜹니다. 섹션의
   좌우 패딩만큼 음수 여백으로 밀어내 화면 폭을 꽉 채우고, 모서리 둥글림과 그림자를 지워
   페이지의 일부처럼 읽히게 합니다.
   ⚠️ 음수 여백 값은 `.content-section` 의 좌우 패딩과 **반드시 같아야** 합니다.
      768px 이하 16px, 480px 이하 12px 입니다. 그 패딩을 바꾸면 여기도 같이 바꾸십시오.
      어긋나면 가로 스크롤이 생깁니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    .pif-hero {
        /* ⚠️ 음수 여백으로 패딩을 되돌리는 방식이었는데 **틀렸습니다**. 섹션 좌우 패딩이
           16px 인 줄 알았으나 실측은 18px 이었고, 그래서 양쪽에 7px 이 남았습니다.
           지금 방식은 패딩 값을 아예 몰라도 됩니다 - 부모 폭의 절반만큼 밀어낸 뒤 자기
           폭의 절반만큼 되당기면 패딩이 얼마든 정확히 화면 폭이 됩니다.
           섹션에 overflow-x:hidden 이 걸려 있어 1px 오차가 가로 스크롤이 되지 않습니다. */
        width: 100vw;
        max-width: 100vw;
        margin-left: 50%;
        transform: translateX(-50%);
        /* 섹션 상단 패딩이 크게 잡혀 있어 그대로 두면 띠가 한참 내려와 앉습니다. 조금
           끌어올려 첫 화면에서 바로 눈에 들어오게 합니다. */
        margin-top: -18px;
        /* ⚠️ 24px 이었는데 실측해 보니 TOC 자체 여백 20px 과 합쳐져 **44px 짜리 빈 띠**가
           생겼습니다. 그 띠에는 섹션의 앰비언트 글로우(황갈색)만 보여서 화면이 끊어진 것처럼
           읽혔습니다. 좁히고, 대신 아래 veil 로 영상 밑단을 페이지에 녹입니다. */
        margin-bottom: 8px;
    }
    .pif-hero-frame {
        height: clamp(200px, 54vw, 310px);
        border-radius: 0;
        box-shadow: none;
    }
    /* 끝까지 채우면 아래 가장자리가 콘텐츠와 바로 맞닿으므로 그늘을 조금 더 깊게 둡니다. */
    /* 밑단을 섹션 배경색(#151515)까지 끌고 내려갑니다. 하드 컷이 아니라 사라지듯 끝나야
       아래 여백이 '끊긴 자리' 가 아니라 '숨 쉬는 자리' 로 읽힙니다. */
    /* ══════════════════════════════════════════════════════════════════════════════════════
       모바일 간판은 화면 꼭대기에서 시작하고 목차가 바로 밑에 붙습니다

       ⚠️ 아래 간격: 헤로의 margin-bottom 을 줄여도 안 줄어듭니다. `.party-toc` 가 자기 위에
          margin-top:44px 를 갖고 있고 인접 형제 마진은 **상쇄**되어 큰 쪽이 이깁니다
          (실측: 헤로를 24px -> 6px 로 내려도 44px 그대로). TOC 쪽을 눌러야 합니다.

       ⚠️ 위 간격: 섹션의 padding-top 이 만듭니다(실측 375x812 에서 60px, 헤로 상단 y=62).
          `#main-header` 는 이미 `position: fixed` 이고 배경이 **완전 투명**이라, 이 여백만
          없애면 로고와 햄버거가 영상 위에 그대로 얹힙니다.
          헤더 높이를 숫자로 베끼지 마십시오 - 실측 48px 이지만 기기·방향에 따라 달라집니다.
          이 방식은 높이를 **알 필요가 없습니다.**

       `:has()` 로 **헤로가 실제로 있는 섹션에만** 겁니다. MUSIC/LIVE 처럼 헤로가 없거나
       헤로가 hidden 이면 원래 여백이 그대로 남습니다. `:has()` 를 모르는 브라우저에서는 규칙
       전체가 무시되어 예전 모습으로 안전하게 되돌아갑니다(fail-safe).
       ══════════════════════════════════════════════════════════════════════════════════════ */
    .content-section:has(> .pif-hero:not([hidden])) {
        padding-top: 0 !important;
    }

    .content-section:has(> .pif-hero:not([hidden])) > .pif-hero {
        /* 위 여백이 사라졌으니 끌어올릴 것이 없습니다. 음수 마진을 남기면 섹션 밖으로 나가
           잘립니다. */
        margin-top: 0;
    }

    .pif-hero:not([hidden]) + .party-toc {
        margin-top: 0;
    }
    /* ⚠️ 위쪽 그늘이 진해야 합니다. 이제 영상이 화면 꼭대기까지 올라가 로고·햄버거·BACK 이
       그 위에 얹히는데, 밝은 프레임이 나오면 아이콘이 묻힙니다. 이 그늘은 장식이 아니라
       그 아이콘들의 가독성 장치입니다. 헤더 높이(실측 48px)를 넉넉히 덮도록 30% 까지 끕니다. */
    .pif-hero-veil {
        background: linear-gradient(to bottom,
                    rgba(0, 0, 0, 0.62) 0%,
                    rgba(0, 0, 0, 0.34) 13%,
                    rgba(0, 0, 0, 0) 30%,
                    rgba(0, 0, 0, 0) 52%,
                    rgba(21, 21, 21, 0.45) 76%,
                    rgba(21, 21, 21, 0.88) 92%,
                    rgba(21, 21, 21, 1) 100%);
    }
}

@media (max-width: 480px) {
    /* 이 구간은 섹션 좌우 패딩이 12px 입니다. */
    .pif-hero {
        /* 위 여백은 768px 블록이 섹션 padding-top 을 0 으로 만들어 처리합니다. 여기서
           끌어올릴 것이 없습니다. */
        margin-top: 0;
        margin-bottom: 0;
    }
    .pif-hero-frame { height: clamp(210px, 58vw, 300px); border-radius: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   띠는 누를 수 있습니다
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.pif-hero-frame[role="button"] { cursor: pointer; }

/* 키보드로 왔을 때만 테두리를 보여 줍니다. 마우스로 누를 때마다 테두리가 뜨면 거슬립니다. */
.pif-hero-frame:focus-visible {
    outline: 2px solid #D4D4D4;
    outline-offset: 3px;
}

/* 누를 수 있다는 힌트. 평소에는 거의 안 보이다가 가까이 가면 또렷해집니다. */
.pif-hero-cue {
    position: absolute;
    right: 14px;
    bottom: 12px;
    width: 34px;
    height: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: rgba(255, 255, 255, 0.82);
    font-size: 0.82rem;
    opacity: 0.45;
    transition: opacity 0.25s ease, transform 0.25s ease;
    pointer-events: none;
}

.pif-hero-frame:hover .pif-hero-cue,
.pif-hero-frame:focus-visible .pif-hero-cue {
    opacity: 1;
    transform: scale(1.06);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   크게 보기 오버레이

   ⚠️ 쉬는 상태를 **보이는 쪽**으로 둡니다. 전환이 돌지 않아도 열리기만 하면 보입니다.
      투명에서 시작해 클래스로만 나타나게 하면, 전환이 씹히는 순간 사용자는 "눌렀는데
      아무 일도 안 일어난" 화면을 봅니다. hidden 이 실제 차단 장치입니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.pif-hx[hidden] { display: none !important; }

.pif-hx {
    position: fixed;
    inset: 0;
    /* ⚠️ 4000 으로는 부족합니다. 이 사이트에는 더 높은 것이 둘 있습니다(실측):
       #podcast-sticky-player 100002, #global-section-close-btn 99999. 4000 이면 팟캐스트
       막대가 영상 하단 79px 를 가리고, 섹션 닫기 버튼이 영상 위에 떠서 누르면 뒤에 있는
       섹션이 닫힙니다. 전체화면이라면 그 위에 있어야 합니다.
       음악은 계속 흐릅니다 - 가려지는 것은 막대의 모습뿐이고 소리는 그대로입니다.
       (간판 영상에는 오디오가 없으므로 서로 부딪히지 않습니다.) */
    z-index: 100003;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
    opacity: 1;
    transition: opacity 0.22s ease;
}

/* 열림 직후 한 틱은 살짝 흐릿하게 들어옵니다. 없어도 기능에는 지장이 없습니다. */
.pif-hx:not(.is-open) { opacity: 0.35; }

.pif-hx-back { position: absolute; inset: 0; }

.pif-hx-video {
    position: relative;
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    /* contain 입니다. 크게 보기는 구도 전체를 보여 주는 자리라 잘라내면 안 됩니다.
       띠에서 cover 로 자르는 것과 의도가 정반대입니다. */
    object-fit: contain;
    background: #000;
}

.pif-hx-btn {
    position: absolute;
    border: 1px solid rgba(255, 255, 255, 0.16);
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: #fff;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 44px 은 손가락 최소 크기입니다. 이보다 작으면 모바일에서 자꾸 빗나갑니다. */
    width: 52px;
    height: 52px;
    font-size: 1.05rem;
    transition: background 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}

.pif-hx-btn:hover {
    background: rgba(242, 17, 44, 0.82);
    border-color: rgba(242, 17, 44, 0.9);
    transform: scale(1.06);
}

.pif-hx-btn:focus-visible {
    outline: 2px solid #D4D4D4;
    outline-offset: 3px;
}

.pif-hx-prev  { left: 22px;  top: 50%; transform: translateY(-50%); }
.pif-hx-next  { right: 22px; top: 50%; transform: translateY(-50%); }
.pif-hx-prev:hover { transform: translateY(-50%) scale(1.06); }
.pif-hx-next:hover { transform: translateY(-50%) scale(1.06); }
.pif-hx-close { right: 22px; top: 22px; }

.pif-hx-hint {
    position: absolute;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    margin: 0;
    font-size: 0.74rem;
    letter-spacing: 0.4px;
    color: rgba(255, 255, 255, 0.5);
    pointer-events: none;
}

/* 오버레이가 열린 동안 뒤가 스크롤되면 닫았을 때 엉뚱한 자리에 가 있습니다. */
body.pif-hx-lock { overflow: hidden; }

@media (max-width: 768px) {
    /* 좁은 화면에서 좌우 화살표가 영상 위에 겹치면 화면을 가립니다. 아래로 내려 나란히
       둡니다. 닫기만 위에 남깁니다. */
    .pif-hx-prev, .pif-hx-next {
        top: auto;
        bottom: 26px;
        transform: none;
        width: 56px;
        height: 56px;
    }
    .pif-hx-prev:hover, .pif-hx-next:hover { transform: scale(1.06); }
    .pif-hx-prev { left: calc(50% - 76px); }
    .pif-hx-next { right: calc(50% - 76px); }
    .pif-hx-close { right: 14px; top: 14px; }
    /* 화살표가 앉을 자리를 비워 둡니다. */
    .pif-hx-video { max-height: calc(100% - 120px); }
    .pif-hx-hint { display: none; }
    .pif-hero-cue { right: 10px; bottom: 10px; width: 30px; height: 30px; font-size: 0.74rem; opacity: 0.6; }
}

@media (prefers-reduced-motion: reduce) {
    .pif-hx, .pif-hx-btn, .pif-hero-cue, .pif-hero-video { transition: none !important; }
    .pif-hx:not(.is-open) { opacity: 1; }
}

/* 동작을 줄여 달라고 한 사람에게는 포스터만 남습니다. js 도 재생을 걸지 않지만, 혹시 다른
   경로로 재생되더라도 여기서 한 번 더 막습니다. */
@media (prefers-reduced-motion: reduce) {
    .pif-hero-video { animation: none !important; }
}

@media print {
    .pif-hero { display: none !important; }
}

/* ── Collaboration portfolio ──────────────────────────────────────────────────────────── */
.collab-list {
    display: flex;
    flex-direction: column;
    gap: 26px;
}

/* A wide two-column card rather than a grid tile: a residency carries a description, a partner,
   a period and a portfolio strip, and none of that survives being squeezed into a poster tile. */
.collab-card {
    display: grid;
    grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
    gap: 0;
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 14px;
    overflow: hidden;
}

.collab-cover {
    /* A FIXED 9:16 on desktop, chosen 2026-09-01. It used to have no ratio at all - a grid item
       with `min-height:260px` that stretched to whatever the body beside it needed - so the shape
       differed per collaboration and moved again every time a session was added (measured:
       TRANCE4 380x752 = 0.505, TRANCISION 380x528 = 0.720). That made a WYSIWYG framing preview
       impossible, because there was no single shape to preview. One ratio fixes both problems and
       every future collaboration inherits it.
       `align-self: start` is what makes it stick: a grid item stretches to the row by default,
       which would override the ratio again on any card whose body is taller. */
    aspect-ratio: 9 / 16;
    align-self: start;
    position: relative;
    background: #0d0d0d;
    overflow: hidden;
    /* No aspect-ratio in the two-column layout. A fixed ratio pins the cover to ~237px tall while
       the body beside it runs to whatever the description + portfolio strip need, which left the
       card visibly lopsided on desktop (reported 2026-08-22). As a grid item it stretches to the
       row height by default; min-height only covers the case where the body is unusually short.
       The single-column breakpoint below restores an explicit ratio, because there it is the
       only thing giving the cover a height at all. */
    min-height: 0;
}

.collab-cover[data-video] { cursor: pointer; }

.collab-cover-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Framing chosen per collaboration in admin. object-position decides which part of the crop
       survives; scale() zooms into it, with transform-origin pinned to the SAME point so zooming
       pushes into whatever was centred instead of drifting away from it. Defaults reproduce the
       plain `object-fit: cover` this had before, so an unframed cover is unchanged. */
    object-position: var(--cvr-pos, 50% 50%);
    transform: scale(var(--cvr-zoom, 1));
    transform-origin: var(--cvr-pos, 50% 50%);
    position: relative;
    z-index: 1;
}

/* FIT: the whole cover is shown and a blurred copy of it fills what is left over. Measured
   2026-08-24, a 1600x900 cover loses 72% of its width in the 380x752 desktop column - zoom and
   position can only choose which 28% survives, so there has to be a way to keep all of it. */
/* CONTAIN is the default now - the whole poster, never a crop chosen for a different image.
   `object-position` is deliberately NOT applied here: under contain the artwork is centred, which
   is the only framing that is correct for every slide regardless of its ratio. */
.collab-cover .collab-cover-img { object-fit: contain; object-position: center; }
.collab-cover[data-fit="cover"] .collab-cover-img {
    object-fit: cover;
    object-position: var(--cvr-pos, 50% 50%);
}

/* Cover rotation / hover preview: the second layer sits exactly on the first and only its opacity
   moves, so a swap dissolves instead of cutting. Both layers are the same element type in the same
   box, so they inherit the identical framing (object-fit / object-position / scale) admin saved -
   the rotation must never reframe the artwork mid-cycle.
   ⚠️ `.collab-cover-img.collab-cover-next`, not `.collab-cover-next`: the element carries BOTH
   classes, and `.collab-cover-img` above sets `position: relative`. At equal specificity the later
   rule wins, which is exactly how this shipped broken the first time. */
/* Superseded: the crossfade is now done by .cvr-layer-b, one opaque group at a time. This element
   is just the picture inside that group. */
.collab-cover-img.collab-cover-next { position: absolute; inset: 0; }


/* Used for one frame by the settle step, to put the incoming layer back to transparent WITHOUT
   animating it - base already holds the same picture underneath, so there is nothing to dissolve. */
.collab-cover-img.collab-cover-next.is-instant { transition: none !important; }

/* While a clip is playing in the cover BOTH still layers step aside. The shared engine only knows
   how to fade one `.hover-poster-img`; with two stacked layers the other one would sit on top of
   the video (the crossfade layer even shares its z-index). */
/* ONLY the crossfade layer, never `.collab-cover-img` as a whole. playHoverVideoInternal names the
   <video> it injects `collab-cover-img` too (so it inherits the cover's sizing), so a blanket rule
   here hides the very clip it is trying to reveal - measured, the video computed to opacity 0.
   The base still is left to the shared engine, which fades it inline on `playing` and deliberately
   brings it BACK if the clip stalls; overriding that would replace a stalled clip with a blank box.
   `.is-shown` on the next layer is specificity 0,3,0, so this has to be 0,4,0 to win. */
.pif-rotator.is-video-preview .collab-cover-img.collab-cover-next {
    opacity: 0;
    transition: opacity 260ms ease;
}

/* The clip fills the 9:16 frame the same way a still does: whole, centred, wash behind it. */
.collab-cover .video-placeholder-wrap video {
    object-fit: contain;
    z-index: 2;
}

.collab-cover[data-fit="cover"] .video-placeholder-wrap video { object-fit: cover; }

/* The incoming layer used to start 4.5% larger and ease back over 1400ms. It has to stay at its
   framed scale instead: the feather stops are computed from the UNSCALED layout, so while the
   picture was scaling its real edge and its fade sat in different places for the whole transition -
   the "테두리 페더랑 포스터가 각각 따로 노는" effect. Opacity is the only thing that animates now, so
   the two are always in register. --cvr-zoom is still honoured; it is just no longer multiplied. */
.collab-cover-img.is-entering { transform: scale(var(--cvr-zoom, 1)); }


/* Reduced motion: swap instantly and never scale. */
@media (prefers-reduced-motion: reduce) {
    .collab-cover-img.collab-cover-next { transition: none; }
    .collab-cover-img.is-entering { transform: scale(var(--cvr-zoom, 1)); }
}

/* The 9:16 frame is fixed, the artwork inside it is not: a poster is 4:5 or 3:4, a clip is 9:16,
   and a landscape cover is 16:9. Rather than crop them all to fit (which is what mis-framed every
   rotation slide - the saved object-position was tuned for ONE image and then applied to every
   other one), the artwork is shown WHOLE and the leftover space is filled with a blurred, dimmed
   copy of the same picture. It reads as part of the artwork instead of as a letterbox.
   Not scoped to [data-fit="contain"] any more: it is the standard treatment for the cover. */
/* The wash used to live here, on the rotator, shared by both pictures - which is exactly what made
   it fade independently of them. It now belongs to each .cvr-layer group. This pseudo-element is
   kept only as the ground colour beneath the groups. */
.pif-rotator::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    background: #151515;
    pointer-events: none;
}

/* A second, darker layer only at the very top and bottom - the two edges a portrait poster leaves
   open. It keeps the frame from washing out at full brightness while the middle, where the poster
   actually sits, stays bright enough to look like one continuous image. */
.pif-rotator::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    /* A vignette, not two bands. The linear version drew a visible horizontal edge exactly where
       the poster ended, which is the opposite of blending; a radial falloff has no edge anywhere
       and just keeps the frame from going flat. */
    background: radial-gradient(135% 85% at 50% 50%,
        rgba(0, 0, 0, 0) 62%,
        rgba(0, 0, 0, 0.06) 82%,
        rgba(0, 0, 0, 0.17) 100%);
}

.collab-cover[data-fit="cover"]::after { display: none; }

/* Explicit stacking, because both wash layers are PSEUDO-elements and therefore the last children
   in tree order: at z-index auto they would paint over the artwork, the logo and the play button
   rather than behind them. Order is wash (::before 0) -> edge gradient (::after 0, later in tree)
   -> artwork (1) -> logo / play (3). */
.collab-cover .collab-cover-img { z-index: 1; }
.collab-cover > .collab-logo { z-index: 3; }

/* Feather the artwork's own edges so it melts into the wash instead of ending on a hard line -
   that hard line is what made the two read as separate images. Vertical only: a portrait poster
   fills the width, so fading the sides would just dim the artwork's own edges for no reason. */
.pif-rotator .collab-cover-img {
    /* Stops come from pifCoverFade(), which computes where the picture ACTUALLY ends inside this
       element - object-fit:contain means that is nowhere near the element's own edge. The defaults
       below are a no-op feather, so an image whose ratio is not known yet is never wrongly faded. */
    --cvr-m1: 0%;
    --cvr-m2: 0%;
    --cvr-m3: 100%;
    --cvr-m4: 100%;
    -webkit-mask-image: linear-gradient(to bottom,
        transparent var(--cvr-m1), #000 var(--cvr-m2), #000 var(--cvr-m3), transparent var(--cvr-m4));
    mask-image: linear-gradient(to bottom,
        transparent var(--cvr-m1), #000 var(--cvr-m2), #000 var(--cvr-m3), transparent var(--cvr-m4));
}

/* FILL crops to the frame, so there is no wash to blend into and no edge to feather. */
.collab-cover[data-fit="cover"] .collab-cover-img {
    -webkit-mask-image: none;
    mask-image: none;
}

/* FILL is still available per collaboration and crops to the frame, so it needs no wash. */
.collab-cover[data-fit="cover"]::before { display: none; }

.collab-cover-empty {
    background: linear-gradient(135deg, rgba(212, 212, 212, 0.12), rgba(0, 0, 0, 0.4));
}

.collab-logo {
    position: absolute;
    left: 14px;
    bottom: 14px;
    max-width: 116px;
    max-height: 46px;
    object-fit: contain;
    filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.85));
}

.collab-body {
    padding: 22px 24px;
    display: flex;
    flex-direction: column;
    gap: 9px;
    min-width: 0;
}

.collab-kind {
    align-self: flex-start;
    font-size: 0.62rem;
    font-weight: 900;
    letter-spacing: 1.6px;
    padding: 4px 10px;
    border-radius: 4px;
    /* White lettering on a filled chip. An amber glyph on an amber-tinted chip is the exact
       red-on-red pattern that made COMING SOON and the contact icon unreadable - not repeating it. */
    color: #fff;
    background: rgba(212, 212, 212, 0.16);
    border: 1px solid var(--neon-party);
}

.collab-name {
    margin: 0;
    font-size: 1.22rem;
    font-weight: 900;
    color: #fff;
    letter-spacing: 0.4px;
    line-height: 1.2;
}

.collab-tagline {
    margin: 0;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--neon-party);
}

.collab-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 7px;
    font-size: 0.76rem;
    color: rgba(255, 255, 255, 0.6);
}

.collab-meta i {
    font-style: normal;
    color: rgba(255, 255, 255, 0.25);
}

.collab-desc {
    margin: 2px 0 0;
    font-size: 0.85rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.75);
    white-space: pre-line; /* admin textarea keeps its paragraph breaks */
}

.collab-links {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 2px;
}

.collab-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.76rem;
    font-weight: 700;
    text-decoration: none;
    padding: 6px 12px;
    border-radius: 6px;
    color: #fff;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.16);
    transition: background 0.2s ease, border-color 0.2s ease;
}

.collab-link:hover {
    background: rgba(212, 212, 212, 0.18);
    border-color: var(--neon-party);
}

.collab-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 6px;
}

.collab-thumb {
    position: relative;
    width: 74px;
    height: 74px;
    padding: 0;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: #0d0d0d;
    cursor: pointer;
    transition: border-color 0.2s ease, transform 0.2s ease;
}

.collab-thumb:hover,
.collab-thumb:focus-visible {
    border-color: var(--neon-party);
    transform: translateY(-2px);
    outline: none;
}

.collab-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.collab-thumb-play {
    z-index: 3;
    position: absolute;
    right: 5px;
    bottom: 5px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.72);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.5rem;
    padding-left: 1px;
    pointer-events: none;
}

/* Inline collaboration previews preserve framing and never intercept lightbox clicks. */
.collab-thumb .video-placeholder-wrap,
.collab-cover .video-placeholder-wrap {
    position: absolute;
    inset: 0;
    display: block;
    pointer-events: none;
}
.collab-preview-still {
    position: relative;
    display: block;
    width: 100%;
    height: 100%;
    z-index: 2;
    pointer-events: none;
}
.collab-cover > .hover-poster-img { z-index: 2; }

/* ═══ COVER STACKING — SINGLE SOURCE OF TRUTH ═══════════════════════════════════════════════════
   Declared last on purpose: every z-index for this component is decided HERE and nowhere else.

     0  wash (::before) and edge gradient (::after)
     1  base still
     2  crossfade layer   <- MUST be above the base
     4  playing clip
     5  logo / play button

   Why it exists. The base layer carries `.hover-poster-img` (the shared video engine finds the
   still to fade by that class), and the older rule directly above gives `.hover-poster-img`
   z-index 2 - while the crossfade layer had 1. The pair was inverted, which produced all three
   reported faults at once, alternating swap by swap:
     - a swap targeting the crossfade layer faded it in UNDERNEATH an opaque base   -> "호버해도 안 바뀜"
     - a swap targeting the base changed its src in place, on top                    -> "슬라이드 효과 없음"
     - mid-dissolve both were partly visible                                        -> two pictures at once
   Add a z-index to any of these elements somewhere else and that returns. */
.collab-cover > .collab-cover-img,
.collab-cover > .hover-poster-img { z-index: 1; }

.collab-cover > .collab-cover-img.collab-cover-next { z-index: 2; }

.collab-cover > .video-placeholder-wrap { z-index: 4; }

/* The wash is no longer a layer of its own - see the .cvr-layer block below. It lives inside each
   slide group, so it can only ever fade together with the picture it belongs to. */

/* ═══ SLIDE LAYERS — one opaque unit per picture ═══════════════════════════════════════════════
   Each slide is a GROUP: its own blurred wash plus its own picture, and only the GROUP's opacity
   animates. This is not a style choice, it is the only arrangement that does not blink.

   Fading the wash and the picture as separate layers composites to
       next*a + base*(1-a)^2 + wash*a*(1-a)
   so at mid-transition 25% of the blurred copy bleeds through the artwork - a brightness dip on
   every single transition, which is the reported 깜빡임. A dip-free cross-dissolve needs the BOTTOM
   layer to stay fully opaque: next*a + base*(1-a) = 1 only while base stays at 1. Grouping makes
   each unit opaque across the whole box (the wash fills it), so fading one in over the other is
   exact at every frame.

   It also takes the feather out of the dissolve. The mask lives on the <img> INSIDE a group, so the
   picture blends into ITS OWN wash at the mask ratio regardless of the group's opacity - where
   before the mask multiplied with the animating opacity (M(y) * a) and the feather band always
   lagged the body of the poster, peaking mid-transition. That was the "페더가 따로 논다" report. */
.pif-rotator > .cvr-layer {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

.pif-rotator > .cvr-layer-a { z-index: 1; opacity: 1; }

.pif-rotator > .cvr-layer-b {
    z-index: 2;
    opacity: 0;
    transition: opacity 900ms cubic-bezier(0.4, 0, 0.2, 1);
}

.pif-rotator > .cvr-layer-b.is-shown { opacity: 1; }
/* Used for one frame at settle, to return the incoming group to transparent without animating it. */
.pif-rotator > .cvr-layer.is-instant { transition: none !important; }

/* The wash belongs to its group, not to the rotator. Overscanned so the blur is only ever sampled
   from the middle of the layer and never drags a soft edge in against the frame. */
.pif-rotator .cvr-wash {
    position: absolute;
    inset: -18%;
    background: var(--wash) center / cover no-repeat;
    /* Static. The muted state is NOT applied here - see the .cvr-layer rule for why. */
    filter: blur(var(--cvr-blur, 22px)) brightness(1) saturate(1.22) contrast(0.96);
    pointer-events: none;
}

/* Inside a group the picture is simply the top of that group. */
.pif-rotator .cvr-layer .collab-cover-img {
    position: absolute;
    inset: 0;
    z-index: 1;
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .pif-rotator > .cvr-layer-b { transition: none; }
}


/* `.gig-card .item-media` paints a solid #151515 behind every layer. Inside a rotator that ground
   would sit ON TOP of the wash, so the letterbox bands would stay flat dark and the wash would never
   be seen at all. The layers have to be transparent for the surround to read through them. */
.pif-rotator .item-media { background-color: transparent !important; }

.pif-rotator { position: relative; overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
}

.collab-cover > .collab-logo,
.collab-cover > .aftermovie-play { z-index: 5; }

/* ── Crossing to and from a clip ────────────────────────────────────────────────────────────────
   Neither side of this had a transition, which is why it cut hard: the shared engine sets the
   still's opacity INLINE (0 on `playing`), and the <video> it injects carries inline geometry with
   no opacity at all. Both are given one here.

   :not(.collab-cover-next) matters - without it this would replace the crossfade layer's own
   900ms/1400ms transition and break the slide dissolve. */
.collab-cover > .collab-cover-img:not(.collab-cover-next) {
    /* Fast by default so the still SNAPS back when a preview ends (the class below is removed
       before the engine restores it), and slow only while a clip is taking over. */
    transition: opacity 140ms linear;
}

/* ⚠️ The outgoing picture has to DISSOLVE, it cannot just be covered.
   The incoming layer only hides the outgoing one where it is itself opaque, and under
   object-fit: contain that is only the rectangle its own artwork occupies. Whenever the incoming
   slide is letterboxed SHORTER than the outgoing one, the remainder of the old picture is not
   covered by anything and simply survives the whole dissolve - then vanishes in a single frame when
   settle() swaps the layers at 980ms. TRANCE4 SEOUL is the worst case on this site: going from a
   9:16 slide (artwork y 0..676) to the 16:9 cover (artwork y 231..445) leaves 231px at the top and
   231px at the bottom uncovered, so ~68% of the frame hard-cuts while the middle third dissolves.
   That is the "TRANCE4 쪽은 페이드가 안 들어갔다" report, and it is why the desync survived the
   per-layer feather fix - neither touched who is responsible for removing the old picture.
   Same duration and curve as .collab-cover-next, so the two halves of the dissolve are symmetric. */

/* Restores the outgoing layer with no animation once it has been handed the new picture. */
.collab-cover > .collab-cover-img.is-instant { transition: none !important; }

/* Event cards get the leaving dissolve ONLY when they letterbox. Under `cover` the incoming picture
   fills the box and fully hides the outgoing one, so fading BOTH would composite
   next_a + base_a*(1-next_a) = 0.75 at the midpoint and dip 25% toward the card's #151515 ground -
   a visible darkening in the middle of every transition. The collaboration covers can afford it
   because their cross-fading wash sits behind and is nearly the same colour; a gig card has no wash. */

@media (prefers-reduced-motion: reduce) {
    .pif-rotator > .cvr-layer-b { transition: none; }
}

.pif-rotator.is-video-preview > .collab-cover-img:not(.collab-cover-next) {
    transition: opacity 520ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* The clip fades IN. Scoped to .collab-cover so the gig grid and the thumbnail strip, which use the
   same engine, keep their existing instant behaviour. Revealed by .is-video-preview, which is added
   from the clip's own `playing` event - so it fades in exactly when there is a frame to show. */
.pif-rotator .video-placeholder-wrap video {
    opacity: 0;
    transition: opacity 520ms cubic-bezier(0.4, 0, 0.2, 1);
}

.pif-rotator.is-video-preview .video-placeholder-wrap video { opacity: 1; }

/* The clip plays ABOVE both slide groups. Each group is now opaque across the whole box (it carries
   its own wash), so a clip left underneath would be completely hidden - and hiding the groups
   instead would take the wash away with them and put black bars back around a letterboxed clip.
   !important because the event-card template sets an inline z-index on this wrap. */
.pif-rotator > .video-placeholder-wrap { z-index: 5 !important; }
.pif-rotator > .collab-logo,
.pif-rotator > .aftermovie-play { z-index: 6; }

/* While a clip runs, only the PICTURES step aside; the groups stay so their wash still backs the
   clip. Overrides the group-level rule above, which sets opacity 1 on the same element. */
.pif-rotator.is-video-preview .cvr-layer .collab-cover-img { opacity: 0 !important; }

/* ⚠️ !important, and it is load-bearing. `restoreStill` in the shared engine puts the still back
   with an INLINE opacity:1 on `pause`, `waiting`, `stalled` and `emptied` - so a single buffering
   stutter mid-playback re-revealed the poster while the clip was still on screen, which is the
   "영상과 이미지 포스터가 겹쳐서 나오는" report. Only an !important rule can outrank an inline style.
   The child combinator keeps this off the injected <video>, which also carries .collab-cover-img
   but sits one level deeper inside .video-placeholder-wrap. */
.pif-rotator.is-video-preview > .collab-cover-img { opacity: 0 !important; }

/* ⚠️ On an event card the clip sits at z-index 1, BELOW both still layers (2 and 3). The engine
   inline-hides only the base still (it knows a single `.hover-poster-img`), so the CROSSFADE layer
   was free to fade a new poster in on top of a clip that was still playing - reported as "영상이 아직
   나오고 있는데 그 위에 겹치는 식으로 포스터가 나와버린다". Hiding BOTH layers from one rule is what
   stops it; these were scoped to .collab-cover, which is why only the covers were ever safe. */
.collab-cover > .collab-logo,
.collab-cover > .aftermovie-play { z-index: 3; }
.collab-thumb .party-video-media { object-fit: cover; transform: none; }
.collab-thumb[data-fit="contain"] .party-video-media { object-fit: contain; }
.collab-thumb.inline-preview-playing { border-color: var(--neon-party); }

/* The collab card is the only two-column block in the tab, so it collapses first. */
@media (max-width: 900px) {
    .collab-card { grid-template-columns: 1fr; }

    /* The desktop cover needs a 260px floor because it stretches beside a tall text column.
       Once the card stacks on a phone that floor must be removed: at a typical 340px card width
       it overrode 16:9's ~191px height and made the live crop ~4:3, while admin correctly previews
       a true 16:9 box. Keeping the exact same box shape makes object-position / zoom WYSIWYG. */
    /* The cover keeps the SAME 9:16 frame here as on desktop (2026-09-01). It used to flip to a
       wide 16/9 banner on a phone, which meant a second set of framing values, a second admin
       preview, and a shape that fought the artwork: collaboration posters are always portrait.
       One frame everywhere removes all of that, and the blurred wash handles whatever ratio the
       picture happens to be. */
    .collab-body { padding: 18px 18px 20px; }
}

@media (max-width: 768px) {
    .aftermovie-strip { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
    .aftermovie-section,
    .collab-section { margin-top: 38px; padding-top: 24px; }
    .collab-name { font-size: 1.08rem; }
    .collab-thumb { width: 64px; height: 64px; }
}

@media (prefers-reduced-motion: reduce) {
    .aftermovie-card,
    .collab-thumb,
    .aftermovie-play { transition: none; }
    .aftermovie-card:hover,
    .collab-thumb:hover { transform: none; }
    .aftermovie-card:hover .aftermovie-play,
    .collab-cover:hover .aftermovie-play { transform: translate(-50%, -50%); }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   DEMO SUBMISSION — open state (2026-08-22)
   The card used to be a "coming soon" placeholder; it now carries a live submission address and
   a response policy, so it needs real structure rather than one paragraph.
   Text colours here are WHITE, not the signature red. The red still reads from the card's dashed
   border, the badge fill and the icons — putting red lettering on this card's own red-tinted
   surfaces is the pattern that made COMING SOON and the contact icon unreadable on a phone.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

.demo-open-line {
    font-size: 0.86rem;
    color: rgba(255, 255, 255, 0.9);
    margin: 0 0 10px;
}

.demo-submit-box {
    margin: 22px auto 18px;
    max-width: 420px;
    padding: 18px 20px;
    border-radius: 12px;
    background: rgba(242, 17, 44, 0.07);
    border: 1px solid rgba(242, 17, 44, 0.35);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 9px;
}

.demo-submit-label {
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 1.4px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
}

/* Monospace + selectable: the visible form is obfuscated, so somebody reading it off the screen
   has to retype it, and a fixed-width face makes "[at]" / "[dot]" unambiguous. */
.demo-email {
    font-family: 'Courier New', ui-monospace, monospace;
    font-size: 0.95rem;
    font-weight: 700;
    color: #fff;
    letter-spacing: 0.4px;
    user-select: text;
    word-break: break-word;
}

.demo-submit-btn {
    margin-top: 4px;
}

.demo-policy {
    list-style: none;
    margin: 0 auto 20px;
    padding: 0;
    max-width: 470px;
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.demo-policy li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 0.8rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.72);
}

.demo-policy strong {
    color: #fff;
}

/* The icon is the only red element in the list and it sits on the card's own background rather
   than on a red tint, so it keeps the accent colour. */
.demo-policy li i {
    color: var(--neon-live);
    font-size: 0.8rem;
    flex: 0 0 auto;
    margin-top: 3px;
}

@media (max-width: 768px) {
    .demo-submit-box { padding: 16px 14px; margin: 18px auto 16px; }
    .demo-email { font-size: 0.84rem; }
    .demo-policy { max-width: 100%; }
    .demo-policy li { font-size: 0.76rem; }
}

/* ── Gallery groups + video-thumb fallback (2026-08-22) ─────────────────────────────────── */

/* Shown only when there is more than one group to tell apart. Spans the whole grid row so the
   tiles below it stay on the normal column rhythm. */
.insta-feed-group-label {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 6px 0 2px;
    font-size: 0.72rem;
    font-weight: 900;
    letter-spacing: 1.6px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.82);
}

.insta-feed-group-label::after {
    content: '';
    flex: 1 1 auto;
    height: 1px;
    background: rgba(255, 255, 255, 0.1);
}

.insta-feed-group-label em {
    font-style: normal;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.5px;
    padding: 2px 7px;
    border-radius: 10px;
    color: #fff;
    background: rgba(212, 212, 212, 0.18);
    border: 1px solid rgba(212, 212, 212, 0.55);
    order: 3;   /* count sits after the rule, not between label and rule */
}

/* Stands in for a video thumbnail whose poster could not be generated. Never point an <img> at
   the clip itself — it cannot render and the browser downloads the whole file first. */
.collab-thumb-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255, 255, 255, 0.35);
    font-size: 1rem;
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(0, 0, 0, 0.35));
}

/* ── ABOUT US: partnerships & residencies (2026-08-23) ──────────────────────────────────
   Reuses .pif-bio-card so it sits in the same rhythm as BRAND BIOGRAPHY. Only the per-entry
   chrome is new. Role chips are white-on-tint, never red-on-red — same rule as .demo-badge. */

.pif-partners-card .partner-entry + .partner-entry {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.partner-role {
    display: inline-block;
    font-size: 0.62rem;
    font-weight: 900;
    letter-spacing: 1.5px;
    padding: 4px 10px;
    border-radius: 4px;
    color: #fff;
    background: rgba(242, 17, 44, 0.16);
    border: 1px solid var(--neon-live);
    margin-bottom: 9px;
}

.partner-name {
    margin: 0 0 4px;
    font-size: 1.02rem;
    font-weight: 900;
    color: #fff;
    letter-spacing: 0.3px;
    line-height: 1.25;
}

.partner-where {
    display: block;
    font-size: 0.74rem;
    color: rgba(255, 255, 255, 0.55);
    margin-bottom: 9px;
}

.partner-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 18px;
    font-size: 0.78rem;
    font-weight: 700;
    text-decoration: none;
    padding: 8px 14px;
    border-radius: 6px;
    color: #fff;
    background: rgba(255, 255, 255, 0.07);
    border: 1px solid rgba(255, 255, 255, 0.15);
    transition: background 0.2s ease, border-color 0.2s ease;
}

.partner-link:hover {
    background: rgba(242, 17, 44, 0.18);
    border-color: var(--neon-live);
}

@media (max-width: 768px) {
    .partner-name { font-size: 0.94rem; }
    .partner-role { font-size: 0.58rem; letter-spacing: 1.1px; }
}

/* ── Residency sessions: a dated playlist inside the collaboration card (2026-08-24) ─────
   Oldest first, the OPPOSITE of the party grids above. A party list answers "what is on next",
   so newest leads; a residency is a run of nights and a run reads forward. */

.collab-sessions {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid rgba(255, 255, 255, 0.09);
}

.collab-sessions-head {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.68rem;
    font-weight: 900;
    letter-spacing: 1.6px;
    color: rgba(255, 255, 255, 0.62);
    margin-bottom: 8px;
}

.collab-sessions-head em {
    font-style: normal;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.5px;
    padding: 2px 7px;
    border-radius: 10px;
    color: #fff;
    background: rgba(212, 212, 212, 0.18);
    border: 1px solid rgba(212, 212, 212, 0.5);
}

.collab-session-list {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Long residencies stay inside the card instead of stretching it down the page. */
    max-height: 420px;
    overflow-y: auto;
    scrollbar-width: thin;
}

.collab-session {
    padding: 10px 0;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    /* A residency accumulates rows forever - four sessions is already ~31 thumbnails, and every
       one of them costs style, layout and paint on every frame whether or not it is on screen.
       content-visibility lets the browser skip all three for rows scrolled out of view, which is
       the single biggest saving available on a weak CPU and changes nothing about how it looks.
       `auto` on contain-intrinsic-size means the row remembers its real height after its first
       render, so the scrollbar stops jumping once a row has been seen.
       Ignored by browsers that do not support it - it degrades to exactly today's behaviour. */
    content-visibility: auto;
    contain-intrinsic-size: auto 116px;
}

.collab-session:first-child { border-top: none; padding-top: 2px; }

/* The head is a <summary> now. Everything below is about making a disclosure control look
   exactly like the row it replaced - no visual change on desktop, where every row ships open. */
.collab-session-head {
    display: flex;
    align-items: baseline;
    gap: 9px;
    flex-wrap: wrap;
    cursor: pointer;
    list-style: none;          /* kills the default triangle in Firefox */
    user-select: none;
}

.collab-session-head::-webkit-details-marker { display: none; }   /* ...and in WebKit */

.collab-session-head:focus-visible {
    outline: 1px solid var(--neon-party);
    outline-offset: 3px;
    border-radius: 4px;
}

/* Hidden on desktop: with every row open a chevron would be pure noise. It only earns its place
   at the width where rows actually collapse. */
.collab-session-caret {
    display: none;
    margin-left: auto;
    font-size: 0.62rem;
    color: rgba(255, 255, 255, 0.35);
    transition: transform 0.2s ease;
    flex: 0 0 auto;
}

.collab-session-fold[open] .collab-session-caret { transform: rotate(180deg); }

/* Sits with the SESSIONS heading. Hidden on desktop for the same reason as the chevron. */
.collab-sessions-all {
    display: none;
    margin-left: auto;
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.55);
    font-size: 0.58rem;
    font-weight: 800;
    letter-spacing: 0.8px;
    padding: 3px 9px;
    border-radius: 4px;
    cursor: pointer;
}

.collab-sessions-all:hover { color: var(--neon-party); border-color: var(--neon-party); }

/* The running number is what makes it read as a series rather than a pile of dates. */
.collab-session-no {
    font-size: 0.64rem;
    font-weight: 900;
    color: rgba(255, 255, 255, 0.32);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

.collab-session-date {
    font-size: 0.8rem;
    font-weight: 800;
    color: #fff;
    font-variant-numeric: tabular-nums;
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
}

.collab-session-date em {
    font-style: normal;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.8px;
    color: var(--neon-party);
}

.collab-session-title {
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.72);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Opens the night's full detail modal - the same one the UPCOMING card opens. Sits in the summary
   next to the caret, so the row still toggles everywhere except on this button. Only rendered for
   an edition that actually has a modal (see pifCollabEditionEventId). */
.collab-session-detail {
    flex: 0 0 auto;
    margin-left: 8px;
    padding: 3px 9px;
    font: 800 0.58rem/1.6 inherit;
    letter-spacing: 1px;
    color: var(--neon-party);
    background: rgba(242, 17, 44, 0.10);
    border: 1px solid rgba(242, 17, 44, 0.45);
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.collab-session-detail:hover,
.collab-session-detail:focus-visible {
    background: var(--neon-party);
    /* White on the red fill, pinned through -webkit-text-fill-color as well: that property
       outranks `color`, and it is the exact trap that made .demo-badge and .method-icon
       unreadable when only `color` was set. */
    color: #fff;
    -webkit-text-fill-color: #fff;
    border-color: var(--neon-party);
}

.collab-session-count {
    margin-left: auto;
    font-size: 0.6rem;
    font-weight: 800;
    color: rgba(255, 255, 255, 0.5);
    background: rgba(255, 255, 255, 0.07);
    border-radius: 9px;
    padding: 2px 7px;
    flex: 0 0 auto;
}

.collab-session-note {
    margin: 5px 0 0;
    font-size: 0.74rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.55);
    white-space: pre-line;
    /* A full event description runs to ~900 characters, and letting it set the session's height
       pushed the media strip far enough down that it was off screen while you were reading. The
       copy scrolls inside its own box instead, so the strip below it stays where it was.
       `overscroll-behavior: contain` stops a flick inside the note from carrying on into the page
       once it reaches the end. */
    max-height: 13.5em;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 8px;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
    /* Fades the last line rather than slicing it, so it reads as "there is more" instead of as a
       layout bug. The top fade only appears once scrolled, via the same mask. */
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.6em), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 1.6em), transparent 100%);
}

/* Once the reader reaches the bottom there is nothing left to hint at, so drop the fade. */
.collab-session-note.is-scrolled-end {
    -webkit-mask-image: none;
    mask-image: none;
}

.collab-session-note::-webkit-scrollbar { width: 6px; }
.collab-session-note::-webkit-scrollbar-track { background: transparent; }
.collab-session-note::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.22);
    border-radius: 3px;
}
.collab-session-note::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.38); }

/* One horizontal run per session — the playlist row. Scrolls sideways rather than wrapping,
   so each date stays one line tall and the list keeps its rhythm. */
.collab-session-media {
    display: flex;
    gap: 7px;
    /* overflow-x:auto also establishes a vertical clipping edge. Keep the same 8px visual gap,
       but move 3px of it inside the scroller so the -2px thumbnail hover lift and its top border
       remain inside the paintable area instead of being sliced off. */
    margin-top: 5px;
    overflow-x: auto;
    padding: 3px 1px 4px;
    scrollbar-width: thin;
    scroll-snap-type: x proximity;
}

.collab-session-media .collab-thumb {
    flex: 0 0 auto;
    width: 84px;
    height: 84px;
    scroll-snap-align: start;
}

/* object-fit is set per item from its stored `fit`, so this must not force one. */
.collab-thumb img {
    width: 100%;
    height: 100%;
    display: block;
}

/* ── Sessions on a narrow screen ───────────────────────────────────────────────────────
   Placed AFTER the base rules on purpose. A media query adds no specificity, so an override
   written above the rule it targets simply loses to the later declaration - which is exactly why
   the chevron stayed hidden on a phone the first time round. Placement is the fix, not !important.
   900px is used rather than 768 because that is where .collab-card drops to one column: above it
   the sessions sit in their own tall column and the scroller is fine, below it they are stacked
   under the cover and everything after them gets pushed off-screen. */
@media (max-width: 900px) {
    /* The 420px inner scroller is dropped, not shrunk. A scrollable box inside a scrollable page
       is the worst thing to hand a touch screen: a swipe meant for the page gets eaten by the
       list and the reader cannot tell which one they are moving. Rows collapse instead, so the
       card stays short without trapping anything. */
    .collab-session-list {
        max-height: none;
        overflow-y: visible;
    }

    /* These only earn their place at the width where rows actually collapse. */
    .collab-session-caret { display: inline-block; }
    .collab-sessions-all { display: inline-block; }

    /* A closed row is one tappable line, so give it a real target instead of a hairline. */
    .collab-session-head {
        padding: 4px 0;
        min-height: 32px;
        align-items: center;
    }
}

@media (max-width: 768px) {
    .collab-session-list { max-height: none; }
    .collab-session-media .collab-thumb { width: 72px; height: 72px; }
    .collab-session-title { flex-basis: 100%; white-space: normal; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   터치 기기 스크롤 성능 — 파일 맨 끝이어야 합니다

   ⚠️ 이 블록은 **반드시 마지막**에 있어야 합니다. 처음에 파일 중간(앰비언트 글로우 앞)에
      넣었더니 `.past-gig-card .cvr-layer` 의 transition 해제가 **먹지 않았습니다** - 뒤쪽
      1467행의 같은 특이도(0,2,0) 규칙이 소스 순서로 이겼기 때문입니다. 미디어 쿼리는
      특이도를 더해 주지 않습니다. 이 블록을 위로 옮기지 마십시오.

   2026-09-19 보고: 터치 태블릿에서 party 탭 스크롤이 거의 되지 않음. 실측한 원인 셋.
     1. `.content-section` 은 **스크롤 컨테이너 자신**인데(party 기준 내용 높이 15,650px)
        backdrop-filter 가 걸려 있어 스크롤 매 프레임 그 큰 면을 다시 블러했습니다.
     2. `.party-toc` 는 `position: sticky; top: 0` 이라 스크롤 내내 화면에 붙어 있는데
        거기에도 `blur(12px) saturate(1.06)` 이 걸려 있었습니다. 배경이 완전 투명이라
        블러를 빼면 글자가 내용 위에 바로 앉으므로 불투명 배경으로 대체합니다.
     3. `will-change` 가 걸린 요소가 **144개**였습니다. 리빌 규칙이 아직 도착하지 않은
        `.pif-reveal` 에 `will-change: opacity, transform` 을 걸어 두는데, 화면 아래
        요소들은 영영 도착하지 않으므로 합성 레이어가 계속 고정됩니다. 태블릿에서는
        이것만으로 합성기가 허덕입니다.

   `any-pointer: coarse` 를 씁니다. `pointer: coarse` 가 아닙니다 - 서피스처럼 키보드를
   붙이면 주 포인터가 fine 으로 잡히지만 손가락으로도 쓰는 기기를 놓치기 때문입니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (any-pointer: coarse) {

    /* 1. 스크롤 컨테이너의 블러 */
    .content-section {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        /* 원래 0.92 였습니다. 블러가 빠진 만큼 불투명도로 메웁니다. */
        background-color: rgba(21, 21, 21, 0.975);
    }

    /* 2. sticky 목차의 블러는 **css/party-nav.css 끝**에서 끕니다.
       그 파일이 이 파일보다 뒤에 실려서 여기 쓰면 먹지 않습니다(실측). */

    /* 3. 도착하지 않은 카드들에 고정돼 있는 합성 레이어
       !important 가 필요합니다. apple-refinements.css 의 리빌 규칙이 이 값을 직접
       선언하고 있고 그 파일이 뒤에 실리기 때문입니다. 애니메이션은 그대로 돕니다 -
       will-change 는 힌트일 뿐입니다. */
    .pif-reveal {
        will-change: auto !important;
    }

    /* 4. 스크롤 중 in-view 가 켜졌다 꺼지며 0.35초짜리 재도색을 계속 만듭니다.
       흑백/컬러 자체는 그대로 두고 전환만 없앱니다. */
    .past-gig-card .cvr-layer,
    .past-gig-card .item-media:not(.collab-cover-img) {
        transition: none;
    }

    /* ⚠️ `.pif-hero-video { transition: none }` 을 여기 다시 넣지 마십시오.
       스크롤 최적화 때 넣었다가 **모바일에서 크로스페이드가 통째로 사라졌습니다** - 5초마다
       하드 컷이 되어 "전환될 때 툭 튄다" 로 보고됐습니다(2026-09-19).
       이 전환은 opacity 하나뿐이라 합성기에서만 돌고, 스크롤 중에는 어차피 간판을 멈추므로
       스크롤 프레임과 겹치지 않습니다. 무거운 것은 backdrop-filter 와 will-change 였고
       그 둘은 이 블록 위에서 이미 껐습니다. */
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   투명한 헤더는 포인터를 통과시킨다 (2026-09-19)

   `#main-header` 는 배경이 **완전 투명**인데도 `position: fixed` + `z-index: 999` 라 화면 위
   48px 띠의 클릭을 전부 자기가 먹습니다. 그 아래에 붙어 있는 `.party-toc`(z-index 60)는
   그래서 **칩을 눌러도 아무 일도 일어나지 않습니다** - 스크롤을 내려 바가 꼭대기에 붙은
   뒤에만 그렇고, 맨 위에서는 바가 헤더 아래에 있으니 잘 눌립니다. 보고된 증상과 정확히
   일치하고, elementFromPoint 로 확인했습니다: 칩 5개 전부 맞는 것이 `HEADER` 였습니다.

   ⚠️ z-index 를 올려 해결하지 마십시오. TOC 를 헤더 위로 올리면 그 불투명한 배경이 로고와
      햄버거를 덮어 버립니다.

   빈 곳은 통과시키고 **실제 조작할 것들만** 다시 받게 합니다. 자식을 새로 추가하면 여기
   목록에도 넣어야 합니다 - 빠뜨리면 그 요소는 눌리지 않습니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
#main-header {
    pointer-events: none;
}

#main-header > *,
#main-header .logo,
#main-header .header-nav-tabs,
#main-header .header-nav-tabs a,
#main-header .mobile-hamburger-btn,
#main-header button,
#main-header a {
    pointer-events: auto;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   화면 밖 카드는 아예 그리지 않는다 (2026-09-19)

   party 탭은 내용 높이가 15,650px 이고 그 안에 `filter: grayscale()` 이 걸린 요소가 45개
   있습니다. 메인 스레드는 깨끗한데(실측: 중앙값 16ms, p99 16.5ms, 50ms 넘는 멈춤 0회)
   태블릿에서 스크롤이 무겁다면 남는 것은 **합성·페인트 비용**입니다. 필터가 걸린 큰 그림
   수십 장을 화면 밖에서도 계속 들고 있는 것이 그 비용의 대부분입니다.

   `content-visibility: auto` 는 화면 밖 요소의 레이아웃·페인트를 통째로 건너뜁니다.

   ⚠️ `contain-intrinsic-size` 없이 쓰면 화면 밖 요소가 높이 0 으로 접혀 스크롤이 요동칩니다.
      `auto <크기>` 형태는 **마지막으로 그렸던 크기를 기억**하므로 그 요동이 없습니다. 그래서
      둘 다 지원할 때만 켭니다 - 하나라도 없으면 규칙 전체가 무시되어 지금 모습 그대로입니다.

   ⚠️ 안에 `position: sticky` 가 있는 컨테이너에는 걸지 마십시오. 화면 밖에서 렌더가
      건너뛰어지면 sticky 계산이 어긋납니다. 그래서 `.party-toc` 를 품은 `.content-section`
      자체가 아니라 **반복되는 카드들**에만 겁니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@supports (content-visibility: auto) and (contain-intrinsic-size: auto 300px) {
    @media (any-pointer: coarse) {
        .past-gig-card,
        .gigs-grid > .gig-card,
        .collab-card,
        .insta-feed-item {
            content-visibility: auto;
            contain-intrinsic-size: auto 420px;
        }

        /* 아프터무비 스트립은 가로로 흐르므로 폭도 기억해야 합니다. */
        .aftermovie-card {
            content-visibility: auto;
            contain-intrinsic-size: auto 260px auto 200px;
        }
    }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   PC: BACK 은 헤더 줄에, 로고 옆에 (2026-09-19)

   원래 `top: 105px; right: 40px` 이라 1440px 에서 y 95~136, x 1306~1400 에 떴습니다.
   간판 띠는 y 109~484, x 61~1365 이므로 **영상 상단 모서리를 대각선으로 27px 파고들고
   오른쪽으로는 35px 삐져나갑니다** - 걸친 것도 아니고 얹힌 것도 아닌 어정쩡한 자리입니다.

   헤더 오른쪽은 비어 있지 않습니다(실측 1440px): 소셜 아이콘 1261~1346, 햄버거 1372~1400.
   비어 있는 곳은 **로고 오른쪽(66~422)** 뿐이고, "뒤로" 가 왼쪽을 향하는 것과도 맞습니다.

   ⚠️ 좌표를 헤더의 오른쪽 위젯 폭에 기대어 잡지 마십시오(예: right: 203px). 소셜 아이콘이
      하나 늘거나 줄면 바로 어긋납니다. 왼쪽 기준은 로고 하나만 피하면 되므로 안정적입니다.

   ⚠️ 모바일은 그대로 둡니다. 거기서는 간판이 화면 꼭대기까지 차서 헤더 아이콘들이 영상 위에
      얹히는 것이 의도된 모습이고, BACK 도 같은 규칙을 따릅니다(소유자 확인).
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
    #global-section-close-btn {
        /* ⚠️ top 을 숫자로 박지 마십시오. 헤더 높이는 그 안의 아이템에 따라 바뀝니다 - 로고의
           인라인 간극을 없애자 81.9px -> 76.2px 로 줄어 BACK 만 2.5px 남았습니다.
           `--pif-hdr-center` 는 js(pifSyncHeaderCenter)가 로고를 재서 넣는 실제 중심이고,
           거기서 버튼 높이의 절반(35/2)을 빼면 두 줄이 정확히 맞습니다.
           변수가 없는 경우를 대비한 38px 는 1440px 에서의 실측값입니다. */
        top: calc(var(--pif-hdr-center, 38px) - 17.5px);
        right: auto;
        left: 92px;

        /* 헤더 크롬의 일부로 읽혀야 하므로 톤을 낮춥니다. 원래는 그림자와 두꺼운 테두리를
           두른 떠 있는 알약이었는데, 영상 위에 떠 있을 때를 가정한 무게였습니다.
           이제 어두운 배경 위에 앉으므로 그만한 방어가 필요 없습니다. */
        background: rgba(255, 255, 255, 0.06);
        border-color: rgba(255, 255, 255, 0.16);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        box-shadow: none;
        font-size: 0.74rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        padding: 8px 16px;
        border-radius: 999px;
        color: rgba(255, 255, 255, 0.86);
    }

    #global-section-close-btn:hover {
        background: rgba(255, 255, 255, 0.14);
        border-color: rgba(255, 255, 255, 0.34);
        color: #fff;
    }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   좌상단 로고: 인라인 이미지의 유령 여백 제거 (2026-09-19)

   실측(1440px): `.logo` 앵커는 높이 **33.9px** 인데 안의 `<img>` 는 **26px** 이고 위쪽에
   붙어 있었습니다. `display: block` 인 앵커 안에 `<img>` 가 **인라인**으로 들어가 있어
   베이스라인 아래 디센더 자리 7.9px 이 남는, 전형적인 인라인 이미지 간극입니다.

   그래서 앵커의 상자 중심은 y 41 인데 **그림의 실제 중심은 y 37** 이었습니다. 헤더의 다른
   것들(햄버거 40.9, 네비탭 41, 소셜 41)보다 로고만 4px 위로 떠 보입니다 - "어설프게 여백이
   잡혀 있다" 는 지적이 이것입니다.

   바깥 여백은 원래 맞아 있었습니다(왼쪽 40px, 오른쪽 40px, 헤더 padding 24px 40px).
   고칠 것은 안쪽 한 군데뿐입니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
#main-header .logo {
    display: flex;
    align-items: center;
    /* 인라인 맥락을 아예 없애 디센더 자리가 생길 여지를 지웁니다. */
    line-height: 0;
}

#main-header .logo img {
    display: block;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   헤더 아이콘의 터치 영역 (2026-09-21)

   떠다니던 BACK 버튼을 없앴으므로(js/script.js 의 updateGlobalCloseBtn) **이 아이콘이
   유일한 귀가 경로**입니다. 그런데 실측 390px 폭에서 그림이 20x20 이라 손가락에는 너무
   작습니다(WCAG 2.5.8 의 최소 24px, 모바일 관행 44px 둘 다 미달).

   ⚠️ padding 으로 넓히지 **않습니다.** 그러면 상자가 커지고, js/party-nav.js 의
      measureHeaderGaps() 가 그 상자를 재어 목차 칩을 비켜 세우므로 칩 줄이 양쪽에서
      24px 을 잃습니다(실측). 보이지 않는 ::before 로 **닿는 범위만** 넓히면 레이아웃과
      측정값이 둘 다 그대로입니다.
   ⚠️ 그래도 목차와 4px 겹칠 수 있어, measureHeaderGaps() 의 여유를 8px 에서 12px 로
      올려 두었습니다. 한 쌍이니 한쪽만 되돌리지 마십시오.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
#main-header .logo,
#main-header .mobile-hamburger-btn { position: relative; }

#main-header .logo::before,
#main-header .mobile-hamburger-btn::before {
    content: '';
    position: absolute;
    inset: -12px;
    /* 그림이 19px 밖에 안 되는 햄버거는 세로로 조금 더 넓힙니다. */
}

#main-header .mobile-hamburger-btn::before { inset: -13px -12px; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   모바일 BACK: 맨 위에서는 헤더 아래, 스크롤하면 헤더 줄로 (2026-09-19, 소유자 선택)

   실측(375px 폭): 헤더는 높이 40px 이고 그 안에서 로고가 x 12~32, 햄버거가 x 329~363 을
   씁니다. 가운데 297px 가 완전히 비어 있으므로 **로고 바로 오른쪽(left 44px)** 이 비어 있는
   자리입니다. PC 에서 BACK 을 로고 옆에 둔 것과도 같은 규칙이라 기기가 바뀌어도 읽는 법이
   같습니다.

   ⚠️ 가로 위치를 두 상태에서 **같게** 둡니다. 위아래로만 움직여야 헤더 줄로 "올라가는"
      것으로 읽힙니다. 가로까지 같이 움직이면 미끄러지듯 날아가 산만합니다.

   ⚠️ 오른쪽 기준(right: 14px)을 쓰지 않습니다. 햄버거 옆으로 가면 메뉴를 누르려다 BACK 을
      누르기 쉽고, 오른쪽 아이콘이 하나 늘거나 줄면 바로 어긋납니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

    /* ⚠️ 스크롤하는 동안에는 **사라집니다**(소유자 지정 2026-09-19). 한때 계속 보이게
       했다가 되돌렸습니다 - 목차 칩과 같은 줄을 나눠 쓰기 때문에, 움직이는 동안 떠 있으면
       글자끼리 스쳐 지나가 어수선합니다. 멈추면 250ms 뒤에 다시 나타납니다
       (js/script.js 의 handleScrollCloseBtnVisibility). 기본 규칙이 그렇게 되어 있으므로
       여기서는 덮어쓰지 않습니다. */

    /* ⚠️ 이제 스크롤 내내 떠 있으므로 backdrop-filter 를 끕니다. 이 상자에서 블러는 스크롤
       합성 비용이 큽니다(2026-09-19 측정으로 .content-section 과 .party-toc 에서도 같은
       이유로 껐습니다). 대신 배경을 더 불투명하게 해서 읽히게 합니다. */
    #global-section-close-btn {
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
        background: rgba(10, 10, 14, 0.88);
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.55);
        /* ⚠️ **위치만** 전환합니다. padding 과 font-size 에 전환을 걸면 폭이 0.28초 동안
           계속 변하는데, party-nav.js 의 measureHeaderGaps() 가 바로 그 순간에 이 버튼의
           폭을 재서 목차 여백을 정합니다. 움직이는 값을 재면 엉뚱한 여백이 박혀 목차 칩이
           버튼 밑으로 들어갑니다(2026-09-19 실측). party-nav.css 가 목차 padding 에 전환을
           걸지 않는 것과 같은 이유입니다. */
        transition: top 0.28s cubic-bezier(0.16, 1, 0.3, 1),
                    opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
                    background 0.25s ease, border-color 0.25s ease;
    }

    /* 스크롤한 상태 = 헤더 줄. 헤더 높이 40px 안에서 세로 가운데입니다(40-27)/2 ≒ 6. */
    #global-section-close-btn.visible {
        top: 6px;
        left: 44px;
        right: auto;
        padding: 5px 12px;
        font-size: 0.64rem;
        letter-spacing: 0.04em;
        border-radius: 16px;
    }

    /* 맨 위 = 헤더 아래로 내려와 조금 크게. 헤더가 40px 이므로 10px 띄웁니다. */
    #global-section-close-btn.visible.at-top {
        top: 50px;
        left: 44px;
        right: auto;
        padding: 7px 15px;
        font-size: 0.72rem;
        border-radius: 20px;
    }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   헤더 줄로 올라간 BACK 은 아이콘만 남긴다 (2026-09-19)

   ⚠️ 그냥 올려 두었더니 목차 칩을 덮었습니다. 실측 375px: BACK 이 x 44~111, 목차 첫 항목
      UPCOMING 이 x 43~102 로 **거의 완전히 겹쳤습니다.** 둘 다 헤더 줄 왼쪽을 원합니다.

      칩 줄을 그만큼 밀어 내는 방법도 있지만 375px 에서 67px 를 빼앗기면 보이는 칩이 다섯에서
      셋으로 줄어듭니다. 글자를 접어 아이콘만 남기면 빼앗기는 폭이 절반 이하가 됩니다.
      맨 위에서는 자리가 넉넉하므로 "✕ BACK" 을 그대로 보여 줍니다 - 스크롤하면서 줄어드는
      모습 자체가 같은 버튼이라는 것을 말해 줍니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
#global-section-close-btn .gcb-label { margin-left: 6px; }

@media (max-width: 768px) {
    /* 스크롤 상태(= at-top 이 아닐 때)에는 아이콘만. 정사각에 가깝게 만듭니다. */
    #global-section-close-btn.visible:not(.at-top) .gcb-label {
        display: none;
    }

    #global-section-close-btn.visible:not(.at-top) {
        padding: 6px 10px;
        border-radius: 14px;
    }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   세 탭의 1줄 로고 (2026-09-19)

   LIVE 가 쓰던 방식을 MUSIC 과 PARTY 로 넓혔습니다. 규칙은 하나입니다 -
   **각 탭의 첫 내용 바로 위에, 왼쪽 정렬로, 같은 높이로.**

   세 파일의 viewBox 가 `0 0 4026.93 260` 으로 **완전히 같습니다**(비율 15.49:1, 모두 단색
   #f2112c). 그래서 높이만 맞추면 세 탭의 로고 크기와 무게가 정확히 일치합니다. 폭을 지정하면
   오히려 어긋나므로 높이로만 잡습니다.

   ⚠️ PARTY 의 순서는 **간판 영상 - 목차 - 로고 - 내용** 입니다(소유자 지정 2026-09-19).
      목차가 영상에 딱 붙어 영상의 아래 테두리처럼 읽히고, 로고는 그 밑에 옵니다. 로고를
      영상 바로 밑이나 영상 위에 두는 배치를 거쳐 여기에 왔으니 되돌리지 마십시오.
      목차를 그 자리에 넣는 것은 js/party-nav.js 이고, 로고 띠 앞에 끼우도록 되어 있습니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.pif-section-brand {
    display: flex;
    align-items: center;
    margin: 0 0 18px;
    line-height: 0;          /* img 아래 생기는 유령 여백 제거 */
}

.pif-1line-logo {
    height: 32px;
    max-height: 36px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    display: block;
    /* ⚠️ 글로우를 0.55/12px 에서 낮췄습니다 (2026-09-21). 로고가 고정 위치에서 흐름 안으로
       내려오면서 바로 아래 섹션 제목과 **같은 줄기둥**에 서게 됐는데, 그 세기로는 로고가
       제목보다 무거워 위계가 뒤집혔습니다. 색은 그대로이고 번짐만 줄입니다. */
    filter: drop-shadow(0 0 9px rgba(242, 17, 44, 0.32));
}

/* PARTY: 목차 아래. 목차와 조금 떨어뜨리고, 아래 제목과는 넉넉히 둡니다. */
#party .pif-section-brand {
    margin-top: 16px;
    margin-bottom: 20px;
}

/* 순서는 간판 - 목차 - 로고 - 내용 입니다. 목차가 다시 간판의 바로 다음 형제가 되었으므로
   `.pif-hero:not([hidden]) + .party-toc { margin-top: 0 }` 가 그대로 살아납니다.
   여기서는 **목차 다음에 오는 로고**의 여백만 잡습니다.

   ⚠️ 인접 형제 선택자는 사이에 무엇이 끼는 순간 조용히 안 맞게 됩니다. 이 자리에 요소를
      더 끼울 일이 생기면 이 규칙들도 같이 봐야 합니다. */
.party-toc + .pif-section-brand {
    margin-top: 16px;
}

@media (max-width: 768px) {
    /* ⚠️ 22px 에서 낮췄습니다 (2026-09-21). 실측 412px 화면에서 22px 는 폭 341px, **화면의
       87%** 라 바로 아래 24px 짜리 섹션 제목보다 로고가 더 크게 읽혔습니다. 19px 이면
       294px(71%)이라 제목이 제자리를 찾고, 로고는 섹션의 서명으로 물러납니다.
       ⚠️ 더 줄이지는 마십시오. 이 SVG 는 비율이 15.49:1 이라 18px 아래로 내려가면 오른쪽
          'PARTY' 배지의 글자가 뭉개집니다. */
    .pif-1line-logo {
        height: 19px;
        max-height: 21px;
    }

    /* 모바일 PARTY: 목차 아래에 놓입니다. 목차 자체는 간판 영상에 **딱 붙습니다**
       (소유자 지정 2026-09-19).

       ⚠️ 위 여백은 이 값이 전부가 아닙니다. 목차 줄이 `position: fixed` 라 그 자리를
          `.pif-toc-anchor` 가 62px 로 잡아 두는데, 줄 자체는 40px 이므로 이미 22px 이
          비어 있습니다. 실측 34px(= 22 + 12) 대 아래 14px 로 위아래가 어긋나 보였습니다.
          위를 4 로 줄이고 아래를 20 으로 올려 26 : 20 으로 맞춥니다. 로고가 아래 제목과
          한 덩어리로 읽히도록 아래쪽을 조금 좁게 둡니다. */
    #party .pif-section-brand {
        margin-top: 4px;
        margin-bottom: 20px;
        padding: 0;
    }

    .pif-section-brand {
        margin-bottom: 14px;
    }
}

/* ── 삼성 인터넷 강제 다크 대응 ─────────────────────────────────────────────────────────────
   ⚠️ 기존 규칙은 `.show-logo` 에 **LIVE 로고를 하드코딩**해 두었습니다. 그 클래스를 그대로
      돌려 썼다면 삼성 인터넷에서 세 탭이 전부 LIVE 로고로 보였을 것입니다. 파일별로 마스크를
      지정합니다. `[src*=...]` 가 특이도를 더하고 위치도 뒤라 기존 규칙을 확실히 덮습니다. */
html[data-sigfix="1"] .pif-1line-logo {
    content: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
    background-color: transparent;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    background-image: linear-gradient(var(--neon-pif-red), var(--neon-pif-red));
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
    aspect-ratio: 4026.93 / 260;
    filter: none;
}

html[data-sigfix="1"] .pif-1line-logo[src*="PIF_1LINE_Music"] {
    -webkit-mask-image: url("../images/pif-logos/PIF_Music/PIF_1LINE_Music.svg");
            mask-image: url("../images/pif-logos/PIF_Music/PIF_1LINE_Music.svg");
}
html[data-sigfix="1"] .pif-1line-logo[src*="PIF_1LINE_Party"] {
    -webkit-mask-image: url("../images/pif-logos/PIF_Party/PIF_1LINE_Party.svg");
            mask-image: url("../images/pif-logos/PIF_Party/PIF_1LINE_Party.svg");
}
html[data-sigfix="1"] .pif-1line-logo[src*="PIF_1LINE_Live"] {
    -webkit-mask-image: url("../images/pif-logos/PIF_Live/PIF_1LINE_Live.svg");
            mask-image: url("../images/pif-logos/PIF_Live/PIF_1LINE_Live.svg");
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   모바일 PARTY: BACK 은 목차 바에 탄다 (2026-09-19, 소유자 선택)

   앞서 헤더 아래(top 50px)에 두었더니 **간판 영상 위에 얹혔습니다.** 영상을 가리지 않는
   자리는 그 아래 목차 바뿐이고, 그 바는 스크롤하면 헤더로 붙으므로 "스크롤하면 헤더 따라
   올라간다" 는 요구도 같이 만족합니다.

   ⚠️ top 을 전환하지 않습니다. 바를 **매 프레임 따라가는** 값이라 전환을 걸면 늘 뒤처져
      미끄러집니다. 위치가 연속적이므로 애초에 전환이 필요 없습니다.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

    /* PARTY 일 때만 목차 바를 따라갑니다. MUSIC/LIVE 는 위의 기본 규칙을 그대로 씁니다. */
    #global-section-close-btn.visible.theme-party {
        top: var(--pif-toc-y, 220px);
        left: auto;
        right: 14px;
        /* ⚠️ `right` 에 전환을 걸지 마십시오. party-nav.js 의 measureHeaderGaps() 가 바가
           붙는 그 프레임에 이 버튼의 left 를 재서 목차 여백을 정하는데, 전환 중이면 아직
           옛 자리라 여백이 햄버거 기준으로만 잡히고 GALLERIES 칩이 가려집니다(실측
           2026-09-19: 여백 56px, 겹침 29px). 이 파일에서 top 과 padding 의 전환을 뺀 것과
           같은 이유입니다 - **실측 대상에는 전환을 걸지 않는다.** */
        transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
                    background 0.25s ease, border-color 0.25s ease;
    }

    /* 붙으면 햄버거(오른쪽 12~46px)를 피해 안쪽으로 들어갑니다. 같은 줄을 나눠 쓰므로
       필요합니다. */
    #global-section-close-btn.visible.theme-party.perched {
        right: 56px;
    }

    /* 붙었을 때만 글자를 접습니다. 목차 칩과 햄버거 사이라 자리가 좁습니다.
       ⚠️ at-top 기준으로 접던 것을 perched 기준으로 바꿉니다. PARTY 에서는 at-top 이
          스크롤 20px 에서 풀리는데 바는 200px 쯤에서야 붙어서, 그 사이 구간에 글자가
          먼저 접혀 버렸습니다. */
    #global-section-close-btn.visible.theme-party .gcb-label {
        display: inline;
    }
    /* 붙었을 때는 글자를 접습니다. 칩 줄과 햄버거 사이라 자리가 좁습니다. */
    #global-section-close-btn.visible.theme-party.perched .gcb-label {
        display: none;
    }
    #global-section-close-btn.visible.theme-party {
        padding: 7px 15px;
        border-radius: 20px;
    }
    #global-section-close-btn.visible.theme-party.perched {
        padding: 6px 10px;
        border-radius: 14px;
    }

    /* 바가 제자리(간판 아래)에 있을 때 마지막 칩이 BACK 밑으로 들어가지 않게 자리를
       비웁니다. 붙었을 때의 여백은 party-nav.js 가 실측해서 넣습니다. */
    #party .party-toc:not(.is-stuck) {
        padding-right: var(--toc-back-r, 105px);
    }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   모바일 MUSIC / LIVE: BACK 은 헤더 줄에 둔다 (2026-09-19)

   ⚠️ 1줄 로고를 넣고 나서 생긴 문제입니다. 이 로고는 375px 화면에서 폭 338px 로 거의 화면
      전체를 차지합니다(높이 22px × 비율 15.49). 그래서 로고와 같은 줄에는 아무것도 둘 수
      없는데, BACK 이 top 50px 에 있어 "PLAY IT FORWARD" 한가운데를 덮었습니다.

   PARTY 는 간판 영상 아래에 목차 바가 있어 거기 태웠지만, MUSIC/LIVE 에는 그런 줄이
   없습니다. 남는 자리는 헤더 줄의 빈 가운데뿐입니다(로고 12~32, 햄버거 335~363).
   ══════════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
    #global-section-close-btn.visible:not(.theme-party),
    #global-section-close-btn.visible:not(.theme-party).at-top {
        top: 6px;
        right: 56px;      /* 햄버거(오른쪽 12~46px)를 피합니다 */
        left: auto;
        padding: 6px 13px;
        font-size: 0.66rem;
        border-radius: 16px;
    }

    /* 여기서는 자리가 넉넉하므로 글자를 접지 않습니다. */
    #global-section-close-btn.visible:not(.theme-party) .gcb-label {
        display: inline;
    }
}

/* ═════════════════════════════════════════════════════════════════════════════════════════════
   LANGUAGE: ENG / 한국어 (2026-09-23)
   English is the default view; ?lang=ko is the Korean one (pif-locale.php). Bilingual copy ships
   as sibling [data-l] elements and ONLY this rule decides which one shows, so an in-place switch
   (js/pif-i18n.js) is a single attribute flip on <html>.
   ⚠️ Keep this at the end of the file. Several components set their own `display` at equal or
   higher specificity; the !important here is what lets one attribute win everywhere.
   ═════════════════════════════════════════════════════════════════════════════════════════════ */
html[lang="en"] [data-l="ko"],
html[lang="ko"] [data-l="en"] {
    display: none !important;
}

/* A short fade on the swapped text only. Video, posters and layout do not move. */
@keyframes pifLangIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}
html.pif-lang-swap [data-l] {
    animation: pifLangIn 0.28s ease both;
}
@media (prefers-reduced-motion: reduce) {
    html.pif-lang-swap [data-l] { animation: none; }
}

/* The toggle. Same shape as casepeat.com's .cp-language (two segments in a thin frame), drawn in
   this site's neutral ladder: the active segment is the light surface, red stays reserved for the
   brand mark. */
.pif-lang {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 999px;
    background: rgba(21, 21, 21, 0.7);
    flex: none;
}
.pif-lang a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 28px;
    padding: 0 11px;
    border-radius: 999px;
    color: var(--n-7, #AEAEAE);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.18s ease, background-color 0.18s ease;
}
.pif-lang a:lang(ko) {
    letter-spacing: 0;
}
.pif-lang a:hover {
    color: #fff;
}
.pif-lang a[aria-current="true"] {
    background: var(--n-9, #EAEAEA);
    color: #151515;
}
.pif-lang a:focus-visible {
    outline: 2px solid var(--n-9, #EAEAEA);
    outline-offset: 2px;
}

/* Header copy: every width. On narrow screens it is compact, and it steps aside while the PARTY
   index bar is docked into the header row (.party-toc.is-stuck), because the bar's chips run
   between the logo and the hamburger and measureHeaderGaps() only clears those two. The section
   end copy and the menu copy stay reachable while it is hidden. Opacity + visibility only, never a
   geometry change: nothing on the measuring path may move (see CLAUDE.md). */
.pif-lang--header {
    transition: opacity 0.2s ease, visibility 0s linear 0s;
}
@media (max-width: 1023px) {
    .header-right-zone {
        gap: 14px;
    }
    /* 769-1023px: the centred nav tabs, the social icons and this toggle do not fit one row
       (measured 820px: toggle 537-633 over nav 160-588). The social icons are also in the menu
       footer, so they are the ones that give way. */
    .social-links-header {
        display: none;
    }
    .pif-lang--header a {
        min-height: 26px;
        padding: 0 9px;
        font-size: 10.5px;
    }
    /* ⚠️ The toggle must not add height to the header row. It is 32px tall outside; the logo and
       the hamburger are 20px, and the header centres on its tallest child, so the toggle pushed
       both icons 5.6px down (measured 20 -> 25.6) while the docked PARTY bar kept aligning to the
       old row - the bar then sat visibly above the icons. Negative block margins give it a 20px
       footprint; it overflows into the header's own 14px padding, which is empty. */
    .pif-lang--header {
        margin-block: -6px;
    }
    /* Scoped to an OPEN #party: the bar keeps .is-stuck after the reader leaves for MUSIC or
       LIVE, and an unscoped rule hid the toggle on those tabs too (measured 2026-09-24). */
    html:has(#party.visible .party-toc.is-stuck) .pif-lang--header {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transition: opacity 0.2s ease, visibility 0s linear 0.2s;
    }
}

/* Menu copy: sits between the menu's logo and its close button. */
.pif-lang--menu {
    margin-left: auto;
    margin-right: 12px;
}
.pif-lang--menu a {
    min-height: 32px;
    padding: 0 13px;
    font-size: 12px;
}

/* ── English view: the English lines of the merch copy become the primary lines ──
   They were written as small italic captions under a Korean line. With the Korean line hidden
   they are the only text, so they take the Korean line's size, colour and spacing. */
html[lang="en"] .merch-hero-sub-en {
    margin-bottom: 16px;
}
html[lang="en"] .notice-desc-en {
    font-size: 0.82rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, 0.85);
}
html[lang="en"] .merch-title {
    margin-bottom: 12px;
}
html[lang="en"] .merch-desc .desc-en {
    display: inline;
    font-size: inherit;
    color: inherit;
    font-style: normal;
    margin: 0;
}

/* End-of-section copy (2026-09-23): narrow screens only. A long mobile section is where the
   header copy is furthest away (and hidden while the PARTY bar is docked). */
.pif-lang-end {
    display: none;
}
@media (max-width: 1023px) {
    .pif-lang-end {
        display: flex;
        justify-content: center;
        margin: 56px 0 8px;
    }
}

/* ═════════════════════════════════════════════════════════════════════════════════════════════
   KOREAN VIEW TYPOGRAPHY (2026-09-23)
   The Korean view shows ONE language: every line that used to carry English and Korean side by
   side now swaps (data-l). What is left to get right is how Hangul sits in a type system that was
   drawn for Latin caps: Roboto 900 and Poppins have no Hangul, so without this the browser falls
   back glyph by glyph to whatever the OS has, and the wide caps tracking (1-2px) that suits
   "UPCOMING EVENTS" pulls Korean syllables apart.
   - Noto Sans KR is loaded only in the Korean view (index.php, and js/pif-i18n.js on a switch).
     Google serves it in unicode-range slices, so only the syllables on screen are fetched.
   - Latin stays in Roboto/Poppins; Hangul falls through to Noto Sans KR in the same stack.
   - Tracking: Hangul is set at 0, titles slightly tight. Tiny caps labels (9-10px) are raised to
     11px for Hangul, which stops reading below that size.
   - keep-all: Korean breaks between words, not inside them.
   ═════════════════════════════════════════════════════════════════════════════════════════════ */
html[lang="ko"] {
    --font-display: 'Roboto', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
    --font-body: 'Poppins', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif;
}
html[lang="ko"] body {
    font-family: var(--font-body);
    word-break: keep-all;
    overflow-wrap: break-word;
}
html[lang="ko"] [data-l="ko"] {
    letter-spacing: 0;
}
/* Section titles: same size and weight as the English, a touch tighter. */
html[lang="ko"] .section-title [data-l="ko"],
html[lang="ko"] .merch-title [data-l="ko"] {
    font-weight: 800;
    letter-spacing: -0.02em;
}
/* Small caps labels and chips. */
html[lang="ko"] :is(.meta-label, .price-label, .merch-status-pill, .notice-tag, .merch-card-badge,
    .merch-category, .collab-sessions-all, .collab-sessions-head, .pif-past-modes button,
    .more-text, .desc-header, .plat-head, .timer-label-main, .archive-title, .merch-buy-btn,
    .party-toc-link) [data-l="ko"] {
    font-weight: 700;
    font-size: max(1em, 11px);
}
/* Korean prose wants a little more leading than the Latin setting. */
html[lang="ko"] p[data-l="ko"],
html[lang="ko"] .merch-desc [data-l="ko"],
html[lang="ko"] .desc-body[data-l="ko"],
html[lang="ko"] .collab-session-note[data-l="ko"] {
    line-height: 1.75;
}
/* Merch hero: the Korean line takes the English line's place and weight. */
html[lang="ko"] .merch-hero-sub-kr {
    font-size: 0.85rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.8);
    margin-bottom: 16px;
}
html[lang="ko"] .merch-desc [data-l="ko"] {
    display: block;
}

/* ═════════════════════════════════════════════════════════════════════════════════════════════
   HEADER QUICK MENU + MENU / TOGGLE COORDINATION + LIVE PROMO (2026-09-24)
   ═════════════════════════════════════════════════════════════════════════════════════════════ */
.pif-quick {
    display: none;
    position: relative;
    margin-left: 14px;
    margin-right: auto;          /* sit beside the logo; the right cluster keeps the far edge */
}
@media (max-width: 768px) {
    /* flex, not block: a block wrapper builds a line box around the inline-flex button and its
       strut added 5.6px to the header row (measured 40 -> 45.6), which again pushed the icons off
       the docked PARTY bar. */
    .pif-quick {
        display: flex;
        align-items: center;
        height: 20px;
    }
}
.pif-quick-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    margin-block: -4px;          /* 20px footprint: must not grow the header row (see CLAUDE.md) */
    padding: 0 12px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(21, 21, 21, 0.55);
    color: var(--n-8, #D4D4D4);
    font: 800 10.5px/1 var(--font-body);
    letter-spacing: 0.12em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: color 0.18s ease, border-color 0.18s ease, background-color 0.18s ease;
}
.pif-quick-btn i {
    font-size: 8px;
    transition: transform 0.2s ease;
}
.pif-quick-btn[aria-expanded="true"] {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.3);
    background: rgba(21, 21, 21, 0.85);
}
.pif-quick-btn[aria-expanded="true"] i {
    transform: rotate(180deg);
}
.pif-quick-btn:focus-visible {
    outline: 2px solid var(--n-9, #EAEAEA);
    outline-offset: 2px;
}
/* Resting state is VISIBLE; only the entering frame is transparent (fail toward showing, as the
   FULL MIX sheet notes: where transitions do not advance, a hidden-by-default menu stays hidden). */
.pif-quick-menu {
    position: absolute;
    top: calc(100% + 12px);
    left: 0;
    min-width: 214px;
    padding: 6px;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, 0.09);
    background: rgba(18, 18, 20, 0.97);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.16s ease, transform 0.16s ease;
    z-index: 5;
}
.pif-quick-menu.is-entering {
    opacity: 0;
    transform: translateY(-6px);
}
.pif-quick-menu a {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 44px;
    padding: 0 14px;
    border-radius: 10px;
    color: var(--n-9, #EAEAEA);
    font: 700 12.5px/1 var(--font-body);
    letter-spacing: 0.06em;
    text-decoration: none;
}
.pif-quick-menu a + a {
    margin-top: 1px;
}
.pif-quick-menu a i {
    width: 16px;
    text-align: center;
    font-size: 13px;
    color: var(--n-6, #7A7A7A);
}
.pif-quick-menu a:hover,
.pif-quick-menu a:focus-visible {
    background: rgba(255, 255, 255, 0.06);
    outline: none;
}
.pif-quick-menu a:hover i,
.pif-quick-menu a:focus-visible i {
    color: var(--neon-live);
}
@media (max-width: 1023px) {
    /* Same rule as the language toggle: step aside while the PARTY bar is docked in this row. */
    html:has(#party.visible .party-toc.is-stuck) .pif-quick {
        visibility: hidden;
    }
}
/* While the hamburger panel is open it carries its own logo, toggle and close button; the header
   row above would otherwise repeat the same three. The header keeps only its close (X). */
html:has(.mobile-nav-overlay.active) .pif-lang--header,
html:has(.mobile-nav-overlay.active) .pif-quick {
    visibility: hidden;
}

/* LIVE: DJ promo address */
.pif-promo-text {
    margin: 10px 0 12px;
    font-size: 0.82rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.72);
    max-width: 46ch;
}
.pif-promo-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
}
.pif-promo-addr {
    font: 600 0.82rem/1 var(--font-body);
    letter-spacing: 0.04em;
    color: var(--n-8, #D4D4D4);
    white-space: nowrap;
}

/* MUSIC tab empty state (2026-09-24): shown until the label's first release is in music_data.json. */
.music-empty {
    grid-column: 1 / -1;
    max-width: 520px;
    margin: 8px auto 0;
    padding: 40px 24px 36px;
    text-align: center;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.025);
}
.music-empty-icon {
    font-size: 2rem;
    color: var(--neon-live);
    margin-bottom: 16px;
}
.music-empty-title {
    margin: 0 0 10px;
    font-size: 1.15rem;
    font-weight: 800;
    color: var(--n-9, #EAEAEA);
    letter-spacing: 0.02em;
}
.music-empty-text {
    margin: 0 auto 22px;
    max-width: 38ch;
    font-size: 0.86rem;
    line-height: 1.65;
    color: rgba(255, 255, 255, 0.62);
}
.music-empty-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0 22px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
    font: 700 0.82rem/1 var(--font-body);
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: background-color 0.18s ease, border-color 0.18s ease;
}
.music-empty-btn:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.3);
}
.music-empty-btn:focus-visible {
    outline: 2px solid var(--n-9, #EAEAEA);
    outline-offset: 3px;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════
   MINI VIDEO CARD, a popup player in three sizes (2026-09-26)
   The replay bar's YouTube player, shown: a 16:9 popup above the bar's RIGHT end (owner: right,
   not left). Hidden it is display:none (script.js pcastShowVideo): the sound goes on, and Samsung
   Internet has no laid-out video to hang its own buttons on.

   Sizes (html.pcast-vs-0/1/2, the button cycles them, the choice is remembered):
     phone   128x72 (default) / 192x108 / bar width
     wider   192x108 / 356x200 (default) / 512x288
   ⚠️ Owner's decision, taken knowingly: YouTube's Required Minimum Functionality asks for at least
   200x200. The first version kept 200px on every screen and was too big on a phone ("너무 커").
   Later the same day the owner also chose to play the sound from the hidden player again, and to
   cover YouTube's own title overlay with the episode thumbnail (see .pcast-video-poster).

   The buttons sit BELOW the video, left-aligned. Above its top-right corner is where Samsung
   Internet draws its own video buttons (full screen, pop-up play; owner screenshot), which covered
   the YouTube and close buttons when they lived there. A page cannot turn those off.
   ═══════════════════════════════════════════════════════════════════════════════════════ */
html {
    --pcast-vw: 356px;
    /* room the popup takes above the bar: video + 6 gap + 28 buttons + 8 gap + 8 spare */
    --pcast-reserve: calc(min(var(--pcast-vw), calc(100vw - 32px)) * 9 / 16 + 50px);
}

html.pcast-vs-0 { --pcast-vw: 192px; }
html.pcast-vs-1 { --pcast-vw: 356px; }
html.pcast-vs-2 { --pcast-vw: 512px; }

@media (max-width: 767px) {
    html.pcast-vs-0 { --pcast-vw: 128px; }
    html.pcast-vs-1 { --pcast-vw: 192px; }
    html.pcast-vs-2 { --pcast-vw: min(356px, calc(95vw - 24px)); }
}

.pcast-video-card {
    display: none;                      /* .is-mounted while shown or fading out */
    position: absolute;
    right: 14px;
    bottom: calc(100% + 42px);          /* 8 gap + 28 buttons + 6 gap */
    width: min(var(--pcast-vw), calc(100vw - 32px));
    aspect-ratio: 16 / 9;
    border-radius: 12px;
    background: #000;
    /* no border: a 1px ring read as a stray bezel on the left edge (owner, PC) */
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
    transform-origin: bottom right;
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px) scale(0.96);
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.3s ease, width 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 0.3s;
}

.pcast-video-card.is-mounted {
    display: block;
}

.pcast-video-card.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
    transition: opacity 0.25s ease, transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), width 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}

.pcast-video-frame {
    position: absolute;
    inset: 0;
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;                    /* a tap plays / pauses (script.js) */
}

/* No pointer input into YouTube's frame: its hover overlays (title, logo) never appear. 2% of
   overscan hides the hairline of black YouTube can leave at a fractional width. */
.pcast-video-frame #pif-youtube-audio-host {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    pointer-events: none;
    transform: scale(1.02);
}

/* The episode thumbnail over the picture while YouTube shows its own title, channel avatar and play
   glyph (the first ~3 s of playback, and whenever paused / cued). script.js adds .is-clean once
   that is over. hqdefault is 4:3 with black bars; cover crops exactly those bars away. */
.pcast-video-poster {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
    opacity: 1;
    transition: opacity 0.45s ease;
}

.pcast-video-card.is-clean .pcast-video-poster,
.pcast-video-poster:not([src]) {
    opacity: 0;
}

/* Paused: a small play mark on the thumbnail, the tap target's hint. */
.pcast-video-card.is-paused .pcast-video-frame::before,
.pcast-video-card.is-paused .pcast-video-frame::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    z-index: 2;                         /* above the positioned thumbnail */
    pointer-events: none;
}

.pcast-video-card.is-paused .pcast-video-frame::before {
    width: 30px;
    height: 30px;
    margin: -15px 0 0 -15px;
    border-radius: 50%;
    background: rgba(16, 16, 19, 0.72);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}

.pcast-video-card.is-paused .pcast-video-frame::after {
    margin: -6px 0 0 -3px;
    border-left: 10px solid #fff;
    border-top: 6px solid transparent;
    border-bottom: 6px solid transparent;
}

.pcast-video-tools {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    display: flex;
    gap: 6px;
}

.pcast-video-tool {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 28px;
    min-width: 28px;
    padding: 0 11px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(16, 16, 19, 0.78);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    color: #fff;
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.04em;
    text-decoration: none;
    cursor: pointer;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}

/* 28px to look at, about 40px to hit (the gaps are 6px, so neighbours overlap by a hair only). */
.pcast-video-tool::before {
    content: '';
    position: absolute;
    inset: -6px -3px;
}

.pcast-video-tool:hover,
.pcast-video-tool:focus-visible {
    border-color: rgba(242, 17, 44, 0.7);
    background: rgba(16, 16, 19, 0.92);
    outline: none;
}

.pcast-video-yt i {
    color: var(--neon-live);
    font-size: 13px;
}

.pcast-video-listen,
.pcast-video-size {
    width: 28px;
    padding: 0;
    font-size: 12px;
}

/* At the smallest size the YouTube button is the logo alone, so all three fit under 128px. */
html.pcast-vs-0 .pcast-video-yt {
    width: 28px;
    padding: 0;
}

html.pcast-vs-0 .pcast-video-yt-label {
    display: none;
}

/* Docked bubble: the popup hangs above the bubble, right-aligned with it. The bubble is 56px wide,
   so the width comes from the viewport (see --pcast-vw), not from the parent. The ID beats the
   docked bubble's own `overflow: hidden !important`, which would clip the popup. */
.podcast-player.docked .pcast-video-card {
    right: 0;
    bottom: calc(100% + 56px);          /* 14px more: the undock badge overhangs the bubble's top */
}

#podcast-sticky-player.docked.has-video {
    overflow: visible !important;
}

/* Open drawer: it gives up the popup's height, so the popup still clears the site header. */
.podcast-player.expanded.has-video .podcast-frame-wrapper {
    max-height: min(58vh, calc(100vh - 190px - var(--pcast-reserve)), 520px);
    max-height: max(150px, min(58dvh, calc(100dvh - 190px - var(--pcast-reserve)), 520px));
}

@media (max-width: 767px) {
    .pcast-video-card {
        right: 12px;
    }

    .podcast-player.docked .pcast-video-card {
        right: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .pcast-video-card,
    .pcast-video-card.is-open {
        transform: none;
        transition: opacity 0.2s ease, visibility 0s;
    }
}

/* Menu clearance, same idea as PLAYER BAR CLEARANCE above: with the popup open the menu stops above
   it too (and scrolls inside), instead of the popup sitting on its last rows. Phones, plus a docked
   player on wide screens (the popup then hangs on the right, where the menu is). */
@media (max-width: 768px) {
    html.pcast-video-open .mobile-nav-overlay {
        max-height: calc(100vh - 50px - 95px - var(--pcast-reserve) - env(safe-area-inset-bottom, 0px));
        max-height: max(220px, calc(100dvh - 50px - 95px - var(--pcast-reserve) - env(safe-area-inset-bottom, 0px)));
    }
}

@media (min-width: 769px) {
    html.pcast-video-open:has(#podcast-sticky-player.docked) .mobile-nav-overlay {
        max-height: calc(100vh - 75px - 112px - var(--pcast-reserve));
        max-height: max(260px, calc(100dvh - 75px - 112px - var(--pcast-reserve)));
    }
}

/* "Watch video" pill: shown while a YouTube episode plays with the video hidden (script.js
   pcastSyncWatchPill). Same material as the popup's buttons, above the bar's right end where the
   popup opens. */
.pcast-watch-pill {
    position: absolute;
    right: 14px;
    bottom: calc(100% + 8px);
}

.pcast-watch-pill[hidden],
.podcast-player.docked .pcast-watch-pill {
    display: none;
}

.pcast-watch-pill i {
    color: var(--neon-live);
    font-size: 11px;
}

@media (max-width: 767px) {
    .pcast-watch-pill {
        right: 12px;
    }
}
