/* =========================================================
   AquaLine Components  —  문서: 디자인/02-components.md
   순서: 셸 / 버튼 / 폼 / 카드 / 배지 / 스텝 / 예약 / 서명 / 티켓 / 표 / 오버레이
   ========================================================= */

/* ============ 1. 앱 셸 ============ */
.topbar {
  position: sticky; top: 0; z-index: var(--z-topbar);
  height: var(--h-topbar);
  background: rgba(255, 255, 255, .82);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--c-line);
}
.topbar--deep {
  background: rgba(9, 22, 37, .72);
  border-bottom-color: rgba(255, 255, 255, .12);
}
.topbar__in { display: flex; align-items: center; gap: var(--sp-6); height: 100%; }
.topbar__nav { display: flex; align-items: center; gap: var(--sp-5); }
.topbar__nav a {
  color: var(--c-muted); font-size: var(--fs-sm); font-weight: var(--fw-md);
  text-decoration: none; transition: color var(--t-fast) var(--ease);
}
.topbar__nav a:hover { color: var(--c-ink); }
.topbar__nav a.is-on { color: var(--c-primary); font-weight: var(--fw-sb); }

/* ---- 1.4 좁은 화면의 내비게이션 (.topbar__burger + .mnav) ----
   720px 이하에서 .topbar__nav 를 hide-sm 으로 숨겨 놓고 **대체 메뉴가 없었다.**
   휴대폰에서는 로고 말고 누를 것이 없어 대시보드·전체 풀장·마이페이지·예약·강사
   어디로도 갈 수 없었고, 로그아웃 버튼은 화면 밖으로 65px 밀려나 있었다
   (마이페이지에도 로그아웃이 없어 로그아웃할 방법 자체가 없었다).

   경계를 900px 로 잡는다. 720이 아니라 900인 이유: 768(태블릿 세로)에서
   메뉴 + 언어 + 이름 + 배지 + 로그아웃이 다 나오면 상단바가 94px 넘친다.
   ============================================================ */
.topbar__burger {
  display: none;                                  /* 900 초과에서는 존재하지 않는다 */
  align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;          /* 터치 타깃 44 */
  padding: 0; border: 1px solid var(--c-line); border-radius: var(--r-sm);
  background: var(--c-bg); color: var(--c-ink); cursor: pointer;
}
.topbar__burger:hover { background: var(--c-bg-soft); }
.topbar__burger span,
.topbar__burger span::before,
.topbar__burger span::after {
  display: block; width: 17px; height: 2px; border-radius: 2px;
  background: currentColor; content: "";
}
.topbar__burger span { position: relative; }
.topbar__burger span::before { position: absolute; top: -5px; }
.topbar__burger span::after  { position: absolute; top: 5px; }
/* 처리할 일이 있으면 점을 찍는다 — 메뉴를 열어야만 보이는 배지를 대신한다 */
.topbar__burger[data-dot="1"]::after {
  content: ""; position: absolute; margin: -18px 0 0 24px;
  width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--c-danger); border: 2px solid var(--c-bg);
}
.topbar--deep .topbar__burger { border-color: rgba(255, 255, 255, .3); background: rgba(255, 255, 255, .1); color: #fff; }

/* 메뉴 서랍 — 오른쪽에서 나온다. 상세용 .drawer(520px)와 쓰임이 달라 따로 둔다:
   이쪽은 '어디로 갈지', 저쪽은 '무엇을 볼지'다. */
.mnav {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: var(--z-drawer);
  width: min(320px, 86vw); background: var(--c-bg);
  box-shadow: var(--sh-4); display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform var(--t-slow) var(--ease-out);
  visibility: hidden;                             /* 닫혔을 때 탭 순서에서 빠진다 */
}
.mnav.is-open { transform: none; visibility: visible; }
.mnav__hd {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--c-line);
}
.mnav__bd { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-4) var(--sp-3) var(--sp-6); }
.mnav__ft { padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--c-line); background: var(--c-bg-soft); }

.mnav__i {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: 48px; padding: 0 var(--sp-4); border-radius: var(--r-sm);
  font-size: var(--fs-body); font-weight: var(--fw-md); color: var(--c-ink-2);
  text-decoration: none;
}
.mnav__i:hover { background: var(--c-bg-soft); color: var(--c-ink); text-decoration: none; }
.mnav__i[aria-current="page"] { background: var(--c-primary-soft); color: var(--c-primary-ink); font-weight: var(--fw-sb); }
.mnav__i .badge { margin-left: auto; }
.mnav__sep { height: 1px; margin: var(--sp-3) var(--sp-4); background: var(--c-line-2); }
.mnav__lbl { padding: 0 var(--sp-4) var(--sp-2); font-size: var(--fs-cap); color: var(--c-muted-2); }

@media (max-width: 900px) {
  /* 관리자 화면은 상단바가 통째로 관리 메뉴(.anav)이고 이미 접혀서 돈다 — 건드리지 않는다 */
  .topbar:not(.topbar--admin) .topbar__nav,
  .topbar:not(.topbar--admin) .topbar__acct > .langsw,
  .topbar:not(.topbar--admin) .topbar__acct > .btn { display: none; }
  .topbar:not(.topbar--admin) .topbar__burger { display: inline-flex; position: relative; }
  .topbar__in { gap: var(--sp-3); }
}

/* ---- 1.5 관리자 상단 메뉴 (.topbar--admin + .anav) ----
   관리자 화면은 '읽는 화면'이 아니라 '쓰는 화면'이다. 메뉴를 본문 안에 두면
   매번 스크롤해서 찾아야 하므로 로고 바로 옆, 화면 최상단에 고정한다.
   본문은 한 번에 한 패널만 보여준다.
   상단바만 wrap(1120)이고 본문이 wrap--wide(1320)면 로고와 본문 왼쪽 끝이
   어긋나 보인다 — 관리자 상단바는 본문과 같은 폭을 쓴다(마크업에서 wrap--wide 를 함께 준다). */
.anav {
  display: flex; align-items: center; gap: var(--sp-5);
  min-width: 0; overflow-x: auto;
  scrollbar-width: none;                      /* 메뉴가 넘칠 때만 밀어서 본다 */
}
.anav::-webkit-scrollbar { display: none; }

.anav__i {
  position: relative; flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--h-topbar); padding: 0 2px;
  font-size: var(--fs-sm); font-weight: var(--fw-md); color: var(--c-muted);
  white-space: nowrap; cursor: pointer;
  transition: color var(--t-fast) var(--ease);
}
.anav__i:hover { color: var(--c-ink); }
/* ⚠️ .anav 는 overflow-x:auto 라 overflow-y 도 auto 가 된다(둘 중 하나만 visible 일 수 없다).
   그래서 바깥으로 나가는 장식은 잘린다 — 밑줄은 상자 안(bottom:0)에 두고,
   포커스는 전역 링(outline-offset:2px, 바깥)을 **안쪽으로 뒤집어** 쓴다.
   포커스 표시를 없애는 것이 아니라, 잘리지 않는 형태로 바꾼 것이다. */
.anav__i[aria-current="page"] { color: var(--c-primary); font-weight: var(--fw-sb); }
.anav__i[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--c-primary); border-radius: 2px 2px 0 0;
}
.anav__i:focus-visible {
  outline: 2px solid var(--c-focus); outline-offset: -3px; border-radius: var(--r-xs);
}
/* 처리할 일 개수. 0이면 아예 렌더하지 않는다 — '0' 은 읽는 사람을 멈춰 세운다.
   숫자이므로 마크업에 .t-num 을 함께 붙인다(디자인 절대 규칙 ③). */
.anav__n {
  min-width: 18px; height: 18px; padding: 0 var(--sp-1); border-radius: var(--r-full);
  background: var(--c-danger); color: #fff;
  font-size: var(--fs-cap); font-weight: var(--fw-bd); line-height: 18px;
}

/* 한 줄에 안 들어가면 메뉴를 둘째 줄로 내린다.
   분기점 1260px 은 실측값이다 — 플랫폼 총관리자 기준 한 줄 최소폭:
     메뉴 8개 601px(심사 대기 배지가 뜨면 625px) + 로고 102 + 풀장 선택기 173
     + 계정 블록 214 + topbar__in gap 4×24=96 + .wrap 좌우 여백 40  ≈ 1,250px.
   ⚠️ 예전에 1100px 로 잡았더니 1,101~1,245px 구간(예: 1152×864, 최대화하지 않은 창)에서
      '설정' 이 통째로 잘렸다. 스크롤바를 숨겨 놨으므로 **잘렸다는 사실 자체가 안 보인다.**
      메뉴 라벨을 늘리면 이 임계도 같이 오르니 그때 다시 재라.
   .topbar__nav 처럼 hide-sm 으로 숨기는 선택지는 없다 — 관리자의 유일한 내비다. */
@media (max-width: 1260px) {
  .topbar--admin { height: auto; }
  .topbar--admin .topbar__in {
    flex-wrap: wrap; height: auto;
    padding-block: var(--sp-3); row-gap: var(--sp-2); column-gap: var(--sp-4);
  }
  .topbar--admin .anav {
    order: 9; width: 100%; gap: var(--sp-4);
    border-top: 1px solid var(--c-line); padding-top: var(--sp-2);
  }
  /* ⚠️ --btn-h 는 .btn 안에서만 선언된 지역 변수라 여기서는 무효다. 값을 직접 쓴다.
     44px 은 터치 타깃 최소치(00-principles.md)이고 4px 그리드 밖이라 대체 토큰이 없다. */
  .topbar--admin .anav__i { height: 44px; }
}

/* 폰 — 첫 줄(로고·풀장·계정)이 또 접히면 상단바가 3줄이 되어 화면을 다 먹는다.
   관리 화면에서 없어도 되는 것부터 덜어 2줄로 유지한다:
   역할 배지(관리자 화면에 있다는 사실이 이미 역할을 말한다)와 로고 글자. */
@media (max-width: 720px) {
  .topbar--admin .topbar__nav,
  .topbar--admin .logo__txt,
  .topbar--admin .badge { display: none; }
  /* .spacer 는 남은 공간을 전부 먹어 계정 블록을 셋째 줄로 밀어낸다 */
  .topbar--admin .spacer { display: none; }
  .topbar--admin .select { max-width: 34vw; }
}

.logo { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; }
.logo:hover { text-decoration: none; }
.logo__mark {
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--grad-accent);
  display: grid; place-items: center; flex: none;
  box-shadow: 0 2px 8px rgba(36, 92, 143, .35);
}
.logo__mark svg { width: 15px; height: 15px; }
.logo__txt {
  font-size: 16.5px; font-weight: var(--fw-bd); letter-spacing: -.03em; color: var(--c-ink);
}
.logo__txt em { font-style: normal; color: var(--c-primary); }

.footer {
  background: var(--c-deep-1); color: var(--c-on-deep-2);
  padding-block: var(--sp-12) var(--sp-8); font-size: var(--fs-sm);
}
.footer a { color: var(--c-on-deep); }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: var(--sp-8); }
@media (max-width: 720px) { .footer__grid { grid-template-columns: minmax(0, 1fr); } }

/* ============ 2. 버튼 ============ */
.btn {
  --btn-h: 44px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: var(--btn-h); padding-inline: var(--sp-5);
  border-radius: var(--r-sm);
  font-size: var(--fs-body); font-weight: var(--fw-sb); letter-spacing: var(--ls-normal);
  white-space: nowrap; text-decoration: none;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
  border: 1px solid transparent;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn.is-off { opacity: .45; pointer-events: none; }

.btn--primary { background: var(--c-primary); color: #fff; box-shadow: 0 2px 10px rgba(36, 92, 143, .28); }
.btn--primary:hover { background: var(--c-primary-ink); }
/* 다크 서피스에서는 --c-primary 가 밝은 청록으로 바뀌므로 글자를 어둡게 뒤집는다.
   (밝은 청록 + 흰 글자는 대비 1.9:1 로 읽히지 않는다) */
.on-deep .btn--primary { background: var(--c-accent); color: var(--c-deep-1); box-shadow: 0 2px 14px rgba(94, 155, 209, .3); }
.on-deep .btn--primary:hover { background: var(--c-on-deep-3); }
.btn--line { background: var(--c-bg); color: var(--c-ink); border-color: var(--c-line); }
.btn--line:hover { border-color: var(--c-muted-2); background: var(--c-bg-soft); }
.btn--soft { background: var(--c-primary-soft); color: var(--c-primary-ink); }
.btn--soft:hover { background: var(--c-primary-soft-2); }
.btn--ghost { background: transparent; color: var(--c-muted); }
.btn--ghost:hover { background: var(--c-bg-soft); color: var(--c-ink); }
.btn--danger { background: var(--c-danger); color: #fff; }
.btn--danger:hover { filter: brightness(.94); }
.btn--dangerline { background: var(--c-bg); color: var(--c-danger); border-color: #EBC7C3; }
.btn--dangerline:hover { background: var(--c-danger-soft); }

.btn--lg { --btn-h: 54px; padding-inline: var(--sp-8); font-size: 16px; border-radius: var(--r-md); }
.btn--sm { --btn-h: 34px; padding-inline: var(--sp-3); font-size: var(--fs-sm); border-radius: var(--r-xs); }
.btn--block { display: flex; width: 100%; }
.btn--icon { width: var(--btn-h); padding-inline: 0; }

/* ============ 3. 폼 ============ */
.field { display: block; }
.field__label {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-ink-2);
  margin-bottom: 7px;
}
.field__label .req { color: var(--c-danger); font-weight: var(--fw-bd); }
.field + .field { margin-top: var(--sp-5); }

.input, .select, .textarea {
  width: 100%; height: 46px; padding: 0 14px;
  background: var(--c-bg);
  border: 1px solid var(--c-line); border-radius: var(--r-sm);
  color: var(--c-ink);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.textarea { height: auto; min-height: 108px; padding: 12px 14px; line-height: 1.62; resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--c-muted-2); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--c-line-3); }
/* ⚠️ 여기에 outline:none 을 두지 않는다. (0,2,0) 이라 전역 :focus-visible (0,1,0) 을 이겨서
   **폼 필드에서만 포커스 링이 사라졌다** — 회원가입·명단 입력처럼 필드만 있는 화면은
   키보드로 어디에 있는지 전혀 알 수 없게 된다.
   테두리 색 + 안쪽 글로우(--sh-focus)는 마우스 클릭에도 반응하는 '지금 입력 중' 표시이고,
   포커스 링은 그 바깥에 별도로 그려진다. 둘은 역할이 다르므로 함께 둔다. */
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--c-primary); box-shadow: var(--sh-focus);
}
.input[readonly], .input[disabled] { background: var(--c-bg-sink); color: var(--c-muted); }
.input.is-bad, .select.is-bad { border-color: var(--c-danger); }

.select {
  appearance: none; padding-right: 38px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%235C7681' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}

.help { margin-top: 6px; font-size: var(--fs-cap); color: var(--c-muted); }
.help--bad { color: var(--c-danger); }

.check { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }
.check input { flex: none; width: 19px; height: 19px; margin: 1px 0 0; accent-color: var(--c-primary); cursor: pointer; }
.check__txt { font-size: var(--fs-sm); color: var(--c-ink-2); }

/* 선택 칩 — 종목 · 인원 · 역할 */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 38px; padding-inline: 14px;
  border: 1px solid var(--c-line); border-radius: var(--r-full);
  background: var(--c-bg); color: var(--c-ink-2);
  font-size: var(--fs-sm); font-weight: var(--fw-md);
  /* ⚠️ transition:all 이 아니다 — all 은 **outline 까지** 애니메이션한다.
     그러면 Tab 을 옮길 때마다 포커스 링이 120ms 동안 번져 들어왔다 나간다(2026-08-22 실측).
     예약 흐름의 컨트롤은 키보드로 가장 빨리 훑는 곳이라 잔상이 그대로 보인다. */
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.chip:hover { border-color: var(--c-muted-2); }
.chip.is-on {
  background: var(--c-primary); border-color: var(--c-primary); color: #fff;
  font-weight: var(--fw-sb); box-shadow: 0 2px 8px rgba(36, 92, 143, .25);
}

/* 선택 카드 — 회원 유형 · 결제수단 */
.pick { display: grid; gap: var(--sp-3); }
.pick__item {
  position: relative; display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-4) var(--sp-5);
  border: 1.5px solid var(--c-line); border-radius: var(--r-md);
  background: var(--c-bg); cursor: pointer; text-align: left;
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease);
}
.pick__item:hover { border-color: var(--c-line-3); background: var(--c-bg-soft); }
.pick__item.is-on {
  border-color: var(--c-primary); background: var(--c-primary-soft);
  box-shadow: 0 0 0 1px var(--c-primary) inset;
}
.pick__item.is-on .pick__radio { border-color: var(--c-primary); }
.pick__item.is-on .pick__radio::after { transform: scale(1); }
.pick__radio {
  flex: none; width: 20px; height: 20px; margin-top: 2px;
  border: 2px solid var(--c-line); border-radius: 50%;
  display: grid; place-items: center;
  transition: border-color var(--t-fast) var(--ease);
}
.pick__radio::after {
  content: ""; width: 10px; height: 10px; border-radius: 50%;
  background: var(--c-primary); transform: scale(0);
  transition: transform var(--t-base) var(--ease-out);
}
.pick__ico {
  flex: none; width: 42px; height: 42px; border-radius: var(--r-sm);
  background: var(--c-primary-soft); color: var(--c-primary);
  display: grid; place-items: center; font-size: 20px;
}
/* 사진 썸네일형 선택 카드 (예약 종목) */
.pick__thumb {
  flex: none; width: 60px; height: 46px; border-radius: var(--r-xs); overflow: hidden;
  background: var(--grad-deep); display: grid; place-items: center;
}
.pick__thumb img { width: 100%; height: 100%; object-fit: cover; }

/* 파일 업로더 — 자격증 이미지 */
.uploader {
  border: 1.5px dashed var(--c-line); border-radius: var(--r-md);
  background: var(--c-bg-soft);
  padding: var(--sp-8) var(--sp-5); text-align: center;
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease); cursor: pointer;
}
.uploader:hover { border-color: var(--c-primary); background: var(--c-primary-soft); }
.uploader__ico {
  width: 48px; height: 48px; margin: 0 auto var(--sp-3);
  border-radius: var(--r-sm); background: #fff; color: var(--c-primary);
  display: grid; place-items: center; box-shadow: var(--sh-2); font-size: 22px;
}
.uploader.has-file { border-style: solid; border-color: var(--c-ok); background: var(--c-ok-soft); }

.thumb {
  position: relative; border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--c-line); background: var(--c-bg-sink);
  aspect-ratio: 4 / 3;
}
.thumb img { width: 100%; height: 100%; object-fit: cover; }
.thumb__del {
  position: absolute; top: 8px; right: 8px; width: 28px; height: 28px;
  border-radius: 50%; background: rgba(12, 26, 40, .62); color: #fff;
  display: grid; place-items: center; font-size: 13px;
}

/* ============ 4. 카드 · 면 ============ */
/* 면은 '선으로 그린 상자' 가 아니라 '바닥에서 떠 있는 판' 으로 읽혀야 한다.
   경계선 + 아주 옅은 그림자 + 안쪽 위 하이라이트 한 줄. 세 개가 같이 있어야
   가장자리가 빛을 받은 것처럼 보인다 — 그림자만 키우면 탁해진다. */
.card {
  background: var(--c-bg); border: 1px solid var(--c-line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1), inset 0 1px 0 rgba(255, 255, 255, .9);
  transition: box-shadow var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.card--flat { box-shadow: none; }
.card--raised { box-shadow: var(--sh-3); border-color: transparent; }
.card--soft { background: var(--c-bg-soft); border-color: transparent; box-shadow: none; }
.card--deep { background: var(--grad-deep); border-color: transparent; color: var(--c-on-deep); }
.card__hd {
  padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--c-line-2);
  display: flex; align-items: center; gap: var(--sp-3);
}
.card__bd { padding: var(--sp-6); }
.card__bd--tight { padding: var(--sp-4) var(--sp-5); }
.card__ft {
  padding: var(--sp-4) var(--sp-6); border-top: 1px solid var(--c-line-2);
  background: var(--c-bg-soft); border-radius: 0 0 var(--r-md) var(--r-md);
}
/* 누를 수 있는 카드는 손이 닿으면 한 단 떠오른다.
   경계선도 같이 진해져야 '떠오름' 이 그림자 흐림이 아니라 상태 변화로 읽힌다. */
.card--link { transition: box-shadow var(--t-base) var(--ease), transform var(--t-base) var(--ease),
                          border-color var(--t-base) var(--ease); }
.card--link:hover { box-shadow: var(--sh-3); border-color: var(--c-line-3); transform: translateY(-2px); }

/* 안내 · 경고 블록 */
.note {
  display: flex; gap: 10px; align-items: flex-start;
  padding: var(--sp-3) var(--sp-4); border-radius: var(--r-sm);
  background: var(--c-bg-soft); color: var(--c-ink-2);
  font-size: var(--fs-sm); border-left: 3px solid var(--c-line);
}
.note--info   { background: var(--c-info-soft);   border-left-color: var(--c-info); }
.note--warn   { background: var(--c-warn-soft);   border-left-color: var(--c-warn); }
.note--danger { background: var(--c-danger-soft); border-left-color: var(--c-danger); }
.note--ok     { background: var(--c-ok-soft);     border-left-color: var(--c-ok); }
.note__ico { flex: none; font-size: 15px; line-height: 1.5; }

/* 빈 상태 */
.empty { padding: var(--sp-14) var(--sp-5); text-align: center; color: var(--c-muted); }
.empty__ico { font-size: 34px; opacity: .4; margin-bottom: var(--sp-3); }

/* ============ 5. 배지 · 상태 ============ */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  height: 24px; padding-inline: 9px; border-radius: var(--r-xs);
  font-size: var(--fs-cap); font-weight: var(--fw-sb); letter-spacing: 0;
  background: var(--c-bg-sink); color: var(--c-ink-2);   /* muted 는 이 배경에서 4.4 라 상태가 흐리게 읽힌다 */
  white-space: nowrap;
}
.badge--ok     { background: var(--c-ok-soft);     color: var(--c-ok); }
.badge--wait   { background: var(--c-info-soft);   color: var(--c-info); }
.badge--warn   { background: var(--c-warn-soft);   color: var(--c-warn); }
.badge--danger { background: var(--c-danger-soft); color: var(--c-danger); }
.badge--brand  { background: var(--c-primary-soft); color: var(--c-primary-ink); }
.badge--solid  { background: var(--c-primary); color: #fff; }
.badge--lg { height: 28px; padding-inline: 11px; font-size: var(--fs-sm); }
.badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* 진행 점 — 면책동의서 N/M */
.dots { display: inline-flex; gap: 4px; }
.dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--c-line); }
.dots i.is-on { background: var(--c-ok); }

.bar { height: 6px; border-radius: var(--r-full); background: var(--c-line-2); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: inherit; background: var(--grad-accent); transition: width var(--t-slow) var(--ease-out); }

/* ============ 6. 스텝퍼 · 탭 ============ */
.steps { display: flex; align-items: center; gap: var(--sp-2); }
.steps__i { display: flex; align-items: center; gap: 8px; color: var(--c-muted-2); font-size: var(--fs-sm); }
.steps__n {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--c-bg-sink); color: var(--c-muted);
  font-size: var(--fs-cap); font-weight: var(--fw-bd);
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-base) var(--ease), color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease);
}
.steps__i.is-on { color: var(--c-ink); font-weight: var(--fw-sb); }
.steps__i.is-on .steps__n { background: var(--c-primary); color: #fff; box-shadow: 0 0 0 4px var(--c-primary-soft); }
.steps__i.is-done .steps__n { background: var(--c-ok); color: #fff; }
.steps__i.is-done { color: var(--c-ink-2); }
.steps__line { flex: 1 1 auto; height: 1px; background: var(--c-line); min-width: 12px; }
/* 모바일에서는 라벨만 접는다. 번호 원(.steps__n)은 항상 남긴다 */
@media (max-width: 720px) {
  .steps__i > span:not(.steps__n) { display: none; }
  .steps__i.is-on > span:not(.steps__n) { display: inline; }
}

.tabs { display: flex; gap: var(--sp-5); border-bottom: 1px solid var(--c-line); }
.tab {
  position: relative; padding: 0 2px var(--sp-3);
  font-size: var(--fs-body); font-weight: var(--fw-md); color: var(--c-muted);
  transition: color var(--t-fast) var(--ease);
}
.tab:hover { color: var(--c-ink); }
.tab.is-on { color: var(--c-primary); font-weight: var(--fw-sb); }
.tab.is-on::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; background: var(--c-primary); border-radius: 2px 2px 0 0;
}
.tab__cnt {
  margin-left: 6px; padding: 1px 6px; border-radius: var(--r-full);
  background: var(--c-bg-sink); color: var(--c-muted);
  font-size: 11px; font-weight: var(--fw-bd);
}
.tab.is-on .tab__cnt { background: var(--c-primary-soft); color: var(--c-primary-ink); }

/* ============ 7. 예약 — 달력 · 시간 슬롯 · 요약 ============ */
.cal__hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-4); }
.cal__nav {
  width: 34px; height: 34px; border-radius: var(--r-xs); border: 1px solid var(--c-line);
  display: grid; place-items: center; color: var(--c-ink-2);
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.cal__nav:hover { background: var(--c-bg-soft); border-color: var(--c-muted-2); }
.cal__dow, .cal__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal__dow { margin-bottom: 8px; }
.cal__dow span { text-align: center; font-size: var(--fs-cap); font-weight: var(--fw-sb); color: var(--c-muted); }
.cal__dow span:first-child { color: var(--c-danger); }
.cal__dow span:last-child { color: var(--c-info); }
.cal__d {
  /* 넓은 화면에서 셀이 과하게 커지지 않도록 정사각 비율에 상한을 둔다 */
  position: relative; aspect-ratio: 1 / 1; max-height: 54px; min-height: 42px;
  border-radius: var(--r-sm);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-size: var(--fs-sm); font-weight: var(--fw-md); color: var(--c-ink-2);
  border: 1px solid transparent;
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.cal__d:hover:not(.is-off) { background: var(--c-primary-soft); }
.cal__d.is-off { color: var(--c-muted-2); opacity: .5; pointer-events: none; text-decoration: line-through; }
.cal__d.is-blank { pointer-events: none; }
.cal__d.is-today { border-color: var(--c-primary); }
.cal__d.is-on { background: var(--c-primary); color: #fff; font-weight: var(--fw-bd); box-shadow: 0 4px 12px rgba(36, 92, 143, .3); }
.cal__d small { font-size: 10px; font-weight: var(--fw-md); color: var(--c-muted); line-height: 1; }
.cal__d.is-on small { color: rgba(255, 255, 255, .85); }
.cal__d.is-full { background: var(--c-bg-sink); color: var(--c-muted-2); pointer-events: none; }

.slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: var(--sp-2); }
.slot {
  position: relative;
  height: 62px; border-radius: var(--r-sm); border: 1px solid var(--c-line);
  background: var(--c-bg); display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
/* 회차 부가 표기 (예: 선수 훈련 병행) */
.slot__note {
  position: absolute; top: -7px; left: 50%; transform: translateX(-50%);
  padding: 1px 7px; border-radius: var(--r-full); white-space: nowrap;
  background: var(--c-warn-soft); color: var(--c-warn);
  font-size: 10px; font-weight: var(--fw-sb); font-style: normal;
}
.slot.is-on .slot__note { background: var(--c-warn); color: #fff; }
.slot b { font-size: 15px; font-weight: var(--fw-sb); letter-spacing: -.01em; }
.slot small { font-size: 11px; color: var(--c-muted); }
.slot:hover:not(.is-full) { border-color: var(--c-primary); background: var(--c-primary-soft); }
.slot.is-on { background: var(--c-primary); border-color: var(--c-primary); color: #fff; box-shadow: 0 4px 12px rgba(36, 92, 143, .26); }
.slot.is-on small { color: rgba(255, 255, 255, .82); }
.slot.is-full { background: var(--c-bg-sink); color: var(--c-muted-2); pointer-events: none; }

/* 인원 스테퍼 */
.qty { display: inline-flex; align-items: center; gap: var(--sp-2); }
.qty__btn {
  width: 40px; height: 40px; border-radius: var(--r-sm);
  border: 1px solid var(--c-line); background: var(--c-bg);
  display: grid; place-items: center; font-size: 18px; color: var(--c-ink-2);
  /* transition:all 금지 — outline 이 같이 번진다(.chip 주석 참고) */
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.qty__btn:hover:not([disabled]) { border-color: var(--c-primary); color: var(--c-primary); }
.qty__btn[disabled] { opacity: .35; pointer-events: none; }
.qty__n { min-width: 56px; text-align: center; font-size: 19px; font-weight: var(--fw-bd); font-variant-numeric: tabular-nums; }

/* 명단 입력 행 */
.roster { display: grid; gap: var(--sp-2); }
.roster__row { display: flex; align-items: center; gap: var(--sp-2); }
.roster__no {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  background: var(--c-bg-sink); color: var(--c-muted);
  display: grid; place-items: center; font-size: var(--fs-cap); font-weight: var(--fw-bd);
}
.roster__row.is-me .roster__no { background: var(--c-primary); color: #fff; }

/* 금액 요약 */
.sum { font-size: var(--fs-sm); }
.sum__row { display: flex; justify-content: space-between; gap: var(--sp-4); padding-block: 7px; }
.sum__row span { color: var(--c-muted); }
.sum__row b { font-weight: var(--fw-md); color: var(--c-ink-2); font-variant-numeric: tabular-nums; }
.sum__total {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--c-line);
}
.sum__total span { font-size: var(--fs-body); font-weight: var(--fw-sb); }
.sum__total b { font-size: 24px; font-weight: var(--fw-bd); color: var(--c-primary); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }

/* 라벨-값 표 */
.info { display: grid; gap: 0; }
.info__r { display: flex; gap: var(--sp-4); padding-block: 11px; border-bottom: 1px solid var(--c-line-2); font-size: var(--fs-sm); }
.info__r:last-child { border-bottom: 0; }
.info__r > dt { flex: none; width: 92px; color: var(--c-muted); }
.info__r > dd { flex: 1 1 auto; margin: 0; color: var(--c-ink); font-weight: var(--fw-md); }

/* ============ 8. 면책동의서 · 서명 ============ */
.doc {
  max-height: 300px; overflow-y: auto;
  padding: var(--sp-5); border: 1px solid var(--c-line); border-radius: var(--r-sm);
  background: var(--c-bg-soft); font-size: var(--fs-sm); line-height: 1.78; color: var(--c-ink-2);
}
.doc h4 { font-size: var(--fs-sm); font-weight: var(--fw-bd); color: var(--c-ink); margin-top: var(--sp-4); }
.doc h4:first-child { margin-top: 0; }
.doc p + p { margin-top: 8px; }
.doc ol { list-style: decimal; padding-left: 18px; }
.doc ol li + li { margin-top: 5px; }
.doc strong { color: var(--c-danger); font-weight: var(--fw-sb); }

.sigpad {
  position: relative; border: 1.5px dashed var(--c-line); border-radius: var(--r-md);
  background: var(--c-bg); overflow: hidden;
}
.sigpad canvas { display: block; width: 100%; height: 190px; touch-action: none; cursor: crosshair; }
.sigpad__ph {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--c-muted-2); font-size: var(--fs-sm); pointer-events: none;
}
.sigpad__line {
  position: absolute; left: var(--sp-6); right: var(--sp-6); bottom: 42px;
  border-bottom: 1px solid var(--c-line); pointer-events: none;
}
.sigpad.has-ink { border-style: solid; border-color: var(--c-primary); }
.sigpad.has-ink .sigpad__ph { display: none; }

/* 동행자 서명 현황 */
.people { display: grid; gap: var(--sp-2); }
.person {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border: 1px solid var(--c-line); border-radius: var(--r-sm);
  background: var(--c-bg);
}
.person__av {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--c-bg-sink); color: var(--c-muted);
  display: grid; place-items: center; font-size: var(--fs-sm); font-weight: var(--fw-bd);
}
.person.is-done { border-color: #CDE7DC; background: #F6FBF8; }
.person.is-done .person__av { background: var(--c-ok); color: #fff; }
.person.is-me .person__av { background: var(--c-primary); color: #fff; }

/* ============ 9. 예약 완료 카드(티켓) ============ */
.ticket {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  background: var(--grad-deep); color: var(--c-on-deep); box-shadow: var(--sh-4);
}
.ticket::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(120% 80% at 90% -10%, rgba(94, 155, 209, .35) 0%, transparent 55%);
  pointer-events: none;
}
.ticket__hd { position: relative; padding: var(--sp-6) var(--sp-6) var(--sp-5); }
.ticket__body { position: relative; padding: 0 var(--sp-6) var(--sp-5); }
.ticket__rip {
  position: relative; height: 22px; margin-block: var(--sp-2);
  display: flex; align-items: center;
}
.ticket__rip::before, .ticket__rip::after {
  content: ""; position: absolute; width: 22px; height: 22px; border-radius: 50%;
  background: var(--c-bg-soft);
}
.ticket__rip::before { left: -11px; }
.ticket__rip::after { right: -11px; }
.ticket__rip i {
  flex: 1 1 auto; margin-inline: 16px;
  border-top: 1.5px dashed rgba(255, 255, 255, .3);
}
.ticket__code {
  position: relative; background: #fff; color: var(--c-ink);
  margin: 0 var(--sp-5) var(--sp-5); padding: var(--sp-5);
  border-radius: var(--r-md); text-align: center;
}
.qr { width: 168px; height: 168px; margin: 0 auto; }
.qr rect { shape-rendering: crispEdges; }
.barcode { display: flex; align-items: flex-end; gap: 1.5px; height: 46px; margin-top: var(--sp-4); justify-content: center; }
.barcode i { width: 2px; background: var(--c-ink); border-radius: .5px; }
.barcode i.w { width: 4px; }
.ticket__no {
  margin-top: 10px; font-size: var(--fs-sm); font-weight: var(--fw-bd);
  letter-spacing: .22em; font-variant-numeric: tabular-nums; color: var(--c-ink-2);
}
.ticket__meta { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-4) var(--sp-5); }
.ticket__meta dt { font-size: 11px; color: var(--c-on-deep-2); letter-spacing: .06em; margin-bottom: 3px; }
.ticket__meta dd { margin: 0; font-size: 15px; font-weight: var(--fw-sb); color: #fff; }

/* QR 수명 표시 — 남은 시간을 줄어드는 바로 보여준다 */
.qr-life {
  height: 3px; margin-top: 10px; border-radius: var(--r-full);
  background: var(--c-line-2); overflow: hidden;
}
.qr-life > i {
  display: block; height: 100%; width: 100%;
  background: var(--grad-accent); border-radius: inherit;
  transition: width 1s linear;
}

/* 입장권 캡처 억제.
   ⚠️ OS 스크린샷은 웹에서 막을 수 없다. 길게눌러 저장·드래그·선택만 차단하고,
      실제 방어는 짧은 수명의 회전 QR 이 담당한다. 문서: docs/01-api.md */
.ticket, .ticket * {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
.ticket img, .ticket svg { pointer-events: none; }

/* ============ 10. 표 (관리자) ============ */
.tbl { font-size: var(--fs-sm); }
.tbl th {
  text-align: left; padding: 11px var(--sp-4);
  background: var(--c-bg-soft); color: var(--c-muted);
  font-size: var(--fs-cap); font-weight: var(--fw-sb); letter-spacing: .02em;
  border-bottom: 1px solid var(--c-line); white-space: nowrap;
}
.tbl th:first-child { border-radius: var(--r-xs) 0 0 0; }
.tbl th:last-child { border-radius: 0 var(--r-xs) 0 0; }
.tbl td { padding: 13px var(--sp-4); border-bottom: 1px solid var(--c-line-2); vertical-align: middle; }
.tbl tbody tr { transition: background var(--t-fast) var(--ease); }
.tbl tbody tr:hover { background: var(--c-bg-soft); cursor: pointer; }
.tbl tbody tr.is-on { background: var(--c-primary-soft); }
.tbl__u { display: flex; align-items: center; gap: 10px; }

/* 행 전체가 눌리는 표(회원·자격 심사)의 **키보드 경로**.
   <tr onclick> 만 두면 초점이 오지 않아 키보드로는 상세를 열 길이 없다 —
   이름 칸을 진짜 <button> 으로 만들고, 보기에는 그냥 칸처럼 둔다.
   행 클릭은 마우스 편의로 남아 있다. */
.tbl__open {
  display: block; width: 100%; text-align: left;
  font: inherit; color: inherit; letter-spacing: inherit;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.tbl__av {
  width: 32px; height: 32px; border-radius: 50%; flex: none;
  background: var(--grad-accent); color: #fff;
  display: grid; place-items: center; font-size: var(--fs-cap); font-weight: var(--fw-bd);
}
.tbl-scroll { overflow-x: auto; }

/* 스크롤 상자 안의 요소로 Tab 이 들어오면 브라우저가 그 요소를 가장자리에 **딱 맞춰**
   스크롤한다 — 그러면 바깥 5px 에 그려지는 포커스 링이 잘린다.
   scroll-padding 으로 6px 를 남겨 링이 들어갈 자리를 만든다.
   (.anav 는 세로로도 좁아 이 방법이 안 통해서 링 자체를 안쪽으로 뒤집었다 — §1.5) */
.mnav__bd, .tbl-scroll, .drawer__bd, .modal__box, .doc, .pmap__pd { scroll-padding: 6px; }
.tbl-scroll > table { min-width: 720px; }

/* 통계 타일 */
.stat { padding: var(--sp-5); border: 1px solid var(--c-line); border-radius: var(--r-md); background: var(--c-bg); }
.stat__k { font-size: var(--fs-cap); color: var(--c-muted); font-weight: var(--fw-md); }
.stat__v { margin-top: 6px; font-size: 28px; font-weight: var(--fw-bd); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.stat__d { margin-top: 4px; font-size: var(--fs-cap); }

/* ---- 본문 바로가기(skip link) ----
   상단바 메뉴가 9개인 관리자 화면은 매 화면 Tab 13번을 눌러야 본문에 닿았다.
   ⚠️ display:none 이나 visibility:hidden 으로 숨기면 **초점 자체를 못 받아** 무용지물이다.
   화면 위로 밀어 두고 초점이 오면 제자리로 데려온다.
   흰 알약인 이유는 어디 위에 뜰지 모르기 때문이다 — 개발 중에는 딥색 데모바 위,
   배포본에서는 밝은 상단바 위에 온다. 포커스 링도 그래서 안쪽으로 넣는다(면 위 9.86:1). */
.skip {
  position: fixed; top: var(--sp-2); left: var(--sp-2); z-index: calc(var(--z-topbar) + 1);
  transform: translateY(calc(-100% - var(--sp-4)));
  padding: var(--sp-3) var(--sp-5); border-radius: var(--r-sm);
  background: var(--c-bg); color: var(--c-primary-ink);
  border: 1px solid var(--c-line-3); box-shadow: var(--sh-3);
  font-size: var(--fs-sm); font-weight: var(--fw-sb);
  transition: transform var(--t-fast) var(--ease);
}
.skip:focus-visible { transform: none; outline-offset: -4px; }
.skip:hover { text-decoration: none; }

/* ============ 11. 오버레이 — 드로어 · 모달 · 토스트 ============ */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-drawer);
  background: rgba(12, 26, 40, .48); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: opacity var(--t-base) var(--ease);
}
.scrim.is-open { opacity: 1; pointer-events: auto; }

/* ⚠️ 닫힘 상태는 화면 밖(translateX)일 뿐 **탭 순서에는 그대로 남는다.**
   transform 도 opacity 도 요소를 순차 포커스에서 빼지 않는다 — visibility 만 뺀다.
   그대로 두면 심사 드로어를 닫은 뒤에도 화면 오른쪽 밖의 '승인'·'반려' 버튼에
   Tab 이 닿고, 아무것도 안 보이는 자리에서 Enter 를 누르면 자격증이 실제로 승인된다.
   .mnav 가 같은 이유로 이미 visibility 를 쓰고 있다(§1.4).
   전환에 `visibility 0s linear var(--t-slow)` 를 얹어야 나가는 애니메이션이 끊기지 않는다 —
   닫을 때는 미끄러짐이 끝난 뒤 숨고, 열 때는 .is-open 의 transition-delay:0 으로 즉시 보인다. */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: var(--z-drawer);
  width: min(520px, 100%); background: var(--c-bg);
  box-shadow: var(--sh-4); display: flex; flex-direction: column;
  transform: translateX(100%); visibility: hidden;
  transition: transform var(--t-slow) var(--ease-out), visibility 0s linear var(--t-slow);
}
.drawer.is-open { transform: none; visibility: visible; transition-delay: 0s; }
.drawer__hd { padding: var(--sp-5) var(--sp-6); border-bottom: 1px solid var(--c-line); display: flex; align-items: center; gap: var(--sp-3); }
.drawer__bd { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-6); }
.drawer__ft { padding: var(--sp-4) var(--sp-6); border-top: 1px solid var(--c-line); background: var(--c-bg-soft); display: flex; gap: var(--sp-2); }

/* ⚠️ .drawer 와 같은 이유로 visibility 가 필요하다. opacity:0 + pointer-events:none 은
   마우스만 막을 뿐 탭 순서는 그대로다 — 마이페이지에서는 닫힌 모달 4개 안의 입력·버튼
   22개가 보이지 않는 탭 스톱으로 남아, 본문을 다 지난 사용자가 아무것도 보이지 않는
   구간을 22번 지나야 했다(2026-08-22 실측: 닫힌 #popupModal 의 자손 2개가 실제로
   focus() 를 받았고, 같은 조건의 .mnav 는 9개 중 0개였다). */
.modal {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: grid; place-items: center; padding: var(--gutter);
  opacity: 0; pointer-events: none; visibility: hidden;
  transition: opacity var(--t-base) var(--ease), visibility 0s linear var(--t-base);
}
.modal.is-open { opacity: 1; pointer-events: auto; visibility: visible; transition-delay: 0s; }
.modal__bg { position: absolute; inset: 0; background: rgba(12, 26, 40, .5); }
.modal__box {
  position: relative; width: min(440px, 100%); background: var(--c-bg);
  border-radius: var(--r-lg); box-shadow: var(--sh-4); padding: var(--sp-8) var(--sp-6) var(--sp-6);
  transform: translateY(10px) scale(.98); transition: transform var(--t-slow) var(--ease-out);
  /* 입력이 많은 모달은 작은 화면에서 화면보다 길어진다. 스크롤을 안 주면
     제출 버튼이 화면 밖으로 나가 손이 닿지 않는다 (375×667 에서 실제로 그랬다). */
  max-height: calc(100dvh - var(--gutter) * 2);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.modal.is-open .modal__box { transform: none; }

.toasts { position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%); z-index: var(--z-toast); display: grid; gap: 8px; }
.toast {
  display: flex; align-items: center; gap: 9px;
  padding: 12px var(--sp-5); border-radius: var(--r-full);
  background: rgba(12, 26, 40, .93); color: #fff; font-size: var(--fs-sm); font-weight: var(--fw-md);
  box-shadow: var(--sh-4); animation: toast-in var(--t-slow) var(--ease-out);
}
@keyframes toast-in { from { opacity: 0; transform: translateY(12px); } }

/* ============ 12. 히어로 · 장식 ============ */
.hero { position: relative; background: var(--grad-deep); color: var(--c-on-deep); overflow: hidden; }
.hero__wave { position: absolute; left: 0; right: 0; bottom: -1px; pointer-events: none; }
.hero__glow {
  position: absolute; width: 620px; height: 620px; border-radius: 50%;
  background: radial-gradient(circle, rgba(94, 155, 209, .3) 0%, transparent 62%);
  top: -220px; right: -160px; pointer-events: none;
}
.hero__in { position: relative; padding-block: var(--sp-24) var(--sp-18); }
@media (max-width: 720px) { .hero__in { padding-block: var(--sp-14) var(--sp-12); } }

/* ---- 12.1 요금표 — 풀장 홈 '이용안내 · 요금' ----
   ⚠️ 예전 .price-card 는 position/overflow 두 줄뿐이었다. 배경·테두리·패딩이 없어
   section--soft 위에서 카드가 아니라 글 덩어리로 흘렀다:
   "스쿠버다이빙 15,000원 프리다이빙 20,000원 …".
   종목마다 면을 세우면 네 면이 서로 경쟁해 비교가 안 된다. 답이 하나(요금)면
   면도 하나여야 한다 — 한 판 위에 실선으로 나눈 칸으로 둔다. */
.price-table {
  display: grid;
  /* 종목 수는 풀장마다 다르다(블루레인 2 · 아쿠아라인 4). auto-fit 이면 몇 개든
     줄바꿈으로 받는다. 한 줄 고정 + 가로 스크롤은 넘친 종목이 힌트 없이 사라져
     '그 종목이 없는' 것처럼 읽힌다. 200px 하한은 배지가 안 잘리는 최소폭. */
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.price-card { display: flex; flex-direction: column; padding: var(--sp-5) var(--sp-6); }
.price-card + .price-card { border-inline-start: 1px solid var(--c-line-2); }

/* 이름은 '찾는 열쇠', 금액은 '답'. 이름을 낮춰야 숫자가 주인공이 된다 */
.price-card__k { font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-muted); }

/* ⚠️ 예전 .price-card__tag 는 우상단 절대배치 + background:var(--c-primary);color:#fff 였다.
   .badge--warn 과 특정도가 같은데 뒤에 있어 경고색을 통째로 덮었다 — 화면의
   '가정값 · 확인 필요' 가 브랜드색 흰 글씨 리본, 즉 '추천!' 같은 홍보 라벨로 렌더됐다.
   마크업은 규칙 ④를 지키는데 시각적으로는 배신하고 있었다. 색·배치를 걷어내면
   .badge--warn 이 제 색을 낸다. */
/* 배지는 금액 바로 위에 붙되, 금액을 밀어내지 않는다(아래 정렬 주석 참고) */
.price-card__tag { align-self: start; margin-top: auto; margin-bottom: var(--sp-2); }

/* 금액은 칸 바닥에 정렬한다 — 이름이 두 줄로 접히거나 배지가 있는 칸만 금액이
   내려앉으면 가로 비교가 끊긴다. 이 컴포넌트의 존재 이유가 그 비교다. */
.price-card__v {
  margin-top: auto;
  font-size: var(--fs-h1); line-height: var(--lh-h1);
  font-weight: var(--fw-bd); letter-spacing: var(--ls-tight);
  color: var(--c-ink); font-variant-numeric: tabular-nums;
}
/* ⚠️ 금액에 padding-top 을 주면 배지 있는 칸과 없는 칸의 금액이 어긋난다(실측 8px).
   여백은 위 요소(이름·배지)의 margin-bottom 으로만 준다 — 그래야 두 경우 모두
   금액 상자의 위가 곧 글자의 위라 바닥 정렬이 그대로 글자 정렬이 된다.
   auto 마진이 둘이면 남는 공간을 나눠 가지므로 배지 뒤 금액은 auto 를 끈다. */
.price-card__k { margin-bottom: var(--sp-4); }
.price-card__tag + .price-card__v { margin-top: 0; }
.price-card__v small {
  font-size: var(--fs-sm); font-weight: var(--fw-md);
  color: var(--c-muted); letter-spacing: var(--ls-normal);
}
.pricing__note { max-width: 68ch; }

@media (max-width: 900px) {
  /* 휴대폰에서는 칸이 아니라 줄이다 — 이름 왼쪽 / 금액 오른쪽.
     금액이 오른쪽에 모이면 tabular-nums 로 자릿수가 세로로 맞아 비교가 끝난다. */
  .price-table { grid-template-columns: minmax(0, 1fr); }
  .price-card {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline; row-gap: var(--sp-2); column-gap: var(--sp-4);
    padding: var(--sp-4) var(--sp-5);
  }
  .price-card + .price-card { border-inline-start: 0; border-top: 1px solid var(--c-line-2); }
  .price-card__v   { grid-column: 2; grid-row: 1; margin-top: 0; padding-top: 0; text-align: right; }
  .price-card__tag { grid-column: 2; grid-row: 2; margin-top: 0; justify-self: end; }
}

/* ---- 섹션 위계: 요금(뜬 면) → 운영시간(구조) → 수칙(글) → 예약 ----
   아래로 갈수록 작아지되 **흐려지지 않는다**. 수칙에 취소 규정이 들어 있어
   대비를 낮추면 '안전·금액 정보는 보여준다'(00-principles)를 어긴다. */
.pricing__ttl   { margin-top: var(--sp-2); }
.pricing__table { margin-top: var(--sp-6); }
.pricing__tail {
  margin-top: var(--sp-10);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
  gap: var(--sp-8); align-items: start;
}
/* 720px — .grid-2(560px)를 안 쓰는 이유다. 561~760px 에서 열이 좁아지면
   .info 의 dt(92px) + dd 가 무너진다. */
@media (max-width: 720px) { .pricing__tail { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); } }
.pricing__box > * + * { margin-top: var(--sp-3); }
.pricing__lbl  { font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-ink); }
.pricing__lead { max-width: 56ch; color: var(--c-ink-2); }
/* .info 의 구분선(--c-line-2)은 section--soft 위에서 사라진다 */
.pricing__box .info__r { border-bottom-color: var(--c-line); }
.pricing__box .info__r > dt { width: auto; min-width: 92px; }   /* '주말·공휴일' 이 92px 를 넘는다 */
/* base 리셋이 list-style:none 이라 지금 수칙 목록은 들여쓰기만 있고 점이 없다 */
.pricing__rules { max-width: 56ch; color: var(--c-ink-2); list-style: disc; padding-inline-start: var(--sp-5); }
.pricing__rules li + li { margin-top: var(--sp-2); }
.pricing__rules li::marker { color: var(--c-muted-2); }
.pricing__cta { margin-top: var(--sp-10); }
@media (max-width: 560px) { .pricing__cta .btn { display: flex; width: 100%; } }

/* ============ 13. 사진 ============ */
/* 원본이 최대 966px 이라 확대 배치를 피한다. 문서: 디자인/05-brand.md §3 */
.media {
  position: relative; overflow: hidden;
  border-radius: var(--r-md); background: var(--c-bg-sink);
}
.media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media--16x9 { aspect-ratio: 16 / 9; }
.media--4x3  { aspect-ratio: 4 / 3; }
.media--1x1  { aspect-ratio: 1 / 1; }
.media--3x4  { aspect-ratio: 3 / 4; }
.media--tall { aspect-ratio: 4 / 5; }
/* 사진 위에 글자를 올릴 때는 반드시 이 그라디언트를 깐다 */
.media__veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(9, 22, 37, 0) 30%, rgba(9, 22, 37, .82) 100%);
}
.media__cap {
  position: absolute; left: var(--sp-4); right: var(--sp-4); bottom: var(--sp-4);
  color: #fff;
}
.media__tag {
  position: absolute; top: var(--sp-3); left: var(--sp-3);
  background: rgba(9, 22, 37, .66); color: #fff;
  padding: 4px 10px; border-radius: var(--r-full);
  font-size: var(--fs-cap); font-weight: var(--fw-sb);
  backdrop-filter: blur(6px);
}
.credit { font-size: 11px; color: var(--c-muted-2); }

/* 사진 히어로 */
.hero--photo { background: var(--c-deep-1); }
.hero__bg { position: absolute; inset: 0; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; opacity: .55; }
.hero__bg::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(105deg, rgba(9, 22, 37, .94) 0%, rgba(9, 22, 37, .72) 46%, rgba(9, 22, 37, .3) 100%),
    linear-gradient(180deg, rgba(9, 22, 37, .5) 0%, transparent 30%, rgba(9, 22, 37, .55) 100%);
}

/* ---- 슬라이드 히어로 (.hero--slides) ----
   왜 가로로 미는 슬라이드가 아니라 교차 페이드인가:
   미는 방식은 전환 중 두 사진이 반씩 걸쳐 보인다. 그러면 왼쪽 타이틀 뒤의 밝기가
   매 프레임 달라져 글자가 읽히다 말다 한다. 겹쳐 놓고 투명도만 바꾸면
   .hero__bg::after 오버레이가 늘 같은 자리에 있어 전환 내내 대비가 일정하다.

   타이밍의 진실은 --hero-hold 한 곳이다. JS(heroSlides)가 이 값을 읽어 다음 장으로
   넘기고, 아래 진행 막대도 같은 값으로 찬다 — 두 군데에 7초를 적어 두면 반드시 어긋난다. */
.hero--slides { --hero-hold: 7000ms; }

/* .hero__bg img 의 opacity:.55 를 이겨야 하므로 선택자를 한 단계 더 준다 */
.hero--slides .hero__bg > img {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--t-vslow) var(--ease);
}
.hero--slides .hero__bg > img.is-on {
  opacity: .55;                       /* 단일 사진 히어로와 같은 농도를 유지한다 */
  /* 아주 느린 축소. 페이드 시간만큼 더 돌려야 다음 장으로 넘어가는 순간 멈춰 보이지 않는다 */
  animation: hero-drift calc(var(--hero-hold) + var(--t-vslow)) var(--ease-out) both;
}
@keyframes hero-drift { from { transform: scale(1.07); } to { transform: scale(1); } }

/* 진행 막대 — 몇 장 중 몇 번째인지, 언제 넘어가는지를 같이 보여준다.
   막대는 4px 지만 버튼은 44px 이다(터치 타깃). i 가 실제 요소인 이유는
   JS 가 이 요소의 animationend 로 다음 장을 넘기기 때문 — 마우스를 올려
   애니메이션이 멈추면 넘김도 같이 멈춘다. 타이머를 따로 두면 이게 안 맞는다. */
.hero__dots { position: relative; display: flex; gap: var(--sp-2); margin-top: var(--sp-8); }
.hero__dot {
  position: relative; width: 34px; height: 44px;
  padding: 0; border: 0; background: none; cursor: pointer;
  transition: width var(--t-base) var(--ease);
}
.hero__dot::before, .hero__dot i {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 4px; margin-top: -2px; border-radius: var(--r-full);
}
.hero__dot::before { background: rgba(255, 255, 255, .3); }
.hero__dot:hover::before { background: rgba(255, 255, 255, .5); }
.hero__dot i { background: var(--c-accent); transform-origin: left; transform: scaleX(0); }
.hero__dot[aria-current="true"] { width: 64px; }
.hero__dot[aria-current="true"] i { animation: hero-fill var(--hero-hold) linear forwards; }
@keyframes hero-fill { to { transform: scaleX(1); } }

/* 마우스를 올리거나 키보드 포커스가 들어오면 멈춘다. 읽는 중에 사진이 갈리면
   방해가 되고, 접근성 기준(자동 전환에는 멈출 수단이 있어야 한다)도 여기서 만족한다. */
.hero--slides.is-held .hero__bg > img.is-on,
.hero--slides.is-held .hero__dot i { animation-play-state: paused; }

/* 사진 출처. .credit 을 쓰지 않는 이유는 --c-muted-2 가 딥오션 위에서 거의 안 보여서다 */
.hero__credit {
  position: absolute; right: var(--gutter); bottom: var(--sp-3);
  font-size: 11px; color: rgba(255, 255, 255, .5);
}

@media (prefers-reduced-motion: reduce) {
  /* 애니메이션을 끄면 animationend 가 오지 않아 자동 넘김 자체가 서지 않는다.
     점은 그대로 눌러서 넘길 수 있다 — 다만 진행 막대는 채워 둬야 현재 장이 보인다. */
  .hero--slides .hero__bg > img.is-on { animation: none; }
  .hero__dot[aria-current="true"] i { animation: none; transform: scaleX(1); }
}

/* 입체 히어로 타이틀 — 시설명 전용. 물속에서 떠오르는 인상을 노린다.
   text-shadow 를 얇게 여러 겹 쌓아 압출(extrude)하고, 마지막 두 겹으로 접지 그림자와
   아쿠아 글로우를 얹는다. background-clip:text 는 쓰지 않는다 —
   글자 속이 투명해지면 아래 압출 그림자가 비쳐 보여 입체감이 도로 무너진다. */
.hero__title {
  position: relative;
  font-size: clamp(38px, 8.2vw, 88px);
  line-height: 1.04;
  font-weight: 800;
  letter-spacing: -.035em;
  color: #FFFFFF;
  text-shadow:
    0 1px 0 rgba(210, 246, 250, .78),
    0 2px 0 rgba(94, 155, 209, .52),
    0 4px 0 rgba(36, 92, 143, .40),
    0 7px 0 rgba(9, 22, 37, .34),
    0 14px 30px rgba(0, 0, 0, .46),
    0 0 52px rgba(94, 155, 209, .30);
}
.hero__title em { font-style: normal; color: var(--c-accent); }

/* 글자 뒤 수중광 — 배경 사진과 타이틀 사이의 대비를 벌려 준다 */
.hero__title::before {
  content: ""; position: absolute; z-index: -1;
  left: -6%; right: 22%; top: -18%; bottom: -22%;
  background: radial-gradient(ellipse at 30% 50%, rgba(94, 155, 209, .26) 0%, transparent 70%);
  pointer-events: none;
}

@media (max-width: 720px) {
  .hero__title { letter-spacing: -.03em; }
  /* 작은 화면에서 6겹은 뭉친다 — 압출을 절반으로 줄인다 */
  .hero__title {
    text-shadow:
      0 1px 0 rgba(210, 246, 250, .7),
      0 3px 0 rgba(36, 92, 143, .38),
      0 8px 18px rgba(0, 0, 0, .45),
      0 0 34px rgba(94, 155, 209, .26);
  }
}

/* 종목 카드 — 사진 + 요금 */
.prog {
  display: block; overflow: hidden; text-decoration: none; color: inherit;
  border-radius: var(--r-md); background: var(--c-bg);
  border: 1px solid var(--c-line);
  transition: box-shadow var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.prog:hover { text-decoration: none; box-shadow: var(--sh-3); transform: translateY(-3px); }
.prog:hover .prog__img img { transform: scale(1.04); }
/* span 으로 감싸도 비율이 먹도록 display 를 명시한다 */
.prog__img { display: block; position: relative; aspect-ratio: 4 / 3; overflow: hidden; background: var(--c-bg-sink); }
.prog__img img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow) var(--ease); }
.prog__bd { display: block; padding: var(--sp-4) var(--sp-5) var(--sp-5); }
/* 사진이 없는 종목 — 다른 종목 사진으로 채우지 않고 자리표시자를 둔다 */
.prog__ph {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  width: 100%; height: 100%; background: var(--grad-deep); color: var(--c-on-deep-2);
}
.prog__price { font-size: 21px; font-weight: var(--fw-bd); letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.prog__price small { font-size: 13px; font-weight: var(--fw-md); color: var(--c-muted); }

/* ============ 14. 계단식 수심 다이어그램 ============ */
/* 수심은 숫자 세 개로 읽으면 감이 안 온다. 옆에서 자른 그림에 사람을 하나 세워 두면
   "1.2m 는 발이 닿고 5m 는 못 닿는다" 가 한눈에 온다.
   입체감은 가짜 3D 가 아니라 **층**으로 만든다 — 원근을 주면 깊이가 왜곡돼
   오히려 읽기 어렵다. 물 그라디언트 + 1m 눈금 + 빛줄기 + 바닥 안쪽 그림자.
   렌더러는 assets/js/depthchart.js 한 곳이다. */
.depth {
  position: relative; border-radius: var(--r-md); overflow: hidden;
  background: var(--grad-deep);
  box-shadow: var(--sh-2), inset 0 1px 0 rgba(255, 255, 255, .12);
}
.depth svg { display: block; width: 100%; height: auto; }

.depth__water   { fill: url(#dcWater); }
.depth__floor   { stroke: var(--c-on-deep-3); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.depth__inner   { stroke: var(--c-deep-1); stroke-width: 14; stroke-linejoin: round; fill: none; opacity: .5; }
.depth__surface { stroke: var(--c-on-deep-3); stroke-width: 2.5; stroke-linecap: round; }
.depth__grid    { stroke: #fff; stroke-width: 1; opacity: .10; }
.depth__drop    { stroke: var(--c-on-deep-3); stroke-width: 1; stroke-dasharray: 3 4; opacity: .55; }
.depth__tick    { fill: var(--c-on-deep-2); font-size: 10.5px; letter-spacing: .04em; font-variant-numeric: tabular-nums; }
.depth__m       { fill: #fff; font-size: 20px; font-weight: var(--fw-bd); letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.depth__lb      { fill: var(--c-on-deep); font-size: 12px; font-weight: var(--fw-sb); }
.depth__nt      { fill: var(--c-on-deep-2); font-size: 11px; }
/* 사람은 크기 기준이지 주인공이 아니다 — 흰색이되 살짝 물러난다 */
.depth__person circle { fill: #fff; opacity: .78; }
.depth__person path   { stroke: #fff; stroke-width: 2.4; opacity: .78; }
.depth__board rect    { fill: var(--c-on-deep); opacity: .85; }

/* 치수 — 단면도가 말하지 못하는 값(가로·레인)을 옆에 붙인다 */
.depth__specs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }
.depth__spec {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: var(--sp-2) var(--sp-4); border-radius: var(--r-full);
  background: var(--c-bg); border: 1px solid var(--c-line);
  box-shadow: var(--sh-1);
}
.depth__spec dt { font-size: var(--fs-cap); color: var(--c-muted); }
.depth__spec dd { font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-ink); }

@media (max-width: 560px) {
  .depth__m  { font-size: 16px; }
  .depth__lb { font-size: 10.5px; }
  .depth__nt { display: none; }        /* 좁으면 설명이 서로 겹친다 — 숫자와 이름만 남긴다 */
}

/* 데모 전용 — 목업 안내 리본 */
.demobar {
  background: var(--c-deep-2); color: var(--c-on-deep); font-size: var(--fs-cap);
  padding: 7px var(--gutter); text-align: center; letter-spacing: .01em;
}
.demobar b { color: var(--c-on-deep-3); font-weight: var(--fw-sb); }
.demobar a { color: var(--c-on-deep-3); text-decoration: underline; }

/* ============ 15. 대시보드 · 차트 ============
   문서: 디자인/02-components.md §15
   색은 --dv-* 토큰만 쓴다. 종목 배지 색을 차트에 끌어오지 않는다(토큰 주석 참고). */

/* 깊이감은 원근 왜곡이 아니라 **표면의 층**으로 만든다.
   막대를 비스듬히 세우면 길이가 값과 어긋나 데이터가 거짓말을 한다. */
.dash { display: grid; gap: var(--sp-5); }
/* 그리드 아이템의 기본 min-width 는 auto 다 — 안쪽 콘텐츠(히트맵 min-width:460px)가
   컨테이너보다 넓으면 축소되지 않고 조상까지 밀어 올린다. 좁은 화면이 통째로 가로 스크롤된다. */
.dash > *, .dash__row > *, .chart > *, .chart__bd > * { min-width: 0; }
.dash .grid { align-items: start; }
/* 인라인 style 로는 미디어 쿼리를 못 건다. 행 배치는 클래스로 둔다. */
.dash__row { display: grid; grid-template-columns: 1.55fr 1fr; gap: var(--sp-5); align-items: start; }
@media (max-width: 980px) { .dash__row { grid-template-columns: minmax(0, 1fr); } }

.dash__hero {
  background: var(--grad-deep); border-radius: var(--r-lg);
  padding: var(--sp-6) var(--sp-6) var(--sp-5);
  position: relative; overflow: hidden;
  box-shadow: 0 18px 40px rgba(9, 22, 37, .28), 0 2px 0 rgba(255, 255, 255, .06) inset;
}
.dash__hero::after {                       /* 우상단 수중광 */
  content: ""; position: absolute; width: 420px; height: 420px; border-radius: 50%;
  top: -230px; right: -120px; pointer-events: none;
  background: radial-gradient(circle, rgba(94, 155, 209, .26) 0%, transparent 65%);
}
.dash__heroGrid { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-6); }
.dash__heroGrid > div { min-width: 0; }
@media (max-width: 860px) { .dash__heroGrid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-5); } }

.kpi__k { font-size: var(--fs-cap); color: var(--c-on-deep-2); letter-spacing: .02em; }
.kpi__v {
  margin-top: 6px; font-size: 30px; font-weight: var(--fw-bd); color: #fff;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums; line-height: 1.1;
}
.kpi__v small { font-size: 15px; font-weight: var(--fw-md); color: var(--c-on-deep-2); margin-left: 3px; }
.kpi__d { margin-top: 5px; font-size: var(--fs-cap); font-weight: var(--fw-sb); display: inline-flex; gap: 4px; }
.kpi__d--up { color: var(--c-on-deep-3); }
.kpi__d--down { color: var(--c-danger-deep); }
.kpi__d--flat { color: var(--c-on-deep-2); }
.kpi__spark { display: block; width: 100%; height: 26px; }
/* 스파크라인은 값이 아니라 '흐름'이다. 높이를 고정하지 않으면 SVG 가 패널 밖으로 흘러나온다. */
.kpi__sparkBox { margin-top: 10px; height: 26px; overflow: hidden; }

/* 차트 카드 */
.chart { background: var(--c-bg); border: 1px solid var(--c-line); border-radius: var(--r-md); box-shadow: var(--sh-1); }
.chart__hd { display: flex; align-items: baseline; gap: var(--sp-3); padding: var(--sp-5) var(--sp-5) 0; }
.chart__hd h3 { font-size: var(--fs-h3); font-weight: var(--fw-sb); letter-spacing: var(--ls-tight); }
.chart__sub { font-size: var(--fs-cap); color: var(--c-muted); }
.chart__bd { padding: var(--sp-4) var(--sp-5) var(--sp-5); position: relative; }
.chart__svg { display: block; width: 100%; overflow: visible; }

.dv-grid line { stroke: var(--dv-grid); stroke-width: 1; }
.dv-axis text { fill: var(--dv-axis); font-size: 11px; }
.dv-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.dv-dot { r: 4.5; }

/* 마우스를 올린 지점 */
.dv-cross { stroke: var(--c-muted-2); stroke-width: 1; stroke-dasharray: 3 3; }
.dv-hit { fill: transparent; cursor: crosshair; }

.dv-tip {
  position: absolute; pointer-events: none; z-index: 5;
  background: rgba(12, 26, 40, .95); color: #fff;
  padding: 8px 11px; border-radius: var(--r-xs);
  font-size: var(--fs-cap); line-height: 1.5; white-space: nowrap;
  box-shadow: var(--sh-3); transform: translate(-50%, -115%);
  opacity: 0; transition: opacity var(--t-fast) var(--ease);
}
.dv-tip.is-on { opacity: 1; }
.dv-tip b { color: var(--c-on-deep-3); font-variant-numeric: tabular-nums; }

.dv-empty { padding: var(--sp-8) var(--sp-4); text-align: center; }
.dv-empty__m { font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-muted); }
.dv-empty__h { font-size: var(--fs-cap); color: var(--c-mute2, var(--c-muted-2)); margin-top: 6px; }

/* 100% 누적 막대 — 부분과 전체 */
.dv-share { display: flex; height: 34px; border-radius: var(--r-xs); overflow: hidden; gap: 2px; }
.dv-share > i { display: block; height: 100%; transition: filter var(--t-fast) var(--ease); }
.dv-share > i:hover { filter: brightness(1.12); }
.dv-legend { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); margin-top: var(--sp-4); }
.dv-legend__i { display: inline-flex; align-items: center; gap: 7px; font-size: var(--fs-sm); color: var(--c-ink-2); }
.dv-legend__sw { width: 11px; height: 11px; border-radius: 3px; flex: none; }
.dv-legend__v { color: var(--c-muted); font-variant-numeric: tabular-nums; }

.dv-rank { display: grid; gap: 10px; margin-top: var(--sp-5); }
.dv-rank__r { display: grid; grid-template-columns: 92px 1fr auto; align-items: center; gap: var(--sp-3); }
.dv-rank__k { font-size: var(--fs-sm); color: var(--c-ink-2); }
.dv-rank__t { display: block; height: 12px; background: var(--c-bg-sink); border-radius: var(--r-full); overflow: hidden; }
.dv-rank__f { display: block; height: 100%; border-radius: var(--r-full); }
.dv-rank__v { font-size: var(--fs-cap); color: var(--c-muted); font-variant-numeric: tabular-nums; min-width: 64px; text-align: right; }

/* 히트맵 — 요일 × 회차.
   열이 12개(평일 8 + 주말 4 합집합)라 좁은 화면에서는 들어가지 않는다.
   페이지 전체를 가로로 밀지 말고 **자기 안에서만** 스크롤시킨다. */
.dv-heatWrap { overflow-x: auto; padding-bottom: 4px; }
.dv-heat { min-width: 460px; display: grid; gap: 3px; font-variant-numeric: tabular-nums; }
.dv-heat__cell { aspect-ratio: 1 / .62; border-radius: 4px; cursor: default; transition: transform var(--t-fast) var(--ease); }
.dv-heat__cell:hover { transform: scale(1.08); }
.dv-heat__lab { font-size: 10.5px; color: var(--dv-axis); text-align: center; }
.dv-heat__row { font-size: 10.5px; color: var(--dv-axis); display: grid; place-items: center end; padding-right: 6px; }

/* ⚠️ 막대의 트랙과 채움은 <span>·<i> 라 **display: block 이 반드시 있어야** 한다.
   인라인 요소에는 width·height 가 먹지 않아 아무것도 안 보인다 (.prog__img 와 같은 함정).

   예약 흐름 — 풀의 계단식 단면을 그대로 쓴다 (로고·수심 다이어그램과 같은 형태 언어) */
.dv-flow { display: grid; gap: var(--sp-2); }
.dv-flow__r { display: grid; grid-template-columns: 96px 1fr auto; align-items: center; gap: var(--sp-3); }
.dv-flow__k { font-size: var(--fs-sm); color: var(--c-ink-2); }
.dv-flow__t { display: block; height: 26px; background: var(--c-bg-sink); border-radius: var(--r-xs); overflow: hidden; }
.dv-flow__f { display: block; height: 100%; border-radius: var(--r-xs); box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset; }
.dv-flow__v { font-size: var(--fs-sm); font-weight: var(--fw-sb); font-variant-numeric: tabular-nums; min-width: 42px; text-align: right; }

/* 표 보기 — 색을 못 읽는 경우의 대안. 모든 차트에 딸린다. */
.dv-tableToggle { background: none; border: 0; color: var(--c-muted); font-size: var(--fs-cap); cursor: pointer; text-decoration: underline; }
.dv-tableToggle:hover { color: var(--c-ink); }
.dv-table { margin-top: var(--sp-4); font-size: var(--fs-sm); width: 100%; border-collapse: collapse; }
.dv-table th, .dv-table td { padding: 7px 10px; border-bottom: 1px solid var(--c-line); text-align: left; }
.dv-table td:not(:first-child), .dv-table th:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }

@media (forced-colors: active) {
  .dv-share > i, .dv-heat__cell, .dv-flow__f { forced-color-adjust: none; border: 1px solid CanvasText; }
}

/* 공지 목록 — 히어로 바로 아래에 오므로 세로로 길게 늘어지지 않게 2열로 편다.
   한 줄짜리 제목이 화면 폭을 다 쓰면 '중요한 안내' 가 아니라 '목록' 으로 읽힌다. */
.notice-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
@media (max-width: 720px) { .notice-grid { grid-template-columns: 1fr; } }

/* ============================================================
   16. 전국 지도 — 다이빙풀 위치 (Leaflet + OpenStreetMap)
   지도 엔진은 self-host 한 Leaflet(mockup/assets/vendor/leaflet/)이고,
   여기서는 **핀과 팝업의 옷만** 입힌다. leaflet.css 를 고치지 않는다 —
   버전을 올리면 통째로 덮어써야 하므로 손댄 것이 사라진다.
   회원풀장은 크고 특별하게, 나머지는 작은 점으로 — '어디에 있고 그중
   어디가 우리인지' 가 한눈에 읽혀야 한다.
   ============================================================ */
.pmap {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  gap: var(--sp-10); align-items: stretch;
}
@media (max-width: 860px) { .pmap { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); } }

/* 지도는 높이를 명시해야 한다 — Leaflet 컨테이너가 0높이면 타일이 아예 안 뜬다 */
.pmap__map {
  height: 480px; min-height: 320px;
  border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--c-line); background: var(--c-bg-sink);
  z-index: 0;                        /* 상단바(--z-topbar) 위로 타일이 올라오지 않게 */
}
@media (max-width: 860px) { .pmap__map { height: 360px; } }

.pmap__fallback { display: grid; place-items: center; height: 100%; padding: var(--sp-6); text-align: center; }

/* ---- 핀 ----
   Leaflet DivIcon 은 위치만 잡아 주고 모양은 우리가 그린다.
   .leaflet-div-icon 의 기본 흰 사각형 테두리를 지우는 것이 먼저다. */
.pmap__pin { background: none; border: 0; }
.pmap__pin span { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); border-radius: var(--r-full); display: block; }

/* 그 밖의 다이빙풀 — 있다는 사실만 알리면 된다 */
.pmap__odot { width: 9px; height: 9px; background: var(--c-muted-2); box-shadow: 0 0 0 2px var(--c-bg); }
.pmap__pin--o:hover .pmap__odot { background: var(--c-ink-2); }

/* 회원풀장 — 크고, 브랜드색이고, 번져 나가는 테를 두른다.
   **크기로 먼저 가른다.** 색만으로 가르면 핀이 작을수록 안 읽힌다. */
.pmap__halo { width: 26px; height: 26px; background: var(--c-primary); opacity: .18; transition: opacity var(--t-base) var(--ease); }
.pmap__dot  { width: 17px; height: 17px; background: var(--c-primary); box-shadow: 0 1px 3px rgba(12, 26, 40, .4); }
.pmap__core { width: 6px; height: 6px; background: #fff; }
.pmap__pin--m:hover .pmap__halo,
.pmap__pin--m:focus-visible .pmap__halo { opacity: .4; }

/* ---- 팝업 ---- */
.pmap__popup .leaflet-popup-content-wrapper {
  border-radius: var(--r-md); box-shadow: var(--sh-4); padding: 0;
  background: var(--c-bg); color: var(--c-ink);
}
.pmap__popup .leaflet-popup-content { margin: 0; padding: var(--sp-4) var(--sp-5); line-height: inherit; }
.pmap__popup .leaflet-popup-tip { background: var(--c-bg); box-shadow: var(--sh-2); }

/* Leaflet 기본 강조색(#0078A8 · rgba(51,181,229))이 팔레트 밖 청록이다.
   leaflet.css 는 고치지 않는다(버전 올릴 때 덮어써진다) — 여기서 덮는다. */
.pmap .leaflet-container a { color: var(--c-primary); }
.pmap .leaflet-container { -webkit-tap-highlight-color: rgba(36, 92, 143, .35); }
.pmap .leaflet-container a.leaflet-active,
.pmap .leaflet-bar a:focus-visible { outline: 2px solid var(--c-focus); outline-offset: -2px; }
/* 저작권 줄은 지도 바닥에 붙어 있고, Leaflet 이 자기 컨테이너에 건 overflow:hidden
   (leaflet.css §.leaflet-container)에 바깥 링의 아랫변이 잘린다 — 실측 여유 2px.
   확대/축소 버튼과 같게 링을 안쪽으로 뒤집는다. leaflet.css 는 고치지 않는다. */
.pmap .leaflet-control-attribution a:focus-visible { outline: 2px solid var(--c-focus); outline-offset: -2px; }

.pmap__pop-t { font-size: var(--fs-h3); font-weight: var(--fw-bd); color: var(--c-ink); letter-spacing: var(--ls-tight); }
.pmap__pop-b { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-2); }

/* 라벨-값 — .info 와 같은 얼개지만 팝업 폭(300)에 맞춰 라벨을 좁힌다 */
/* 내용이 길어도 팝업이 지도보다 길어지면 안 된다 — 아래 버튼이 잘려
   '풀장 홈' 으로 갈 방법이 사라진다. 표만 접히고 버튼은 늘 보이게 한다. */
.pmap__pd { margin-top: var(--sp-3); display: grid; gap: 5px; max-height: 210px; overflow-y: auto; }
.pmap__pr { display: grid; grid-template-columns: 58px minmax(0, 1fr); gap: var(--sp-3); align-items: baseline; }
.pmap__pr dt { font-size: var(--fs-cap); color: var(--c-muted); }
.pmap__pr dd { font-size: var(--fs-sm); color: var(--c-ink-2); margin: 0; word-break: keep-all; }

.pmap__pop-a { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }
/* 확인 안 된 값이라는 사실을 팝업 안에서 다시 말한다(디자인 절대 규칙 ④) */
.pmap__pop-n {
  margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--c-line-2);
  font-size: 11px; line-height: 1.5; color: var(--c-muted);
}

/* ---- 옆 패널 ---- */
.pmap__side { display: flex; flex-direction: column; justify-content: center; }
.pmap__legend { list-style: none; display: grid; gap: var(--sp-2); margin-top: var(--sp-5); }
.pmap__legend li {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-sm); color: var(--c-ink-2);
}
.pmap__key { flex: none; border-radius: var(--r-full); }
.pmap__key--m {
  width: 15px; height: 15px; background: var(--c-primary);
  box-shadow: 0 0 0 5px var(--c-primary-soft);
}
.pmap__key--o { width: 8px; height: 8px; background: var(--c-muted-2); margin-inline: 3px 4px; }

.pmap__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-5); }
.pmap__item {
  display: inline-flex; align-items: baseline; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4); border: 1px solid var(--c-line);
  border-radius: var(--r-full); background: var(--c-bg); text-decoration: none;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.pmap__item:hover { border-color: var(--c-primary); background: var(--c-primary-soft); text-decoration: none; }
.pmap__item-n { font-size: var(--fs-sm); font-weight: var(--fw-sb); color: var(--c-ink); }
.pmap__item-r { font-size: var(--fs-cap); color: var(--c-muted); }

/* ============================================================
   15. 사용설명서 — 긴 글을 순서대로 따라 읽는 화면
   장식이 아니라 가독성이 목적이다. 한 줄 길이를 제한하고,
   목차는 옆에 고정해 지금 어디를 보는지 알 수 있게 한다.
   ============================================================ */
.guide { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: var(--sp-10); align-items: start; }
/* ⚠️ 1fr 이 아니라 minmax(0, 1fr) 이다. 그리드 트랙의 최소폭 기본값은 auto(=콘텐츠
   최소폭)라, 본문에 min-width:720px 인 .tbl 이 하나 있으면 트랙이 608px 로 벌어져
   페이지가 통째로 248px 넘쳤다. 0 을 최소로 못박아야 .tbl-scroll 이 제 역할을 한다. */
@media (max-width: 900px) { .guide { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); } }

/* 목차 — 데스크톱에서만 따라온다 */
.guide__toc { position: sticky; top: 84px; display: grid; gap: 2px; }
@media (max-width: 900px) { .guide__toc { position: static; } }
.guide__toc a {
  display: block; padding: 7px 10px; border-radius: var(--r-xs);
  font-size: var(--fs-sm); color: var(--c-ink-2); text-decoration: none;
}
.guide__toc a:hover { background: var(--c-bg-soft); color: var(--c-ink); }
.guide__toc a.is-on { background: var(--c-primary-soft); color: var(--c-primary-ink); font-weight: var(--fw-sb); }

.guide__body { max-width: 68ch; }
.guide__sec + .guide__sec { margin-top: var(--sp-12); padding-top: var(--sp-10); border-top: 1px solid var(--c-line); }
.guide__sec h2 { font-size: var(--fs-h2); font-weight: var(--fw-bd); color: var(--c-ink); scroll-margin-top: 96px; }
.guide__sec p { margin-top: var(--sp-3); font-size: var(--fs-body); line-height: 1.8; color: var(--c-ink-2); }
.guide__sec .note { margin-top: var(--sp-4); }
.guide__sec .tbl-scroll { margin-top: var(--sp-4); }

/* 순서대로 하는 동작 — 번호가 손을 이끈다 */
.guide__steps { margin-top: var(--sp-4); display: grid; gap: var(--sp-3); counter-reset: gstep; }
.guide__steps li {
  position: relative; padding-left: 38px; font-size: var(--fs-body);
  line-height: 1.75; color: var(--c-ink-2);
}
.guide__steps li::before {
  counter-increment: gstep; content: counter(gstep);
  position: absolute; left: 0; top: 1px;
  width: 26px; height: 26px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--c-primary); color: #fff;
  font-size: var(--fs-cap); font-weight: var(--fw-bd); font-variant-numeric: tabular-nums;
}

/* ============ 16. 언어 전환 ============
   상단바 우측 상단. 문서: 디자인/02-components.md §16 */
/* ⚠️ overflow:hidden 을 쓰지 않는다 — 안쪽 버튼의 포커스 링은 바깥 5px(offset 2 + 굵기 3)에
   그려지는데, 버튼이 컨테이너를 꽉 채우고 있어 링이 **통째로 잘렸다**(2026-08-22 실측).
   hidden 은 is-on 배경을 알약 모양으로 자르려던 것인데, 양 끝 버튼에 반경을 직접 주면
   같은 그림이 되고 링은 살아난다. */
.langsw {
  display: inline-flex; flex: none;
  border: 1px solid var(--c-line); border-radius: var(--r-full);
  background: var(--c-bg);
}
.langsw__b:first-child { border-radius: var(--r-full) 0 0 var(--r-full); }
.langsw__b:last-child  { border-radius: 0 var(--r-full) var(--r-full) 0; }
.langsw__b {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 0 10px; height: 30px;
  font-size: 11.5px; font-weight: var(--fw-sb); letter-spacing: .04em;
  color: var(--c-muted);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.langsw__b + .langsw__b { border-left: 1px solid var(--c-line); }
.langsw__b:hover { background: var(--c-bg-soft); color: var(--c-ink); }
.langsw__b.is-on { background: var(--c-primary); color: #fff; }
/* 다크 상단바(관리자 모드)에서도 읽히게 */
.topbar--admin .langsw { border-color: rgba(255, 255, 255, .22); background: rgba(255, 255, 255, .06); }
.topbar--admin .langsw__b { color: var(--c-on-deep-2); }
.topbar--admin .langsw__b + .langsw__b { border-left-color: rgba(255, 255, 255, .22); }
.topbar--admin .langsw__b.is-on { background: var(--c-accent); color: var(--c-deep-1); }
@media (max-width: 560px) { .langsw__b { padding: 0 7px; font-size: 11px; } }

/* ============================================================
   99. 터치 화면의 최소 타깃 — **파일 맨 끝이어야 한다**
   손가락은 마우스 커서가 아니다. 34px 짜리 .btn--sm 과 30px 짜리 언어 버튼은
   화면에서는 멀쩡해 보여도 휴대폰에서 옆 것이 눌린다. 저장소 기준은 44px 다
   (.anav 주석에도 같은 값이 적혀 있다).

   화면 폭이 아니라 **포인터 종류**로 가른다. 좁은 창으로 줄인 데스크톱은
   여전히 마우스라 촘촘한 편이 낫고, 큰 태블릿은 넓어도 손가락이다.
   ⚠️ 미디어쿼리는 명시도를 올리지 않는다. 앞쪽에 두면 뒤에 오는 같은 명시도의
   .btn--sm(--btn-h:34px)·.langsw__b(height:30px)에 그대로 덮인다 — 실제로 그랬다.
   ============================================================ */
@media (pointer: coarse) {
  .btn--sm { --btn-h: 44px; }
  .chip { height: 44px; }
  .langsw__b { height: 44px; }
  .cal__nav { width: 44px; height: 44px; }   /* 달 넘기기 — 옆 날짜가 눌리면 예약일이 틀어진다 */
  /* 밑줄 링크 모양이라 높이를 주면 모양이 무너진다 — 위아래 여백으로만 넓힌다 */
  .dv-tableToggle { padding: 12px 4px; }
  /* .tab 은 밑줄 메뉴라 높이가 아니라 여백으로 늘린다 — 밑줄 위치가 바뀌면 안 된다 */
  .tab { padding-top: var(--sp-3); }
}
