/* 네오 채소 도감 — 홀로그램 효과는 neo/pepper_neo_hologram.html 에서 옮겨왔다.
   원본과 달라진 핵심 한 가지: 마우스/기울기 변수(--mouse-x, --rotate-x, --glare …)가
   원본에선 :root 에 붙어 있었다. 카드가 한 장일 땐 그래도 되지만 도감에선 6장이
   같은 변수를 공유해 전부 같이 기울어진다. 그래서 변수를 .stage 단위로 내렸다.
   자식(.card/.foil/.glare)은 상속으로 받으므로 효과 코드 자체는 원본 그대로다. */

/* 원래 gohome 레포의 shared/base.css 를 함께 링크해 쓰던 페이지다. 여기서는 그 파일이
   없고, public/ 에 통째로 떨어뜨려 두는 정적 데모라 폴더 하나로 자족해야 해서
   실제로 쓰던 규칙만 옮겨 왔다. 색 변수는 이 페이지가 자기 팔레트를 쓰므로 안 가져온다.
   button 줄이 빠지면 확대 뷰의 이전/다음/닫기 버튼이 브라우저 기본 폰트로 튄다. */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { line-height: 1.6; -webkit-font-smoothing: antialiased; }
button, input, select, textarea { font: inherit; color: inherit; }
img { max-width: 100%; height: auto; }

/* 폰에서 카드를 꾹 누르면 iOS 는 "이미지 저장" 팝업(-webkit-touch-callout)을, 안드로이드는
   이미지 선택·드래그를 띄운다. 그 순간 꾹 누르기 제스처가 끊기고 파란 선택 블록만 남는다.
   이 페이지는 읽어서 복사할 문서가 아니라 만지는 화면이라 통째로 끈다.
   접두사 붙은 -webkit-user-select 는 iOS Safari 가 아직 표준 이름을 안 보므로 함께 쓴다.
   CSS 로 막히는 건 iOS 뿐이라, 안드로이드의 길게 누르기 메뉴는 main.js 에서 따로 막는다.
   dialog 를 따로 적는 이유: 확대 뷰와 이머시브는 <dialog> 라 top layer 로 올라가고,
   그때 html 에서 내려오던 상속이 끊겨 user-select 가 text 로 되돌아간다. */
html, dialog {
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

/* 데스크톱에서 사진을 끌어다 놓는 것까지 막는다 */
img { -webkit-user-drag: none; }

:root {
  color-scheme: dark;
  --ink: #eefaf1;
  --ink-dim: #99aaa1;
  --ink-faint: #6f7d76;
  --line-soft: rgba(255, 255, 255, .12);
}

/* ── 페이지 셸 ─────────────────────────────────────────── */

/* 카드 뒤 글로우(.stage::before)는 좌우로 15% 삐져나가도록 되어 있어서, 좁은 화면에서
   막아주지 않으면 가로 스크롤이 생긴다. body 에만 걸면 뷰포트로 전파되지 않아
   실제로 안 잘리므로 html 에 건다. clip 은 hidden 과 달리 세로축을 스크롤
   컨테이너로 만들지 않는다. */
html { overflow-x: clip; }

body {
  margin: 0;
  min-height: 100svh;
  overflow-x: clip;
  color: var(--ink);
  font-family: Inter, Pretendard, "Noto Sans KR", system-ui, sans-serif;
  /* 평평한 바탕색만. 실제 그라디언트는 아래 ::after 가 그린다.
     이 색이 캔버스로 전파돼 화면 전체의 바닥이 된다 (html 에 배경이 없으므로). */
  background: #0e0b05;
}

/* 배경 그라디언트. 예전엔 body 에 직접 얹고 `background-attachment: fixed` 로
   붙박이를 만들었는데, **그게 모바일 스크롤을 메인스레드로 끌어내렸다.** 크롬은
   고정 배경을 가진 요소를 합성 스크롤에서 제외하고 스크롤 프레임마다 다시 그린다
   (DevTools 의 Rendering → Scrolling performance issues 에 사유가 그대로 찍힌다).
   4겹 그라디언트를 뷰포트 전체 크기로 매 프레임 다시 칠하던 셈이다.

   그래서 같은 그림을 `position: fixed` 인 별도 레이어로 내렸다. 스크롤과 무관한
   레이어라 크롬이 합성만 하면 되고, **화면은 픽셀 단위로 이전과 같다** —
   attachment:fixed 든 fixed 요소든 그라디언트의 기준 상자가 똑같이 뷰포트라서다.
   덤으로 iOS 사파리도 고쳐진다. 거기선 attachment:fixed 가 아예 안 먹었다.

   z-index 는 body::before(점 패턴, 0) 와 .page(1) 아래로 내려가야 하므로 -1.
   body 는 스택 컨텍스트를 안 만들어서 이 셋이 같은 판에서 순서대로 쌓인다. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  /* 안드로이드 크롬은 주소창이 접히면 화면이 세로로 커지는데, fixed 요소는 레이아웃
     뷰포트에 붙어 있어서 아래쪽에 안 덮인 띠가 남는다. lvh(주소창이 접힌 상태의
     높이)로 처음부터 큰 쪽에 맞춰 둔다. inset:0 이 함께 있으므로 lvh 를 모르는
     브라우저에서도 height 가 auto 로 떨어져 뷰포트를 그대로 덮는다. */
  height: 100lvh;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 0%, rgba(255, 216, 56, .10), transparent 34rem),
    radial-gradient(circle at 8% 22%, rgba(0, 217, 255, .09), transparent 28rem),
    radial-gradient(circle at 92% 68%, rgba(255, 50, 193, .10), transparent 30rem),
    linear-gradient(160deg, #151206 0%, #0e0b05 46%, #050402 100%);
}

/* 원본의 점 패턴 오버레이 */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .22;
  background-image: radial-gradient(rgba(255, 255, 255, .16) .65px, transparent .65px);
  background-size: 5px 5px;
  mask-image: linear-gradient(to bottom, black, transparent 92%);
}

.page {
  position: relative;
  z-index: 1;
  width: min(100%, 1180px);
  margin: 0 auto;
  padding: 20px clamp(16px, 4vw, 40px) 64px;
  filter: blur(0);
  transition: filter 280ms ease;
}

.topbar {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: clamp(28px, 6vw, 52px);
}

.back {
  color: var(--ink-dim);
  font-size: 13px;
  text-decoration: none;
  transition: color .2s ease;
}

.back:hover,
.back:focus-visible { color: var(--ink); }

/* 도감 → 홀로 스튜디오(studio.html). 왼쪽의 '← DAENGS' 와 붙어 있으면 둘 다 뒤로
   가는 링크처럼 보여서 오른쪽 끝으로 밀어 둔다. 스튜디오 쪽 topbar 에는 이 링크가
   없으므로 저기서는 아무 일도 안 일어난다. */
.studio-link { margin-left: auto; }

.eyebrow {
  margin: 0 0 6px;
  color: var(--ink-faint);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .22em;
  text-transform: uppercase;
}

.masthead { text-align: center; }

.masthead h1 {
  margin: 0;
  font-size: clamp(26px, 5vw, 44px);
  line-height: 1.06;
  letter-spacing: -.045em;
}

.count {
  display: inline-block;
  margin-top: 12px;
  padding: 6px 13px;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  color: #b7c7bf;
  background: rgba(255, 255, 255, .04);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  backdrop-filter: blur(10px);
}

.hint {
  margin: 14px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  color: var(--ink-dim);
  font-size: 13px;
  letter-spacing: -.01em;
}

.hint::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ffe34f;
  box-shadow: 0 0 12px #ffe34f;
}

/* ── 도감 그리드 ───────────────────────────────────────── */

.dex {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr));
  gap: clamp(26px, 4vw, 44px) clamp(18px, 3vw, 34px);
  margin: clamp(34px, 7vw, 64px) 0 0;
  padding: 0;
  list-style: none;
}

/* 셀은 폭·높이가 균일하고, 그 안에서 카드가 자기 비율대로 가운데 선다.
   카드 높이는 전부 같고 폭만 비율만큼 달라진다 — 실물 카드 바인더와 같은 정렬이라
   한 픽셀도 자르지 않으면서 행이 어긋나지 않는다. */
.slot {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: 12px;
  justify-items: center;
}

.slot .frame {
  aspect-ratio: 4 / 5;
  width: 100%;
  display: grid;
  place-items: center;
}

.caption {
  text-align: center;
  line-height: 1.35;
}

.caption .no {
  display: block;
  color: var(--ink-faint);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .18em;
}

.caption .name {
  display: block;
  margin-top: 2px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -.02em;
}

.caption .stat {
  display: block;
  margin-top: 1px;
  color: var(--ink-faint);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

/* ── 카드 무대 ─────────────────────────────────────────── */

.stage {
  /* 원본에선 :root 에 있던 값들. 카드마다 따로 굴러야 해서 여기로 내렸다. */
  --mouse-x: 50%;
  --mouse-y: 50%;
  --rotate-x: 0deg;
  --rotate-y: 0deg;
  --glare: 0;
  --shadow-x: 0px;
  --shadow-y: 22px;

  position: relative;
  height: 100%;
  aspect-ratio: var(--ar);
  perspective: 1150px;
  touch-action: manipulation;
  isolation: isolate;
}

/* 카드 뒤 글로우 — 색은 카드마다 다르다 (--accent) */
.stage::before {
  content: "";
  position: absolute;
  z-index: -2;
  inset: 10% -15% -8%;
  /* 카드 밖으로 좌우 15% · 아래 8% 삐져나가는 장식이라 클릭을 먹으면 안 된다.
     빼먹으면 확대 뷰에서 이 글로우가 '이전' 버튼 왼쪽 절반을 덮어, 눌러도 버튼이
     아니라 .stage 가 잡혀 카드만 기울어진다. 그리드에서도 옆 카드의 hover 를 훔친다. */
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(ellipse,
    color-mix(in srgb, var(--accent) 34%, transparent),
    color-mix(in srgb, var(--accent2) 12%, transparent) 48%,
    transparent 72%);
  filter: blur(30px);
  transform: translateY(8%) scale(calc(1 + var(--glare) * .08));
  transition: transform .4s ease;
}

/* ── 겹 세 장: .spin(회전) · .tilt(기울기) · .card(그림) ──
   원래는 .card 하나가 기울기까지 맡았는데, 클릭 회전과 누끼 팝아웃이 붙으면서
   셋으로 갈랐다. **한 요소에 회전과 기울기를 같이 걸면 안 된다** — 중첩 3D 회전이
   되어 반 바퀴 근처에서 카드가 도는 게 아니라 비스듬한 바늘로 넘어간다.
   FLIP(확대 뷰로 날아가기)은 여전히 .stage 가 맡으므로 셋 다 서로 안 겹친다. */
.spin {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
}

/* 기울기. 누끼(.hero)가 카드와 같이 기울어야 해서 둘을 함께 감싸는 자리로 올렸다. */
.tilt {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--rotate-x)) rotateY(var(--rotate-y)) translateZ(0);
  transition: transform 620ms cubic-bezier(.2, .8, .2, 1);
}

/* 도는 동안에는 기울기를 접는다. .stage 의 인라인 변수를 상속으로 받는 대신
   여기서 직접 선언해 이긴다 — JS 를 건드리지 않아도 된다. */
.stage.is-spinning .tilt {
  --rotate-x: 0deg;
  --rotate-y: 0deg;
}

.card {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  appearance: none;
  background: transparent;
  overflow: hidden;
  border-radius: 3.05% / 2.2%;
  cursor: pointer;
  transform-style: preserve-3d;
  transition: filter 400ms ease;
  box-shadow:
    var(--shadow-x) var(--shadow-y) 55px rgba(0, 0, 0, .58),
    0 22px 44px rgba(0, 0, 0, .48),
    0 0 0 1px rgba(255, 255, 255, .18),
    inset 0 0 0 1px rgba(255, 255, 255, .2);
}

/* will-change 는 활성 카드에만 — 6장 전부에 걸면 레이어가 6개 생긴다 */
.stage:is(:hover, :focus-within) .tilt {
  transition: transform 85ms linear;
  will-change: transform;
}

.stage:is(:hover, :focus-within) .card {
  transition: filter 180ms ease;
  filter: saturate(1.07) contrast(1.03);
}

/* ── 누끼 팝아웃 ───────────────────────────────────────────
   확대 뷰에서 카드를 누르면 주인공이 카드 테두리 밖으로 떠오른다.
   **.card 의 형제여야 한다** — 안에 넣으면 .card 의 overflow:hidden 에 잘려
   프레임을 못 넘는다. 자리는 cards.mjs 의 scene.fit 이 정한다.
   쉴 때 안 보이게 두는 것은 의도다: 누끼가 카드 그림과 다른 렌더인 카드가 있어서
   (고구마) 겹쳐 두면 어긋난 두 겹이 같이 보인다. 떠오르면서 나타나게 한다. */
.hero {
  position: absolute;
  left: calc(var(--fx) * 1%);
  top: calc(var(--fy) * 1%);
  width: calc(var(--fw) * 1%);
  height: calc(var(--fh) * 1%);
  transform-origin: 50% 100%;
  transform: translateZ(var(--lift, 0px)) translateY(var(--rise, 0px)) scale(var(--grow, 1));
  filter: drop-shadow(0 18px 26px rgba(0, 0, 0, .58));
  opacity: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  transition: transform 520ms cubic-bezier(.2, .85, .25, 1), opacity 320ms ease;
}

/* 얼마나 올릴지(--rise)는 **JS 가 화면을 보고 px 로 넣는다** (main.js 의 pop()).
   비율로 박아 두면 안 된다 — 확대 뷰의 카드는 창 비율에 따라 화면보다 커지기도 해서,
   같은 값이 어떤 창에서는 프레임을 못 넘고 어떤 창에서는 화면 위로 잘려 나간다. */
.stage.is-popped .hero {
  --lift: 74px;
  --grow: 1.06;
  opacity: 1;
}

/* 떠오른 동안 카드에 인쇄된 주인공은 자국처럼 죽인다 — 안 죽이면 유령이 하나 더 보인다 */
.stage.is-popped .card {
  filter: brightness(.62) saturate(.7) blur(.6px);
}

.stage[data-pop="1"] .card { cursor: zoom-in; }
.stage[data-pop="1"].is-popped .card { cursor: zoom-out; }

.stage:active .card { cursor: grabbing; }

.card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 5px;
}

.art {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: fill;      /* 셀 비율 = 이미지 비율이라 늘어나지 않는다 */
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.foil,
.glare,
.grain,
.edge {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

.foil {
  opacity: calc(.11 + var(--glare) * .5);
  background:
    radial-gradient(circle at var(--mouse-x) var(--mouse-y), rgba(255, 255, 255, .92) 0 2%, rgba(255, 255, 255, .24) 8%, transparent 27%),
    conic-gradient(from 210deg at var(--mouse-x) var(--mouse-y),
      transparent 0deg, rgba(255, 49, 180, .55) 42deg, rgba(75, 235, 255, .72) 85deg,
      rgba(255, 247, 84, .58) 130deg, transparent 175deg, rgba(134, 76, 255, .48) 245deg,
      rgba(61, 255, 164, .58) 305deg, transparent 360deg),
    repeating-linear-gradient(125deg,
      rgba(255, 65, 191, .2) 0 8px, rgba(55, 232, 255, .21) 8px 16px,
      rgba(255, 232, 68, .18) 16px 24px, rgba(112, 255, 164, .2) 24px 32px);
  background-size: 100% 100%, 145% 145%, 220% 220%;
  background-position: center, var(--mouse-x) var(--mouse-y), var(--mouse-x) var(--mouse-y);
  mix-blend-mode: color-dodge;
  filter: saturate(1.5) contrast(1.18);
  transform: translateZ(1px);
}

.glare {
  opacity: calc(.04 + var(--glare) * .86);
  background:
    radial-gradient(circle at var(--mouse-x) var(--mouse-y),
      rgba(255, 255, 255, .95) 0%, rgba(255, 255, 255, .46) 5%,
      rgba(184, 255, 230, .14) 17%, transparent 39%),
    linear-gradient(113deg,
      transparent 18%, rgba(255, 255, 255, .08) 33%, rgba(255, 255, 255, .7) 46%,
      rgba(157, 255, 239, .18) 54%, transparent 72%);
  background-size: 100% 100%, 230% 230%;
  background-position: center, var(--mouse-x) var(--mouse-y);
  mix-blend-mode: screen;
  transform: translateZ(2px);
}

.grain {
  opacity: .12;
  background:
    repeating-radial-gradient(circle at 23% 31%, rgba(255, 255, 255, .7) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(92deg, transparent 0 3px, rgba(255, 255, 255, .16) 3px 4px);
  background-size: 43px 47px, 100% 100%;
  mix-blend-mode: overlay;
  transform: translateZ(2.5px);
}

/* 테두리 광택 — 아래쪽 색만 카드 accent 를 탄다 */
.edge {
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, .4),
    inset 12px 0 22px -20px rgba(255, 255, 255, .9),
    inset -12px 0 22px -20px rgba(68, 255, 211, .8),
    inset 0 -12px 25px -22px color-mix(in srgb, var(--accent2) 90%, transparent);
  transform: translateZ(3px);
}

/* ── 확대 뷰 ───────────────────────────────────────────── */

/* 폰 전용. 데스크톱에는 마우스와 설명 패널 안의 버튼이 있으므로 안 띄운다. */
.sheet-hint,
.viewer-close,
.edge-nav { display: none; }

/* dialog 를 스크롤 컨테이너로 쓰지 않는다.
   예전엔 여기에 overflow:auto 를 걸었는데, .stage::before 글로우가 아래로 8% 삐져나가
   있어서 내용이 전부 보이는데도 스크롤바가 생겼다(953px 화면에서 내용 620px,
   scrollHeight 679px). 굴려봐야 나오는 건 없고 카드만 위로 밀려 잘렸다.
   그래서 dialog 는 뷰포트를 덮는 고정 무대로 두고, 넘칠 수 있는 설명 패널 안에만
   스크롤을 둔다. 카드는 어떤 경우에도 안 움직이고 안 잘린다. */
.viewer {
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100dvh;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: clip;
  /* 폰에서 아래로 쓸어내릴 때 브라우저의 '당겨서 새로고침'이 먼저 물지 않도록 */
  overscroll-behavior: none;
  color: var(--ink);
  background: transparent;
}

/* display 는 [open] 에만 — 안 그러면 UA 의 dialog:not([open]){display:none} 을
   덮어써서 닫혀 있을 때도 보인다. */
.viewer[open] {
  display: grid;
  place-items: center;
}

/* 여기에 backdrop-filter 를 걸면 안 된다. Chrome 이 backdrop-filter 를 만나면
   별도의 backdrop root 를 만드는데, 그 안에서는 dialog 자식들의 mix-blend-mode 가
   무시된다 — foil/glare 가 블렌딩 없이 그냥 덮여서 카드 그림이 안 보인다.
   배경 흐림은 dialog 바깥의 .page 에 filter 로 건다 (아래 .is-viewing 참고). */
.viewer::backdrop {
  background: rgba(4, 4, 2, .9);
}

.viewer-inner {
  display: grid;
  grid-template-columns: auto minmax(0, 300px);
  align-items: center;
  gap: clamp(20px, 4vw, 48px);
  padding: clamp(18px, 3vw, 30px);
  max-height: 100dvh;
}

.viewer .stage {
  height: min(72svh, 680px);
  justify-self: center;
}

/* 스크롤이 필요하면 여기서만 굴러간다 — 사진은 휠에 반응하지 않는다 */
.detail {
  max-height: calc(100dvh - 2 * clamp(18px, 3vw, 30px));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* 확대 뷰가 열려 있는 동안 뒤 그리드를 흐린다. 카드가 날아가는 시간에 맞춰
   서서히 들어오고 나가도록 transition 은 .page 쪽(페이지 셸)에 걸어 뒀다. */
.is-viewing .page {
  filter: blur(5px);
  pointer-events: none;
}

.detail h2 {
  margin: 0;
  font-size: clamp(22px, 3.4vw, 32px);
  line-height: 1.08;
  letter-spacing: -.04em;
}

.detail .tagline {
  margin: 0 0 6px;
  color: var(--ink-dim);
  font-size: 14px;
}

.detail dl {
  margin: 22px 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 18px;
  font-size: 13px;
}

.detail dt {
  color: var(--ink-faint);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  align-self: center;
}

.detail dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* 기술 설명 — cabbage/mushroom 카드에만 인쇄돼 있다 */
.detail dd small {
  display: block;
  margin-top: 2px;
  color: var(--ink-faint);
  font-size: 11px;
  font-weight: 400;
  font-style: italic;
}

.detail .flavor {
  margin: 22px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  color: var(--ink-dim);
  font-size: 13px;
  font-style: italic;
  line-height: 1.5;
}

.detail .edition {
  display: inline-block;
  margin-top: 16px;
  padding: 6px 11px;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  color: color-mix(in srgb, var(--accent) 65%, #ffffff);
  background: rgba(255, 255, 255, .04);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.viewer-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 26px;
}

.viewer-nav button {
  padding: 8px 14px;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  color: var(--ink-dim);
  background: rgba(255, 255, 255, .04);
  font-size: 12px;
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease;
}

.viewer-nav button:hover,
.viewer-nav button:focus-visible {
  color: var(--ink);
  border-color: rgba(255, 255, 255, .3);
}

.viewer-nav .close { margin-left: auto; }

@media (max-width: 760px) {
  /* ── 폰: 카드를 화면 가득, 설명은 아래에서 올라오는 시트로 ────
     예전엔 카드(44svh)와 설명을 위아래로 나눠 담았는데, 그러면 폰에서 카드가
     작아서 포일이 거의 안 보였다. 이 데모는 카드를 보라고 만든 것이므로 카드에
     화면을 다 주고 설명은 감춘다.

     제스처 배정은 main.js 에 있다 — 탭=설명, 좌우=카드 넘기기, 아래로=닫기. */
  .viewer-inner {
    position: relative;          /* 설명 시트를 여기에 붙인다 */
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    place-items: center;
    width: 100%;
    height: 100dvh;
    gap: 0;
    padding: 10px;
    transition: padding 300ms cubic-bezier(.2, .8, .2, 1);
  }

  /* 높이로도 폭으로도 안 넘치게 — 둘 중 작은 쪽이 이긴다. 카드마다 비율이
     달라서(0.72 / 0.725 / 0.80) 폭 한계는 --ar 로 계산한다.
     **transform 이 아니라 height 로 줄인다** — .stage 의 transform 은 여닫을 때
     날아가는 FLIP 애니메이션이 쓰고 있어서 같은 속성을 두고 싸우게 된다. */
  .viewer .stage {
    height: min(88svh, calc((100vw - 20px) / var(--ar)));
    transition: height 300ms cubic-bezier(.2, .8, .2, 1);

    /* **이게 없으면 스와이프가 통째로 안 먹는다.** 기본값(.stage 의 manipulation)에서는
       손가락을 옆으로 미는 순간 브라우저가 "스크롤하려나 보다" 하고 제스처를 가져가
       버린다. 그러면 페이지로는 pointercancel 만 오고 pointermove 가 끊긴다 —
       탭은 멀쩡히 되는데 스와이프만 안 되는 증상이 이것이다.
       확대 뷰의 카드는 스크롤할 게 없으므로 다 꺼도 안전하다. 그리드의 카드는
       세로 스크롤을 해야 하므로 여기서만 끈다. */
    touch-action: none;
  }

  .viewer .stage .card { touch-action: none; }

  /* 설명이 올라오면 카드는 위로 비켜 준다 */
  .viewer.show-detail .viewer-inner { align-items: start; padding-top: 3vh; }
  .viewer.show-detail .stage { height: min(38svh, calc((100vw - 20px) / var(--ar))); }

  .detail {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    max-height: 74dvh;
    margin: 0;
    padding: 16px 20px calc(20px + env(safe-area-inset-bottom));
    text-align: center;
    background: rgba(12, 10, 5, .94);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -18px 44px rgba(0, 0, 0, .6);
    transform: translateY(101%);
    transition: transform 300ms cubic-bezier(.2, .8, .2, 1);
    will-change: transform;
  }

  .viewer.show-detail .detail { transform: none; }

  .detail { z-index: 2; }

  /* 좌우 넘기기 버튼. **폰에서 카드를 넘기는 유일한 길이다** — 쓸어서 넘기기는
     포일 구경과 구분이 안 돼서 뺐다 (main.js 의 제스처 주석 참고).

     그렇다고 계속 띄워 두면 카드를 가린다. 확대한 직후와 카드에서 손을 뗄 때만
     잠깐 나왔다 사라진다 (show-nav, main.js 의 peekNav). */
  .edge-nav {
    display: grid;
    place-items: center;
    position: absolute;
    top: 50%;
    translate: 0 -50%;
    z-index: 3;
    width: 44px;
    height: 44px;
    padding: 0 0 3px;
    border: 0;
    border-radius: 50%;
    background: rgba(8, 7, 4, .5);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14);
    color: var(--ink);
    font-size: 24px;
    line-height: 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity 200ms ease;
  }

  .edge-nav.prev { left: 6px; }
  .edge-nav.next { right: 6px; }

  .viewer.show-nav .edge-nav {
    opacity: .82;
    pointer-events: auto;
  }

  /* 설명이 열리면 시트 안에 이전/다음이 이미 있다. peekNav 가 애초에 안 띄우지만,
     여는 순간 이미 떠 있던 걸 즉시 걷으려면 이 규칙이 필요하다. */
  .viewer.show-detail .edge-nav { opacity: 0; pointer-events: none; }

  /* 항상 보이는 닫기 버튼. 예전에는 닫기가 설명 시트 안에만 있어서, 닫으려면 먼저
     설명을 열어야 했다. */
  .viewer-close {
    display: grid;
    place-items: center;
    position: absolute;
    top: calc(10px + env(safe-area-inset-top));
    right: 12px;
    z-index: 3;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    /* backdrop-filter 는 쓰지 않는다 — dialog 안에서 backdrop root 가 생기면
       카드의 mix-blend-mode 가 무시된다 (.viewer::backdrop 주석 참고) */
    background: rgba(8, 7, 4, .62);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
    color: var(--ink);
    font-size: 15px;
    line-height: 1;
  }

  /* 시트 손잡이. 이게 있어야 "위로 올라온 판"으로 읽힌다 */
  .detail::before {
    content: "";
    display: block;
    width: 38px;
    height: 4px;
    margin: 0 auto 14px;
    border-radius: 99px;
    background: rgba(255, 255, 255, .3);
  }

  .detail dl { margin-top: 14px; justify-content: center; }
  .detail .flavor { margin-top: 14px; padding-top: 10px; }
  .viewer-nav { margin-top: 16px; justify-content: center; }
  .viewer-nav .close { margin-left: 0; }

  /* 제스처를 알려 주는 한 줄. 설명이 열리면 시트가 대신 말해주므로 사라진다. */
  .sheet-hint {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(10px + env(safe-area-inset-bottom));
    margin: 0;
    color: var(--ink-faint);
    font-size: 12px;
    text-align: center;
    text-shadow: 0 1px 6px rgba(0, 0, 0, .9);
    pointer-events: none;
    transition: opacity 200ms ease;
  }

  .viewer.show-detail .sheet-hint { opacity: 0; }
}

/* ── 열고 닫는 전환 ───────────────────────────────────────
   카드 자체는 main.js 가 FLIP(그리드 슬롯 ↔ 가운데)으로 날려 보낸다.
   배경과 설명 패널은 거기에 맞춰 페이드만 시킨다. */

@keyframes viewer-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes detail-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

.viewer[open]::backdrop { animation: viewer-fade 280ms ease both; }
.viewer.is-closing::backdrop { animation: viewer-fade 220ms ease reverse both; }

/* 설명은 열 때만 떠오른다. ←/→ 로 카드를 넘길 때까지 매번 깜빡이면 산만하다. */
.viewer.is-opening .detail { animation: detail-in 280ms 60ms ease both; }
.viewer.is-closing .detail { animation: detail-in 180ms ease reverse both; }

/* **이 규칙은 반드시 위 두 줄보다 뒤에 있어야 한다.** 특정도가 똑같아서(0,3,0)
   순서로만 갈린다 — 폰 블록(760px) 안에 써 두면 그쪽이 앞이라 아무 힘이 없다.

   폰에서는 설명이 아래에 숨어 있다가 translateY 로 올라오는 시트다. 위 애니메이션은
   `both` 라 끝난 자리에 transform:none 을 남기는데, 그러면 **숨어 있어야 할 시트가
   카드를 여는 동안 잠깐 나타났다가** is-opening 이 걷히면서 도로 내려간다.
   "확대할 때 상세가 번쩍했다 사라진다"가 이 증상이다. */
@media (max-width: 760px) {
  .viewer.is-opening .detail,
  .viewer.is-closing .detail { animation: none; }
}

/* ── 키보드 ────────────────────────────────────────────
   포커스만으로도 홀로그램이 보이도록 살짝 기울여 둔다. 포인터가 올라오면
   인라인 스타일이 이 값들을 덮으므로 마우스 조작이 항상 우선한다. */
.slot .stage:has(.card:focus-visible) {
  --glare: .32;
  --rotate-x: -3.5deg;
  --rotate-y: 5deg;
}

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
  .tilt { transition: none; transform: none !important; }
  .hero { transition: none; }
  .stage::before { transition: none; }
  .foil, .glare { opacity: .16; }
  .page { transition: none; }
  .slot .stage:has(.card:focus-visible) { --rotate-x: 0deg; --rotate-y: 0deg; }

  .viewer[open]::backdrop,
  .viewer.is-closing::backdrop,
  .viewer.is-opening .detail,
  .viewer.is-closing .detail { animation: none; }
}
