/* 기획안 허브 큐레이션 — 5개 스튜디오 탭 공용(planHub.js·planHubCard.js·planHubRail.js·planHubToolbar.js).
 * 숨김 처리는 전부 hidden 속성으로 한다 — glass.css:10 의 [hidden]{display:none!important} 가 전역으로 이긴다.
 *   그래서 .ph-card-wrap/.ph-rail 에 display 를 줘도 hidden 이 이긴다(중복 규칙 불필요).
 * 토큰은 tokens.css. 카드 껍데기(.ep-card)·그리드(.ep-hub-grid)는 components.css 것을 그대로 쓰고,
 * 여기서는 큐레이션 층(툴바·레일)과 카드 내부 구조(장르 칩·로그라인·작성자)만 더한다.
 * 키·모델명·원문 없음 — 순수 CSS. CDN 로드 0. */

/* ── 레이아웃 골격 ── */
.ph-body { display: flex; flex-direction: column; gap: var(--sp-lg); }

/* ── 헤드: 제목 블록(좌) + 액션(우, 전역 갤러리 진입). 액션이 제목 아래 흐름에 있으면 sm 고스트가
 * '작은 뒤로가기 링크' 로 읽혀 눈에 안 들어온다(오너 지적 2026-07-30) — 제목 줄 우측 md 액센트 링으로 세운다. ── */
.ph-hub .ep-hub-head { flex-direction: row; align-items: flex-end; gap: var(--sp-md); }
.ph-head-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
/* 형태는 btn({variant:'nova', size:'lg'}) 가 전부 소유한다(button.css) — 여기서는 자리만 잡는다. */
.ph-head-actions { flex: none; margin-left: auto; display: flex; align-items: center; gap: var(--sp-xs); }

/* ── 툴바: 흐름에서는 면 없이 두고(배경면·블러·구분선 0), **스크롤로 시야에서 벗어날 때만** 하단 독으로 띄운다.
 * 1차에는 상단 sticky + 배경면이었는데, .ep-hub 가 max-width 1080 중앙정렬이라 면이 화면 끝까지 못 가고
 * 좌우에 여백을 남겨 **회색 슬랩이 떠 있는 것처럼** 보였다(오너 지적). 면만 지우고 sticky 를 남기면
 * 고정된 글자가 카드 위에 겹쳐 읽히지 않아 둘을 함께 걷었고, 그 대가로 필터를 쓰러 위로 올라가야 했다.
 * 독은 그 대가만 되돌린다 — 슬랩과 다른 점은 위치가 아니라 **면의 경계가 닫혀 있는가** 다.
 *   알약 라운드 + 리퀴드글라스 + 섀도로 '떠 있는 섬'(네비게이션 독)이 되면 좌우 여백이 의도로 읽힌다.
 * 흐름 상태에 면을 깔면 같은 지적이 되돌아온다 — 면은 독에서만 쓴다. ── */
.ph-toolbar { display: flex; flex-direction: column; gap: var(--sp-sm); }
.ph-toolbar-slot { position: relative; }
/* 독 전환 판정용 — 툴바가 원래 있던 스크롤 위치에 남는 1px(planHubDock.js 의 IntersectionObserver 대상). */
.ph-dock-sentinel { height: 1px; margin-bottom: -1px; pointer-events: none; }

.ph-toolbar--docked {
  position: fixed; z-index: 30;                 /* 컴포저(25) 위 · 드로어(60)/토스트(60) 아래 */
  bottom: var(--sp-md); left: 50%;
  width: min(1032px, calc(100vw - 2 * var(--sp-lg)));  /* 1032 = 허브 1080 − 좌우 패딩 24 → 카드 그리드와 좌우 끝이 맞는다 */
  gap: var(--sp-xs);
  padding: var(--sp-sm) var(--sp-md);
  border: 1px solid var(--hairline); border-radius: var(--r-lg);
  background: rgba(15, 15, 23, 0.62);
  -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);
  /* 위쪽 1px 하이라이트 = 유리 두께. 섀도는 카드 위에 떠 있음을 알리는 최소치. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 12px 40px rgba(0, 0, 0, 0.45);
  transform: translate(-50%, 0);
  animation: ph-dock-in var(--t-mid) var(--ease) both;
}
/* ⚠️ 키프레임에 translateX(-50%) 를 같이 써야 한다 — transform 은 한 속성이라 빼면 등장 순간 좌측으로 튄다. */
@keyframes ph-dock-in {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
/* fixed 는 뷰포트 기준이라 body 의 좌측 레일 패딩을 못 받는다 — .app-toast 와 같은 방식으로 절반만 보정한다. */
body.shell-chrome .ph-toolbar--docked {
  left: calc(50% + var(--sidebar-w) / 2);
  width: min(1032px, calc(100vw - var(--sidebar-w) - 2 * var(--sp-lg)));
}
body.shell-chrome.rail-collapsed .ph-toolbar--docked { left: 50%; width: min(1032px, calc(100vw - 2 * var(--sp-lg))); }

/* 독 안에서는 세로를 아낀다(뷰포트를 먹는 만큼 카드가 가려진다) — 컨트롤 높이 38→32, 칩은 한 줄 유지 후 가로 스크롤. */
.ph-toolbar--docked .ph-search-input, .ph-toolbar--docked .ph-sort { height: 32px; }
.ph-toolbar--docked .ph-tb-chips { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.ph-toolbar--docked .ph-tb-chips::-webkit-scrollbar { display: none; }
.ph-toolbar--docked .ph-tb-group { flex-wrap: nowrap; }

/* 독이 마지막 카드 줄을 덮지 않게 스크롤 바닥을 늘린다(독 높이 ≈92 + bottom 17). */
.ph-hub.ph-has-dock { padding-bottom: 148px; }
/* 독 뒤 스크림 — 틴트 단독(::after 만)은 독 배경(rgba(15,15,23,.62)+블러)과 카드 배경이 색이 거의 같아
 * 경계가 잘 안 보였다(오너 지적 2026-07-30). 3가지 시안(틴트 강화 / 블러 / 블러+틴트)을 만들어 비교한 뒤
 * 블러+틴트로 결정(오너 확정 2026-07-30) — 독 자체가 이미 블러+틴트 재질이라, 그 재질을 그라데이션으로
 * 카드 쪽까지 확장하면 독으로 자연스럽게 이어진다(iOS 탭바 아래 콘텐츠 페이드와 같은 패턴).
 * 독 자체를 더 불투명하게 하면 독 안 텍스트/입력 대비 균형이 깨지므로 여기서 하지 않는다.
 * 독과 같은 노드 이동 없이 순수 장식이라 ::before(블러)+::after(틴트) — 클릭을 먹지 않게 둘 다 pointer-events:none 필수.
 * ::before 를 더 낮은 z-index 로 둬서 블러가 먼저 깔리고 틴트가 그 위에 얹힌다. */
.ph-hub.ph-has-dock::before {
  content: '';
  position: fixed; inset: auto 0 0 0; height: 320px;
  z-index: 19;                                  /* 카드(auto) 위 · 틴트(20) 아래 */
  pointer-events: none;
  backdrop-filter: blur(32px) saturate(160%); -webkit-backdrop-filter: blur(32px) saturate(160%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 90%);
  mask-image: linear-gradient(to top, #000 0%, #000 30%, transparent 90%);
}
.ph-hub.ph-has-dock::after {
  content: '';
  position: fixed; inset: auto 0 0 0; height: 320px;
  z-index: 20;                                  /* 블러(19) 위 · 컴포저(25)·독(30) 아래 */
  pointer-events: none;
  background: linear-gradient(to top, rgba(15, 15, 23, 0.92) 0%, rgba(15, 15, 23, 0.75) 25%, rgba(15, 15, 23, 0.45) 55%, rgba(15, 15, 23, 0.15) 80%, rgba(15, 15, 23, 0) 100%);
}
.ph-tb-top { display: flex; align-items: center; gap: var(--sp-sm); }
.ph-search-box { flex: 1; min-width: 0; }

.ph-search-input {
  width: 100%; height: 38px; padding: 0 var(--sp-sm);
  border: 1px solid var(--hairline); border-radius: var(--r-md);
  background: var(--surface-1); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--fs-caption); letter-spacing: var(--ls-caption);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.ph-search-input::placeholder { color: var(--ink-48); }
.ph-search-input:hover { border-color: var(--divider-soft); }
.ph-search-input:focus { outline: none; border-color: var(--accent); background: var(--surface-2); box-shadow: 0 0 0 3px var(--accent-ring); }
/* 사파리 검색 취소 버튼이 다크 배경에서 검은 점으로 보인다 — 라이트닝으로 살린다. */
.ph-search-input::-webkit-search-cancel-button { filter: invert(0.7); cursor: pointer; }

.ph-sort, .ph-owner-select {
  height: 38px; padding: 0 var(--sp-xs); max-width: 180px;
  border: 1px solid var(--hairline); border-radius: var(--r-md);
  background: var(--surface-1); color: var(--ink-80);
  font-family: var(--font-sans); font-size: var(--fs-fine); cursor: pointer;
}
.ph-owner-select { height: 30px; max-width: 156px; border-radius: var(--r-pill); }
.ph-sort:hover, .ph-owner-select:hover { border-color: var(--divider-soft); color: var(--ink); }
.ph-sort:focus-visible, .ph-owner-select:focus-visible { outline: 2px solid var(--accent-focus); outline-offset: 2px; }

/* 칩 줄 — 축(소유자·상태·모드·타겟·북마크)끼리 묶고 축 사이에 헤어라인 구분선. */
.ph-tb-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-xs) 6px; }
.ph-tb-group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.ph-tb-group + .ph-tb-group { margin-left: 6px; padding-left: var(--sp-sm); border-left: 1px solid var(--hairline); }
.btn.ph-chip { border-radius: var(--r-pill); }
.btn.ph-tb-reset { margin-left: auto; color: var(--ink-48); }
.btn.ph-tb-reset:hover { color: var(--ink); }

.ph-result-line { font-size: var(--fs-fine); color: var(--ink-48); letter-spacing: var(--ls-fine); }

/* ── 레일(이어서 작업하기): 평면 스크롤 스냅. 3D 커버플로우는 '훑기' 를 방해하므로 쓰지 않는다. ── */
.ph-rail { display: flex; flex-direction: column; gap: var(--sp-xs); }
.ph-rail-head { display: flex; align-items: center; gap: var(--sp-xs); }
.ph-rail-title { margin: 0; font-size: var(--fs-caption); font-weight: 700; color: var(--ink); }
.ph-rail-count { font-size: var(--fs-fine); color: var(--ink-48); font-variant-numeric: tabular-nums; }
.ph-rail-hint { font-size: var(--fs-fine); color: var(--ink-48); }
.ph-rail-nav { margin-left: auto; display: flex; gap: 4px; }
.btn.ph-rail-btn { border: 1px solid var(--hairline); border-radius: var(--r-pill); }
.btn.ph-rail-btn:disabled { opacity: 0.32; }

.ph-rail-track {
  display: flex; gap: var(--sp-sm);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  padding-bottom: var(--sp-xs); /* 스크롤바가 카드 밑단을 덮지 않게 — 전역 얇은 스크롤바(glass.css) 두께분 */
}
.ph-rail-item { flex: 0 0 272px; scroll-snap-align: start; }

/* ── 카드 ── */
/* 별(북마크)은 카드 버튼의 형제다(button 중첩 금지) → 래퍼가 위치 기준을 잡는다. */
.ph-card-wrap { position: relative; display: flex; }
.ph-card { flex: 1; min-width: 0; gap: var(--sp-xs); }
/* 우상단 별(36×16 + 우측 10px) 자리 예약 — 제목만 밀면 상태 배지가 별 밑으로 들어간다. */
.ph-card--starred .ep-card-top { padding-right: 42px; }

/* 별은 **항상 보인다** — hover 로만 나타나게 두면 평소 우상단이 빈 칸으로 보이고(오너 지적),
 *   마우스를 올릴 때마다 없던 요소가 튀어나온다. 기본은 낮은 강조(0.45, .pl-bm 기본값)로 두고
 *   hover/포커스에서 선명해지며, 켜져 있으면(--on) 항상 선명하다.
 * 상태 배지와 같은 줄에 얹는다(top/right = 카드 패딩 기준) — 배지 위에 떠 있으면 어디에도 안 붙은 것처럼 보인다.
 *   .pl-bm 기본형은 행 레이아웃용(align-self:stretch)이라 절대배치에선 크기를 명시해야 한다. */
.btn.pl-bm.ph-card-star {
  position: absolute; top: 13px; right: var(--sp-sm);
  width: 28px; min-width: 28px; height: 28px; align-self: auto;
}
.ph-card-wrap:hover .btn.pl-bm.ph-card-star,
.btn.pl-bm.ph-card-star:focus-visible { opacity: 1; }

.ph-card-status { flex: 0 0 auto; font-size: 10px; border-radius: var(--r-pill); padding: 2px 8px; border: 1px solid currentColor; }
.ph-card-status--done { color: var(--accent); }
.ph-card-status--wip { color: #d7a44f; } /* 진행 중 — 완료(액센트 블루)와 색으로만 구분되지 않게 라벨도 함께 표기 */

.ph-card-meta { font-size: var(--fs-fine); color: var(--ink-48); letter-spacing: var(--ls-fine); }
.ph-card-genres { display: flex; flex-wrap: wrap; gap: 4px; }
.ph-genre { font-size: 11px; line-height: 1.5; padding: 2px 8px; border-radius: var(--r-pill); background: var(--surface-2); color: var(--ink-80); white-space: nowrap; }
.ph-genre--more { background: transparent; border: 1px solid var(--hairline); color: var(--ink-48); }

.ph-card-logline {
  font-size: 13px; line-height: 1.5; color: var(--ink-80);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ph-card-body { margin-top: 2px; }

.ph-card-owner { display: flex; align-items: center; gap: 6px; margin-top: auto; padding-top: 2px; font-size: 11px; color: var(--ink-48); }
.ph-avatar { display: grid; place-items: center; width: 18px; height: 18px; border-radius: 50%; background: var(--surface-3); color: var(--ink-80); font-size: 10px; font-weight: 700; }
.ph-card-owner--mine .ph-avatar { background: var(--accent-soft); color: var(--accent); }
.ph-card-owner--mine .ph-owner-name { color: var(--ink-80); }

/* 레일 카드는 뷰 고유 지표가 없어 키가 들쭉날쭉해진다 — 최소 높이로 줄을 맞춘다. */
.ph-card--compact { min-height: 128px; }

/* ── 좁은 화면 ── */
/* 720 이하에서 좌측 레일이 상단 가로바로 바뀌고 body padding-left 가 풀린다(glass.css) — 독 보정도 함께 풀어야 한다.
 * 안 풀면 독이 레일 폭의 절반만큼 우측으로 밀린 채 화면 밖으로 걸친다. */
@media (max-width: 720px) {
  body.shell-chrome .ph-toolbar--docked,
  body.shell-chrome.rail-collapsed .ph-toolbar--docked {
    left: 50%; width: calc(100vw - 2 * var(--sp-md));
  }
}

@media (max-width: 640px) {
  .ph-hub .ep-hub-head { flex-direction: column; align-items: stretch; }
  .ph-head-actions { margin-left: 0; }
  .ph-tb-top { flex-wrap: wrap; }
  .ph-sort { max-width: none; flex: 1; }
  .ph-tb-group + .ph-tb-group { margin-left: 0; padding-left: 0; border-left: 0; }
  .ph-rail-item { flex: 0 0 78vw; }
}
