@font-face {
  font-family: 'NEXON Lv2 Gothic';
  src: url('fonts/NEXONLv2Gothic-Medium.ttf') format('truetype');
  font-weight: 500;
  font-display: swap;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* iOS 사파리는 "문서 자체가 세로로 스크롤되는 페이지"일 때만 상·하단
   툴바를 접고, 레이아웃을 툴바 아래까지 포함한 큰 뷰포트 기준으로 잡는다.
   예전처럼 html/body에 height:100% + overflow:hidden을 걸고 안쪽 div만
   스크롤시키면 사파리 입장에서는 스크롤이 없는 페이지라, 툴바가 불투명한
   UI로 고정되고 그 아래는 아예 페이지 영역이 되지 않아 잘려 보인다.
   그래서 스크롤 주체를 문서로 되돌린다. 배경을 #000으로 둔 것도 같은
   이유 — 툴바 틴트와 러버밴드(끝까지 당겼을 때) 영역까지 검게 이어진다. */
html,
body {
  width: 100%;
  height: auto;
  /* [주의] 여기에 overflow를 걸지 않는다. 루트 요소의 overflow는 뷰포트로
     전파돼서 스크롤 동작 전체에 관여한다. hidden은 이 요소를 스크롤
     컨테이너로 만들어 #control-bars의 position:sticky를 풀어버렸고,
     clip 역시 사파리가 툴바를 접을지 판단하는 데 영향을 줄 수 있다.
     이 페이지는 그리드가 auto-fill/minmax로 폭에 맞춰 접히므로 가로로
     넘칠 일이 없다 — 넘침 방지를 걸 이유 자체가 없다. */
  background: #000;
}

/* 세로 스크롤바 자리를 항상 비워 둔다. 상세 오버레이를 열면 body.detail-open
   이 position:fixed + overflow:hidden 을 걸어 문서가 넘치지 않게 되는데,
   그때 데스크탑(윈도우 크롬/엣지)의 스크롤바가 사라진다. 이 스크롤바는
   콘텐츠 위에 뜨는 게 아니라 레이아웃 폭을 실제로 차지하므로, 사라지는
   순간 쓸 수 있는 가로폭이 15~17px 늘어나면서 화면 전체가 살짝 커졌다가
   닫을 때 되돌아온다. innerWidth 도 같이 바뀌어 windowResized()가
   buildGridView()로 그리드를 다시 그리기 때문에 더 크게 티가 난다.
   stable 로 자리를 고정해 두면 스크롤바가 있든 없든 폭이 변하지 않는다.
   맥·모바일처럼 스크롤바가 겹쳐 뜨는 환경에서는 원래 폭을 차지하지 않아
   이 선언이 아무 영향도 주지 않는다. */
html {
  scrollbar-gutter: stable;
}

#archive-wrap {
  width: 100%;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* 버튼 두 줄 묶음(그래픽 이름 + 수집순/오차율순) — 문서가 스크롤돼도 화면
   맨 위에 붙어 있는다. 메뉴 버튼(z-index 200)·메뉴 커튼(190)·상세 오버레이
   (100)보다 낮게 둬서, 메뉴를 열거나 그래픽을 누르면 그 아래로 가려진다.

   배경을 명시한 이유 — 버튼 자체는 흰색이지만 그 사이/바깥의 1px 빈틈으로
   뒤의 그래픽이 비쳐 지나가면 고정된 바가 아니라 떠 있는 것처럼 보인다.
   아래 .mode-row의 border-top(4px 검정)과 맞춰 검정으로 깔아둔다. */
#control-bars {
  position: sticky;
  top: 0;
  z-index: 10;
  flex-shrink: 0;
  background: #111;
}

/* 메뉴 커튼은 탭 줄을 덮는 게 맞다(위 주석의 쌓임 순서). 다만 덮이기만 해야
   하고 자리가 움직이면 안 된다 — 아래 두 규칙이 그걸 지킨다.

   shared/menu.css 의 body.menu-open{overflow:hidden} 이 걸리면 body 가
   스크롤 컨테이너가 된다. 이 페이지는 html 에 overflow-x:clip 이 있어 body 의
   overflow 가 뷰포트로 전파되지 않기 때문이다. 그러면 #control-bars 의
   sticky 가 뷰포트가 아니라 '스스로는 스크롤되지 않는 body' 를 기준으로
   잡혀, 스크롤한 상태에서 메뉴를 열면 탭 줄이 문서 맨 위로 튕겨 나간다.
   커튼을 닫아도 제자리로 안 돌아오므로 탭이 사라진 것처럼 보인다.

   그래서 body 쪽 잠금은 되돌리고(= overflow:visible), 실제 스크롤 주체인
   html 을 잠근다. style.css 가 menu.css 보다 먼저 로드되므로 같은 선택자로는
   덮이지 않아 html 을 붙여 우선순위를 올렸다. */
html body.menu-open {
  overflow: visible;
}

html:has(body.menu-open) {
  /* overflow-x:clip 은 그대로 둔다 — 위쪽 html/body 주석 참고.
     세로만 잠가도 뒤 페이지는 움직이지 않는다. */
  overflow-y: hidden;
}

/* 시리즈 선택(burst, bloom, ...) — 여백 없이 화면 양 끝을 꽉 채우는 세그먼트.
   버튼이 flex:1로 균등 분할하므로 개수가 늘어나도(최대 10개 예정)
   가로 한 줄 안에서 계속 세로로만 쪼개진다. */
.shape-row {
  flex-shrink: 0;
  display: flex;
  width: 100%;
}

/* 세트 이름(BURST / BLOOM)이 버튼의 전부다. 표시만 대문자로 바꾸고
   HTML에는 소문자를 그대로 둔다 — 메뉴 항목(shared/menu.css)과 같은
   방식이라, 검색·스크린리더에는 원래 표기가 남는다.

   글자 크기는 버튼 개수를 기준으로 잡는다. 지금은 둘이 한 줄을 반씩 나눠
   쓰므로 320px 화면에서도 한 칸이 160px이라 여유가 있다. 탭을 더 붙이면
   한 칸이 좁아지니 최소·최대값을 같이 낮춰야 한다. */
.shape-btn {
  flex: 1;
  padding: clamp(14px, 2.6vmin, 20px) 0;
  background: #fff;
  border: none;
  color: #111;
  font-family: 'Pretendard', sans-serif;
  font-weight: 800;
  font-size: clamp(19px, 3.4vmin, 28px);
  text-transform: uppercase;
  /* 대문자는 글자 사이가 답답해 보여 소문자와 달리 자간을 살짝 벌린다. */
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s;
}

.shape-btn:hover {
  background: #ddd;
}

.shape-btn.active {
  background: #111;
  color: #fff;
}

/* 정렬 선택(수집순/오차율순) — 시리즈 선택 바로 아래 여백 없이 붙되,
   시리즈 바와 헷갈리지 않도록 폰트·세로폭을 작게 하고 경계선으로 구분. */
.mode-row {
  flex-shrink: 0;
  display: flex;
  width: 100%;
  border-top: 4px solid #111;
}

.mode-btn {
  flex: 1;
  padding: clamp(6px, 1.2vmin, 10px) 0;
  background: #fff;
  border: none;
  color: #111;
  /* 위 시리즈 버튼(Pretendard)과 글꼴을 달리해 두 줄의 역할을 구분한다.
     메인 페이지 본문(.main-text)과 같은 Wanted Sans다. */
  font-family: 'Wanted Sans', 'Pretendard', sans-serif;
  font-weight: 800;
  font-size: clamp(13px, 2.2vmin, 17px);
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s;
}

.mode-btn:hover {
  background: #ddd;
}

.mode-btn.active {
  background: #111;
  color: #fff;
}

/* QR 상세 오버레이(z-index 100)가 열려 있는 동안에는 메뉴 버튼을 숨긴다.
   메뉴는 z-index 200이라 그냥 두면 모달 위에 버튼이 떠서, 상세를 보다가
   엉뚱하게 메뉴가 열린다. :has()를 못 쓰는 구형 브라우저에서는 이 규칙만
   무시되고 나머지 동작은 그대로다. */
body:has(#detail-overlay.open) #menu-btn {
  opacity: 0;
  pointer-events: none;
}

/* 수집순 / 오차율순 — 실제 CSS Grid. 열 수는 화면 너비에 맞춰
   auto-fill이 자동으로 정하고, 셀 하나하나가 독립된 <canvas>를 담는다. */
#canvas-holder {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 6px;
  /* 아래쪽에 안전 영역(env(safe-area-inset-bottom))만큼 여백을 더 주면
     마지막 줄이 툴바·홈 인디케이터에 가리지 않지만, 대신 그리드가 툴바
     위에서 끊겨 화면 아래쪽이 빈 검정 띠로 남는다. 그래픽이 툴바 아래까지
     이어져 보이는 쪽을 택했으므로 그 여백을 두지 않는다 — 네 변 모두 6px. */
  padding: 6px;
  width: 100%;
  align-content: start;
  /* 스크롤은 문서가 담당한다 — 여기에 overflow를 주면 사파리가 툴바를
     접지 않는다(위 html/body 주석 참고). */
  /* 그리드 영역 배경은 항상 검게 (셀 사이 간격·캔버스 로드 전에도 검게).
     transition을 두면 첫 로드 때 흰색→검정으로 페이드돼서 넣지 않는다. */
  background: #000;
}

.archive-cell {
  width: 100%;
  aspect-ratio: 1 / 1;
  min-width: 0;
}

.archive-cell canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: pointer;
}

/* 그래픽 클릭 시 뜨는 상세 오버레이 — 배경(현재 페이지)이 어두워지면서
   반투명해 보이는 효과, 박스 밖 클릭 시 닫힘. */
#detail-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
  z-index: 100;
}

#detail-overlay.open {
  opacity: 1;
  pointer-events: auto;
}

.detail-box {
  /* 상세 UI의 '선' 두께. 박스 테두리, 좌우 ‹ › 버튼 테두리, 네 꼭짓점
     장식의 검은 띠가 모두 이 값을 쓴다. 한 군데서 바꾸면 셋이 같이
     움직이므로 굵기가 서로 어긋날 일이 없다. */
  --line: 5pt;
  position: relative;
  background: #fff;
  /* 흰 배경을 테두리 안쪽까지만 칠한다. 기본값(border-box)으로 두면 흰색이
     검은 테두리 밑에까지 깔리는데, 5pt는 6.67px이라 테두리 바깥 끝이 픽셀
     경계에 안 떨어진다. 그 끝 픽셀이 안티에일리어싱으로 반투명해지면서
     밑에 깔린 흰색이 1px 선처럼 비쳐 나왔다. 밑에 흰색이 없으면 비칠 것도
     없고, 끝 픽셀은 뒤의 어두운 오버레이와 섞여 연한 검정이 된다. */
  background-clip: padding-box;
  border: var(--line) solid #111;
  padding: clamp(14px, 3vmin, 28px);
  width: clamp(300px, 60vmin, 560px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 2vmin, 16px);
}

/* 네 꼭짓점에 걸치는 검은 사각형 장식 — QR 코드 파인더 패턴 느낌.
   %기반 위치라 박스 크기(clamp)가 바뀌어도 꼭짓점에 그대로 맞는다. */
.corner {
  position: absolute;
  width: clamp(22px, 4vmin, 36px);
  height: clamp(22px, 4vmin, 36px);
  background: #111;
  transform: translate(-50%, -50%);
}

/* 검은 사각형 안쪽에 흰 사각형을 띄워 인터페이스가 살짝 분리된 듯한
   느낌을 준다 — QR 파인더 패턴처럼 검은 테두리만 남기는 형태.

   inset을 %로 두면 남는 검은 띠의 굵기가 사각형 크기를 따라간다. 크기가
   clamp(22px, 4vmin, 36px)이라 데스크탑에서는 대개 36px에 걸리는데, 그때
   28%면 약 10px이라 박스·버튼 테두리(5pt ≈ 6.67px)보다 눈에 띄게 굵었다.
   선 두께 자체를 넣어 셋을 같은 굵기로 맞춘다. */
.corner::after {
  content: '';
  position: absolute;
  inset: var(--line);
  background: #fff;
}

.corner-tl {
  top: 0;
  left: 0;
}

.corner-tr {
  top: 0;
  left: 100%;
}

.corner-bl {
  top: 100%;
  left: 0;
}

.corner-br {
  top: 100%;
  left: 100%;
}

/* 그래픽 오브젝트 / QR 이미지가 앞뒤로 붙은 카드. 박스를 클릭할 때마다
   sketch.js가 #detail-flipper 의 rotateY 각도를 같은 방향으로 180도씩 더한다.
   perspective를 주지 않아(원근 없음) 3D로 던져지는 느낌 없이 카드가 가로로
   납작해졌다 펴지듯 뒤집히고, overflow:hidden으로 회전 중 박스 밖으로
   삐져나오는 부분을 잘라낸다. */
#detail-media {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  cursor: pointer;
}

#detail-flipper {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.6s ease;
}

#detail-graphic,
#detail-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
}

/* QR 이미지는 카드 뒷면 — 미리 180도 돌려두면 플립됐을 때 정방향이 된다. */
#detail-media img {
  display: block;
  object-fit: cover;
  transform: rotateY(180deg);
}

#detail-graphic canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* 이전/다음 버튼 — 흰 fill · 검은 stroke(크레딧 버튼과 같은 톤).
   상세 박스(.detail-box, position:relative)의 좌우 바깥쪽에 딱 붙는다. */
.detail-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 110;
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(51px, 10.5vmin, 90px);
  height: clamp(51px, 10.5vmin, 90px);
  background: #fff;
  /* .detail-box와 같은 이유로 흰 배경을 테두리 안쪽까지만 칠한다. 테두리는
     6px 정수지만 크기가 clamp(…vmin)이고 top:50% + translateY(-50%)로
     놓이기 때문에, 창 크기에 따라 가장자리가 픽셀 경계에 안 떨어진다.
     그러면 끝 픽셀이 반투명해지면서 밑에 깔린 흰 배경이 검은 선 바깥으로
     비친다. hover(#ddd)도 마찬가지라 같이 막힌다. */
  background-clip: padding-box;
  border: var(--line) solid #111;
  color: #111;
  font-family: 'Pretendard', sans-serif;
  font-weight: 800;
  font-size: clamp(34px, 8vmin, 56px);
  line-height: 1;
  /* ‹ › 글리프가 글꼴상 baseline보다 살짝 아래에 앉아 시각적으로 내려가
     보이므로, 아래 여백을 조금 줘서 flex 중앙 정렬 기준을 위로 당긴다. */
  padding-bottom: 0.14em;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s;
}

.detail-nav:hover {
  background: #ddd;
}

.detail-nav:active {
  background: #111;
  color: #fff;
}

/* 버튼 테두리가 박스 테두리 위에 얹혀 한 줄로 보이도록, 버튼 테두리
   두께만큼 박스 쪽으로 당긴다. --line 을 그대로 쓰므로 선 굵기를 바꿔도
   이 보정값이 따로 어긋나지 않는다. */
#detail-prev {
  right: 100%;
  margin-right: calc(-1 * var(--line));
}

#detail-next {
  left: 100%;
  margin-left: calc(-1 * var(--line));
}

.detail-box p {
  font-family: 'Wanted Sans', 'Pretendard', sans-serif;
  font-weight: 700;
  font-size: clamp(18px, 3.6vmin, 28px);
  letter-spacing: -0.01em;
  color: #111;
}

/* [수집 순번 표시] sketch.js의 SHOW_QR_NUMBER_PREFIX가 true일 때만 붙는
   클래스 — 번호(No.n)는 왼쪽, 이름은 오른쪽에 배치한다. 번호 표시를
   없앨 때는 이 블록을 지우면(플래그도 false로) 위 .detail-box p의
   가운데 정렬로 자동 복구된다. */
.detail-box p.has-number {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  align-self: stretch;
  width: 100%;
  /* QR 사진 자체의 여백(코너 파인더 안쪽 여백)만큼 좌우로 들여써서,
     No.n / 이름 텍스트가 QR 패턴의 실제 좌우 끝보다 밖으로 튀어나가지
     않게 한다. 이미지마다 살짝 다를 수 있어 대략치(7%)로 맞춤. */
  padding: 0 7%;
}

/* 상세 오버레이가 열려 있는 동안 뒤 페이지가 같이 스크롤되지 않게 잠근다.
   문서 스크롤로 바뀌면서 필요해진 처리다. iOS에서는 body에 overflow:hidden만
   줘도 흘러내리는 경우가 있어 position:fixed로 고정하고, 원래 스크롤 위치는
   sketch.js가 --scroll-lock-top 에 담아 두었다가 닫을 때 복구한다. */
body.detail-open {
  position: fixed;
  top: var(--scroll-lock-top, 0);
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

/* 위 잠금이 걸리면 #control-bars의 position:sticky가 무력해진다. body가
   position:fixed가 되면서 문서 스크롤이 0으로 떨어지면, sticky는 붙잡을
   스크롤이 없어 그냥 제자리(= body 맨 위)에 놓인다. 그런데 그 body는
   top:-스크롤량 만큼 올라가 있으므로, 결국 바도 같이 그만큼 위로 딸려
   올라간다 — 열고 닫을 때마다 탭 줄이 스크롤한 거리만큼 위아래로 튀는
   이유다.

   그 어긋난 양(--scroll-lock-top)을 부호만 뒤집어 되돌려 준다.
   transform은 레이아웃을 건드리지 않으므로, position을 fixed로 바꿀 때와
   달리 바가 흐름에서 빠지면서 아래 그리드가 위로 밀려 올라가는 일이 없다. */
body.detail-open #control-bars {
  transform: translateY(calc(-1 * var(--scroll-lock-top, 0px)));
}
