/* 트레이(web/ui/tray.js) — 아래에서 올라오는 모달 서랍. vaul 드로어를 Framer 다크 토큰으로 옮겼다.
 *   🔴 라이브러리 시트(.lib-inspector, library.css)와 **같은 값**을 쓴다 — 두 서랍이 한 제품의 같은 부품으로 읽혀야 한다:
 *      반경 --r-xl · 머리/본문 여백 --modal-pad · 닫기 32px 알약 · 핸들 36×4 · 이징 cubic-bezier(.32,.72,0,1) · 스크림 canvas 48%(≤810 72%).
 *      한쪽을 바꾸면 다른 쪽도 바꾼다.
 *   재질(블러·틴트·광연)은 여기 두지 않는다 — liquid-glass-overlays.css 가 G2 오버레이로 소유한다(토큰 한 곳이 폴백을 끈다).
 *   넓은 화면: 아래 가운데 떠 있는 640px 패널(라이브러리 시트처럼 가장자리 8px 띄움) · ≤810: 화면 폭 바텀시트. */

body.shell-v2 dialog.tray {
  position: fixed; inset: auto 0 var(--sp-sm) 0; margin: 0 auto; box-sizing: border-box;
  width: min(640px, 100% - 2 * var(--sp-sm)); max-width: none; max-height: min(85dvh, 100% - 2 * var(--sp-sm));
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  border: 0; border-radius: var(--r-xl); color: var(--ink); word-break: keep-all;
  background: var(--surface-1); box-shadow: 0 0 0 1px var(--hairline), var(--shadow-float); /* 재질 층이 덮는다 — 이건 재질 층이 빠졌을 때의 바닥 */
  transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
  @starting-style { transform: translate3d(0, 100%, 0); }
}
body.shell-v2 dialog.tray:focus { outline: none; }
/* 넓은 트레이(피드 상세) — 오너 2026-09-18 세 번의 교정을 합친 판:
 *   ① 폭: 화면 가장자리 8px 까지(최대 1440) — "좌우 화면을 더 넓게".
 *   ② 높이: 내용과 무관한 고정값 — "세로는 고정 높이로 높게". 위 틈은 화면 높이의 8%(40~96px) —
 *      6%(24~64)는 "상단 여백이 없는 것 같다"는 판정을 받았다. 틈이 보여야 뒤 페이지가 살아 '서랍'으로 읽힌다.
 *   ③ 안쪽: 패널은 넓어도 읽는 줄은 좁게 — "콘텐츠를 꽉 채우지 말고 좌우 여백". 제목·본문이 한 세로 칸(--tray-measure 920px ·
 *      한글 16px 기준 한 줄 약 55~60자, 가독 한계 안)에 가운데로 모인다. 좌우 여백 = max(최소 여백, (패널폭 − 칸폭)/2) —
 *      padding 의 % 는 패널 폭 기준이라 래퍼 없이 된다. 좁은 화면에선 최소 여백으로 자연히 줄어든다.
 *   ⚠️ 닫기는 칸 끝이 아니라 **패널 오른쪽 모서리**다(직전 판에서 칸 끝선으로 옮겼다가 "원래대로 우측에" 로 되돌림).
 *   머리는 스크롤 밖(flex none)이라 본문이 그 아래로 지나간다 — 경계를 헤어라인으로 긋는다. */
body.shell-v2 dialog.tray.tray--wide {
  --tray-top-gap: clamp(40px, 8dvh, 96px);
  --tray-measure: 920px;
  --tray-inline: max(var(--sp-xl), calc((100% - var(--tray-measure)) / 2));
  width: min(1440px, 100% - 2 * var(--sp-sm));
  height: calc(100dvh - var(--tray-top-gap) - var(--sp-sm)); max-height: none;
}
body.shell-v2 dialog.tray.tray--wide .tray-head {
  padding: var(--sp-xs) max(var(--tray-inline), calc(var(--modal-pad) + 32px + var(--sp-md))) var(--sp-md) var(--tray-inline);
  border-bottom: 1px solid var(--lg-divider, rgba(255, 255, 255, 0.10)); /* 글라스 위라 불투명 헤어라인(#1a1a1a)은 안 보인다 — 라이브러리 시트 발과 같은 토큰 */
}
body.shell-v2 dialog.tray.tray--wide .tray-title { font-size: var(--fs-title-3); line-height: 1.4; }
body.shell-v2 dialog.tray.tray--wide { --tray-ctl-top: 24px; }
body.shell-v2 dialog.tray.tray--wide .tray-body { padding: var(--sp-lg) var(--tray-inline) var(--sp-xxl); }

body.shell-v2 dialog.tray.is-dragging { transition: none; user-select: none; }
body.shell-v2 dialog.tray.is-leaving { transform: translate3d(0, calc(100% + var(--sp-sm)), 0); transition-duration: 0.3s; }

body.shell-v2 dialog.tray::backdrop {
  background: rgba(9, 9, 9, 0.48); /* = .lib-scrim(color-mix canvas #090909 48%) — ::backdrop 은 판본에 따라 변수를 못 물려받아 값으로 적는다 */
  transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1);
  @starting-style { opacity: 0; }
}
body.shell-v2 dialog.tray.is-leaving::backdrop { opacity: 0; transition-duration: 0.3s; }

/* 핸들 — 라이브러리 바텀시트의 ::before 와 같은 막대. 트레이는 폭과 상관없이 늘 서랍이라 항상 보인다. */
body.shell-v2 .tray-grip { flex: none; height: 20px; display: grid; place-items: center; cursor: grab; touch-action: none; }
body.shell-v2 .tray-grip::before { content: ''; width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--surface-3); }
body.shell-v2 dialog.tray.is-dragging .tray-grip { cursor: grabbing; }

body.shell-v2 .tray-head { flex: none; display: flex; flex-direction: column; gap: 2px; padding: 0 calc(var(--modal-pad) + 32px + var(--sp-sm)) var(--sp-md) var(--modal-pad); touch-action: none; }
body.shell-v2 .tray-title { margin: 0; font-size: var(--fs-title-3); font-weight: 700; line-height: 1.35; letter-spacing: -0.2px; color: var(--ink); }
body.shell-v2 .tray-desc { margin: 0; font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--ink-48); }
/* 머리 오른쪽 컨트롤 자리 — 닫기(여기)와 트렌드 트레이의 이웃 넘기기(v2/trends.css)가 이 세 값을 나눠 읽는다.
   따로 적어 두면 2px 씩 어긋난다(2026-09-21 오너 신고 "하단 트레이에 ui 정렬 이상함"의 절반이 그것이었다). */
body.shell-v2 dialog.tray { --tray-ctl-top: 20px; --tray-ctl-right: var(--modal-pad); --tray-ctl-bg: rgba(255, 255, 255, 0.05); }
body.shell-v2 .tray-close {
  position: absolute; top: var(--tray-ctl-top); right: var(--tray-ctl-right); width: 32px; height: 32px; padding: 0; margin: 0;
  display: inline-grid; place-items: center; border: 0; border-radius: var(--r-pill);
  background: var(--tray-ctl-bg); color: var(--ink-48); cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
body.shell-v2 .tray-close:hover { background: rgba(255, 255, 255, 0.10); color: var(--ink); }
body.shell-v2 .tray-close:focus-visible { outline: none; box-shadow: var(--ring-focus); }
body.shell-v2 .tray-body { flex: 1; min-height: 0; padding: 0 var(--modal-pad) var(--modal-pad); overflow-y: auto; overscroll-behavior: contain; }

/* 뒤 페이지 축소 중(trayBackdrop.js) — 줄어든 가장자리 사이로 보이는 바탕. 안 칠하면 body 배경이 뷰포트로 번져 흰/투명 틈이 난다. */
html.tray-scaled { background: #000; }

@media (max-width: 810px) {
  body.shell-v2 dialog.tray {
    inset: auto 0 0 0; width: 100%; max-height: 85dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0; padding-bottom: env(safe-area-inset-bottom);
  }
  body.shell-v2 dialog.tray.is-leaving { transform: translate3d(0, 100%, 0); }
  body.shell-v2 dialog.tray::backdrop { background: rgba(9, 9, 9, 0.72); } /* = ≤810 .lib-scrim */
  body.shell-v2 .tray-head { padding: 0 calc(var(--sp-md) + 44px + var(--sp-xs)) var(--sp-sm) var(--sp-md); }
  body.shell-v2 .tray-body { padding: 0 var(--sp-md) var(--sp-lg); }
  body.shell-v2 dialog.tray { --tray-ctl-top: 14px; --tray-ctl-right: var(--sp-sm); }
  body.shell-v2 .tray-close { width: 44px; height: 44px; }
  /* 넓은 트레이도 모바일에선 화면 폭 바텀시트 — 높이만 고정(위쪽 8% 남김) · 좌우 여백은 모바일 기본값 */
  body.shell-v2 dialog.tray.tray--wide { width: 100%; height: 92dvh; }
  body.shell-v2 dialog.tray.tray--wide .tray-head { padding: 0 calc(var(--sp-md) + 44px + var(--sp-xs)) var(--sp-sm) var(--sp-md); }
  body.shell-v2 dialog.tray.tray--wide .tray-body { padding: var(--sp-md) var(--sp-md) calc(var(--sp-lg) + env(safe-area-inset-bottom)); }
  body.shell-v2 dialog.tray.tray--wide { --tray-ctl-top: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  body.shell-v2 dialog.tray, body.shell-v2 dialog.tray::backdrop { transition: none; }
}

/* 피드 상세 트레이(web/ui/feed/feedModal.js) — 이미지는 서랍 높이 안에 온전히 보이게. 확대(라이트박스)는 트레이 뒤에 깔려 걸지 않는다. */
body.shell-v2 .feed-tray .gd-img { max-height: 60dvh; width: auto; max-width: 100%; margin: 0 auto; object-fit: contain; }
body.shell-v2 .feed-tray .gd-content--nozoom :is(.gd-img-btn, .gd-ref-cell) { cursor: default; }


/* 중앙 모달 변형(`tray--modal`) — vaul 바텀시트 대신 화면 가운데. 요금제 변경·체크아웃·모의결제·영수증(spec_billing §5) 네 흐름이
 *   바텀시트 대신 이것을 쓴다 — top-layer <dialog> 메커니즘(포커스트랩·Esc·배경스크림)은 그대로, 자리·진입 모션만 다르다.
 *   drag:false 와 짝으로 쓴다(JS 에서 끌기 bindDrag 자체를 안 건다) — 그립을 감추기만 하면 머리를 여전히 끌 수 있어 위험하다. */
body.shell-v2 dialog.tray.tray--modal {
  position: fixed; inset: 0; margin: auto;
  width: min(560px, 100% - 2 * var(--sp-sm)); max-width: none;
  max-height: min(85dvh, 100% - 2 * var(--sp-lg));
  border-radius: var(--r-xl);
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.22s ease;
  @starting-style { transform: scale(0.96); opacity: 0; }
}
body.shell-v2 dialog.tray.tray--modal.is-leaving { transform: scale(0.98); opacity: 0; transition-duration: 0.14s; }
body.shell-v2 dialog.tray.tray--modal .tray-grip { display: none; }
body.shell-v2 dialog.tray.tray--modal .tray-head { padding-top: var(--modal-pad); touch-action: auto; }
body.shell-v2 dialog.tray.tray--modal::backdrop { background: rgba(9, 9, 9, 0.56); }
/* D1(2026-09-27 크리틱) — edge-attached.css 는 811px~ 에서 dialog.tray::before/::after 에 화면 가장자리
 *   필렛(오목한 귀 모양)을 덧댄다(바텀시트가 화면 밖으로 이어지는 것처럼). 중앙 모달은 화면 가장자리에
 *   붙지 않으므로 그 필렛이 카드 네 귀퉁이 중 바닥 좌우에 떠 있는 잘린 조각으로 남았다 — 위치·크기(inset:0 등)는
 *   이 파일의 더 높은 특이도(.tray--modal 한 클래스 더)가 이미 이기지만, ::before/::after 자체는 dialog.tray
 *   선택자라 그대로 그려졌다. content:none 으로 그 두 pseudo-element 생성 자체를 죽인다 — edge-attached.css
 *   는 그대로 두고(다른 서랍이 여전히 그 필렛을 쓴다) 여기서만 끈다. */
body.shell-v2 dialog.tray.tray--modal::before, body.shell-v2 dialog.tray.tray--modal::after { content: none; }
/* 요금 비교(4티어)만 넓게 — P2-1: 4개가 한 행에 들어가야 이 폭에서 billing-plan-grid(admin-billing.css, auto-fill)가 4장 모두를 한 줄에 담아 카드가 좁아지지 않는다. */
body.shell-v2 dialog.tray.tray--modal.tray--modal-lg { width: min(1120px, 100% - 2 * var(--sp-lg)); }
@media (max-width: 810px) {
  body.shell-v2 dialog.tray.tray--modal { width: calc(100% - 2 * var(--sp-sm)); border-radius: var(--r-xl); }
  body.shell-v2 dialog.tray.tray--modal.tray--modal-lg { width: calc(100% - 2 * var(--sp-sm)); }
}
@media (prefers-reduced-motion: reduce) {
  body.shell-v2 dialog.tray.tray--modal { transition: none; }
}