/* 교수 자기소개 인터랙티브 페이지 — DESIGN_SPEC v1.0 구현.
   모든 값은 css/tokens.css 토큰 사용(하드코딩 금지), 기울임은 --tilt-1~3 3종만. */

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

html,
body {
  height: 100%;
}

body {
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--ink);
  overflow: hidden;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

/* ===== 수평 월드 + 스냅 팬 (§1) ===== */

.viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.world {
  position: absolute;
  top: 0;
  height: 100%;
  transition: transform var(--dur-move) var(--ease-move);
  will-change: transform;
}

.road {
  position: absolute;
  inset: 0;
  height: 100%;
  pointer-events: none;
}

.road > path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 3;
  stroke-dasharray: 2 12;
  stroke-linecap: round;
}

/* 종착역 뒤 강의실 문 낙서 (§2 종착역) — 잉크 라인, 살짝 열린 문 */
.road .door rect,
.road .door path,
.road .door circle {
  fill: none;
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* 연도 배경 숫자 — 장식 전용 (§7-6) */
.year-bg {
  position: absolute;
  top: 62%;
  font-family: var(--font-head);
  font-size: 7rem;
  color: rgba(91, 97, 108, 0.14);
  pointer-events: none;
  user-select: none;
}

/* ===== 오프닝 ===== */

.opening {
  position: absolute;
  left: 120px;
  top: 50%;
  transform: translateY(-50%);
  width: 680px;
}

.opening-figure {
  position: relative;
  display: inline-block;
}

#opening-sticker {
  height: 38vh;
  display: block;
  margin-bottom: 10px;
}

.opening h1 {
  font-family: var(--font-head);
  font-size: var(--fs-hero);
  line-height: 1.2;
}

.opening h1 em,
.station .st-name em,
.card h3 em,
.mark em {
  font-style: normal;
  background: var(--sunny-marker);
}

.op-title {
  font-family: var(--font-head);
  font-size: clamp(1rem, 1.6vw, 1.3rem);
  color: var(--coral);
  margin-top: 6px;
}

/* 짧은 훅("오래된 개발자") — 형광펜 마커 + 헤드라인 폰트로 자막 한 컷 처리 */
.op-hook {
  display: inline-block;
  font-family: var(--font-head);
  font-size: clamp(1.6rem, 2.8vw, 2.4rem);
  line-height: 1.35;
  margin-top: 14px;
  background: var(--sunny-marker);
  transform: rotate(var(--tilt-2));
}

.op-sub {
  font-size: var(--fs-body); /* 원거리 가독 기준 — 캡션 크기 금지 (@designer QA P2) */
  color: var(--ink-soft);
  line-height: 1.6;
  margin-top: 10px;
  max-width: 620px;
}

.big-btn {
  margin-top: 18px;
  font-family: var(--font-head);
  font-size: clamp(1.1rem, 1.8vw, 1.4rem);
  padding: 12px 36px;
  border: var(--line) solid var(--ink);
  border-radius: var(--radius-pill);
  background: var(--sunny);
  box-shadow: var(--pop-shadow);
  transition: transform 120ms var(--ease-pop), box-shadow 120ms;
}

.big-btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 rgba(35, 39, 47, 0.9);
}

.big-btn.ghost {
  background: var(--card);
}

/* ===== 정거장 노드 (§2) ===== */

.station {
  position: absolute;
  opacity: 0.6;
  transition: opacity 300ms;
}

.station.current {
  opacity: 1;
}

.station .dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--coral);
  border: 2px solid var(--ink);
  transform: translate(-50%, -50%);
  transition: width 200ms var(--ease-pop), height 200ms var(--ease-pop);
}

.station.current .dot {
  width: 14px;
  height: 14px;
  animation: dot-pulse 300ms var(--ease-pop);
}

@keyframes dot-pulse {
  50% {
    transform: translate(-50%, -50%) scale(1.6);
  }
}

.station .info {
  position: absolute;
  left: 0;
  width: 320px;
  transform: translateX(-50%);
  text-align: center;
  cursor: pointer;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
}

.station.above .info {
  bottom: 30px;
}

.station.below .info {
  top: 30px;
}

.station .period {
  font-size: var(--fs-caption);
  color: var(--ink-soft);
}

.station .st-name {
  display: block;
  font-family: var(--font-head);
  font-size: var(--fs-station);
  line-height: 1.3;
  margin: 2px 0;
  font-weight: 400;
}

.station .hook {
  font-size: var(--fs-caption);
  color: var(--ink-soft);
}

.station .click-hint {
  font-size: var(--fs-caption);
  color: var(--coral);
  font-weight: 700;
  opacity: 0;
  transition: opacity 150ms;
}

.station .info:hover .click-hint {
  opacity: 1;
}

/* 간이역: 75% 스케일 (§2) */
.station.minor .dot {
  width: 8px;
  height: 8px;
}

.station.minor.current .dot {
  width: 11px;
  height: 11px;
}

.station.minor .info {
  width: 250px;
}

.station.minor .st-name {
  font-size: calc(var(--fs-station) * 0.85);
}

/* 종착역: 간이역의 반대 위계 — 115% 스케일 + NOW 스탬프 길 위 상시 노출 (§2) */
.station.terminal .info {
  transform: translateX(-50%) scale(1.15);
}

.station.terminal.above .info {
  transform-origin: bottom center;
}

.station.terminal.below .info {
  transform-origin: top center;
}

.station.terminal .dot {
  opacity: 0;
}

.road-stamp {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%) rotate(var(--tilt-3));
  width: 74px;
  height: 74px;
  border-radius: 50%;
  border: 2.5px dashed var(--coral);
  color: var(--coral);
  background: var(--paper);
  font-family: var(--font-head);
  font-size: 1.05rem;
  display: grid;
  place-items: center;
  text-align: center;
}

/* ===== 클로징 — 톤 다운(진심 구간, 장식 최소) ===== */

.closing {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 720px;
  text-align: center;
}

.closing-lead {
  font-family: var(--font-head);
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  color: var(--coral);
}

.closing h2 {
  font-family: var(--font-head);
  font-size: clamp(1.9rem, 3.4vw, 2.8rem);
  margin: 8px 0 20px;
  font-weight: 400;
}

.closing-message {
  font-size: var(--fs-body); /* 원거리 가독 기준 (@designer QA P2) */
  line-height: 1.85;
  text-align: left;
  white-space: pre-line;
}

.closing-message strong {
  font-weight: 800;
}

#closing-sticker {
  display: block;
  height: 24vh;
  margin: 22px auto 0;
  transform: scaleX(-1) scale(0.85) rotate(var(--tilt-1));
}

.closing-contact {
  font-size: var(--fs-caption);
  color: var(--ink-soft);
  margin-top: 14px;
}

.closing-contact a {
  color: var(--coral);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 2px solid var(--coral);
}

/* ===== 진행 표시 + 키 안내 (§6) ===== */

.progress {
  position: fixed;
  right: 24px;
  bottom: 20px;
  font-family: var(--font-head);
  font-size: 1.25rem;
  z-index: 5;
}

.keys {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  font-size: var(--fs-caption);
  color: var(--ink-soft);
  z-index: 5;
  transition: opacity 400ms;
}

kbd {
  font-family: var(--font-body);
  font-weight: 700;
  border: var(--line) solid var(--ink);
  border-radius: 6px;
  background: var(--card);
  box-shadow: var(--pop-shadow-small);
  padding: 0 7px;
  margin-right: 2px;
  font-size: 0.8rem;
}

/* ===== 확대 카드 (§3) ===== */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: rgba(35, 39, 47, 0.35);
  display: none;
  place-items: center;
  padding: 4vh 4vw;
}

.overlay.open {
  display: grid;
}

.card {
  width: min(1060px, 88vw);
  max-height: 90vh;
  overflow-y: auto;
  /* 클래식 스크롤바(Windows)에서 스크롤바 토글→내부 폭 변동→재랩→높이 변동의
     진동 루프를 차단 — 거터를 상시 예약해 내부 폭을 불변으로 만든다 (§9 멱등 원칙) */
  scrollbar-gutter: stable;
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: var(--radius-card);
  box-shadow: var(--pop-shadow);
  padding: 40px;
  display: grid;
  grid-template-columns: 42% 1fr;
  gap: 32px;
  position: relative;
  animation: card-in var(--dur-pop) var(--ease-pop) both;
}

.card.minor {
  width: min(880px, 84vw);
}

/* §9 v1.9: 오프스크린 선측정 — 불가시 상태로 완전한 콘텐츠를 실측한 뒤 열림 애니메이션 시작 */
.card.measuring {
  visibility: hidden;
  animation: none;
}


/* 폭·컬럼 불변 규칙 (DESIGN_SPEC §3 v1.1)
   ① 카드 폭은 콘텐츠와 무관하게 위 고정값 — 이미지 비율이 폭을 결정하지 않는다.
   ② 프레임 열 상한: 가로형 42%(기본) / 세로형 34% / 간이역+세로형 30%. */
.card.portrait {
  grid-template-columns: 34% 1fr;
}

.card.minor.portrait {
  grid-template-columns: 30% 1fr;
}

/* §9 v1.10 무스크롤 2단계: 텍스트 기둥 카드 — 사진 열 축소로 텍스트 열 확폭 (portrait보다 우선하도록 후순위 선언) */
.card.squeeze {
  grid-template-columns: 30% 1fr;
}

/* ③ 텍스트 컬럼 실폭 40% 미달 시 스티커 22vh 축소(예약 폭 동반 축소) — 엔진이 측정 후 부여 */
.card.slim-sticker .card-sticker {
  height: 22vh;
}

.card.slim-sticker .card-sticker.reuse {
  height: 18.7vh; /* 22vh × 85% */
}

.card.slim-sticker.has-sticker .text-col {
  padding-right: max(120px, calc(16.5vh - 16px));
}

.card.slim-sticker.has-sticker.sticker-left .text-col {
  padding-right: 0;
}

/* 종착역 카드 배치 확정 (§4 v1.3, 종착역 전용):
   스티커를 좌열(사진 열) 하단 중앙으로 이동 — "박사 사진 위 + 도착한 나 아래" 세로 구성.
   텍스트 컬럼의 스티커 예약이 사라져 본문이 우열 전폭을 되찾는다. 28vh 보장은 유지. */
.card.terminal {
  grid-template-rows: auto 1fr;
}

.card.terminal .frame-stack {
  grid-row: 1;
  grid-column: 1;
}

.card.terminal .text-col {
  grid-row: 1 / 3;
  grid-column: 2;
}

/* 종착역 좌열 2행 = 저서 진열 (§4 v1.4) — 3권 가로 한 줄, 8~12% 겹침.
   표지 = 인쇄물: 잉크 1px 보더 + 4px radius + 하드 섀도, 기울임 3종 순환, 높이 상한 20vh.
   미확정 권은 점선 실루엣 플레이스홀더(점선 = 미확정 관례). */
.books {
  grid-row: 2;
  grid-column: 1;
  align-self: start;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-top: 12px;
}

.book {
  height: min(16vh, 160px); /* v1.10: 20vh→16vh 하향(§8.3 모바일 값과 통일) */
  max-width: 34%; /* 좌열 폭 하드 클램프 — 3권 겹침 총폭이 열을 넘지 않게 */
  object-fit: contain;
  border: 1px solid var(--ink);
  border-radius: 4px;
  box-shadow: var(--pop-shadow-small);
  transform: scale(0);
  background: var(--card);
}

.books {
  max-width: 100%;
}

.book + .book {
  margin-left: -1.4vh; /* 표지 폭(~14vh)의 약 10% 겹침 */
}

.book:nth-child(3n + 1) {
  --book-tilt: var(--tilt-1);
}

.book:nth-child(3n + 2) {
  --book-tilt: var(--tilt-2);
}

.book:nth-child(3n) {
  --book-tilt: var(--tilt-3);
}

.book.show {
  animation: book-in var(--dur-pop) var(--ease-pop) both;
}

@keyframes book-in {
  from {
    transform: rotate(var(--book-tilt)) scale(0.6);
    opacity: 0;
  }
  to {
    transform: rotate(var(--book-tilt)) scale(1);
    opacity: 1;
  }
}

.book.placeholder {
  width: 13vh;
  border: 2px dashed var(--ink-soft);
  box-shadow: none;
  background: none;
  display: grid;
  place-items: center;
  padding: 8px;
}

.book.placeholder span {
  font-family: var(--font-head);
  font-size: 0.72rem;
  color: var(--ink-soft);
  text-align: center;
  line-height: 1.35;
  word-break: keep-all;
}

@keyframes card-in {
  from {
    opacity: 0;
    transform: scale(0.9) rotate(var(--tilt-1));
  }
  to {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

.card .close {
  position: absolute;
  top: 14px;
  right: 18px;
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--ink-soft);
  z-index: 2;
}

/* 폴라로이드 프레임 스택 — 스샷 or 대형 이모지 (§3-1) */
.frame-stack {
  position: relative;
  align-self: center;
  min-height: 300px;
}

.frame {
  background: var(--card);
  border: var(--line) solid var(--ink);
  box-shadow: var(--pop-shadow-small);
  padding: 12px 12px 30px;
}

.frame.front {
  position: relative;
  transform: rotate(var(--tilt-2));
  cursor: pointer;
  z-index: 2;
}

.frame.back {
  position: absolute;
  inset: 0;
  transform: rotate(var(--tilt-3)) translate(-10px, 8px);
  z-index: 1;
}

.frame.back.b2 {
  transform: rotate(var(--tilt-1)) translate(10px, -6px);
  z-index: 0;
}

.frame .shot {
  background: var(--paper);
  display: grid;
  place-items: center;
  min-height: 240px;
  position: relative; /* §9 크로스페이드 레이어 기준 */
}

.frame .shot img {
  max-width: 100%;
  max-height: 42vh;
  object-fit: contain;
  display: block;
}

.frame .shot.emoji {
  font-size: 5rem;
  letter-spacing: 8px;
  padding: 30px 10px;
}

.frame .cap {
  text-align: center;
  font-size: var(--fs-caption);
  color: var(--ink-soft);
  margin-top: 8px;
  min-height: 2.6em; /* §9-3: 캡션 2줄 예약 — 장별 캡션 길이 차이로 인한 2차 리플로우 차단 */
}

/* §9 (v1.6): 순환용 2레이어 이미지 — 프레임 고정 후 크로스페이드 150ms */
.frame .shot .ph.b {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: scale-down;
}

.frame .shot .ph {
  opacity: 0;
  transition: opacity 150ms var(--ease-move);
}

.frame .shot .ph.show {
  opacity: 1;
}

/* §9 락 전·후 공통 안전 기본값: 어떤 타이밍/계산값에서도 프레임·컬럼을 넘을 수 없다 */
.frame {
  max-width: 100%;
}

.frame .shot.locked {
  margin: 0 auto;
  max-width: 100%;
  max-height: 42vh;
}

body.mobile .frame .shot.locked {
  max-height: min(32vh, 340px);
}

.frame .shot.locked .ph {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: scale-down;
}

/* ⓐ 상상도 프레임 — 점선 보더 = 실물 아님 (DESIGN_SPEC §3 프레임 변형) */
.frame.sketch {
  border: 2px dashed var(--ink);
}

.imagine-chip {
  display: inline-block;
  border: 2px solid var(--sky);
  color: var(--sky);
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: 0.75rem;
  padding: 0 8px;
  margin-right: 6px;
}

/* ⓑ 브라운관 프레임 — 저해상 방송 스틸용, 업스케일 금지 */
.frame.crt {
  border: 12px solid var(--ink);
  border-radius: 22px;
  padding: 0 0 20px;
  max-width: 82%;
  margin: 0 auto;
  position: relative;
}

.frame.crt .shot {
  min-height: 0;
  border-radius: 8px 8px 0 0;
  overflow: hidden;
}

.frame.crt .shot img {
  width: auto;
  max-width: 100%;
  max-height: 34vh;
}

.frame.crt::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 6px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--coral);
  transform: translateX(-50%);
}

.frame.crt .cap {
  margin: 8px 6px 0;
}

/* 텍스트 컬럼 — 스티커 보유 시 폭 예약으로 오버랩 차단 (§4) */
.card .text-col {
  min-width: 0;
}

/* 예약 폭 = 스티커 실폭 연동(높이 30vh × 원본 비율 0.75 = 22.5vh) — 항상 22px 이상 이격 보장 */
.card.has-sticker .text-col {
  padding-right: max(160px, calc(22.5vh - 24px));
}

.card.has-sticker.sticker-left .text-col {
  padding-right: 0;
}

.card .period {
  font-size: var(--fs-caption);
  color: var(--ink-soft);
}

.card h3 {
  font-family: var(--font-head);
  font-size: var(--fs-card-title);
  line-height: 1.25;
  margin: 4px 0 12px;
  font-weight: 400;
  word-break: keep-all; /* 한국어 어절 단위 랩 — 마지막 줄 한 글자 고아 방지 */
  text-wrap: balance;
}

.card .desc {
  font-size: var(--fs-body);
  line-height: 1.65;
}

.card .desc strong {
  color: var(--coral);
  font-weight: 700;
}

/* 간이역 미니 스탬프 (§2) */
.minor-stamp {
  display: inline-block;
  vertical-align: middle;
  margin-left: 10px;
  border: 2px dashed var(--sky);
  color: var(--sky);
  border-radius: var(--radius-pill);
  font-family: var(--font-head);
  font-size: 0.85rem;
  padding: 2px 12px;
  transform: rotate(var(--tilt-2));
}

/* 하이라이트 숫자 + 스탬프 (§3·§5) */
.numrow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 24px;
  margin: 20px 0 14px;
}

.stat .bignum {
  font-family: var(--font-head);
  font-size: var(--fs-num);
  color: var(--coral);
  line-height: 1.1;
}

.stat .bignum small {
  font-size: 55%;
}

.stat .stat-label {
  display: block;
  font-size: var(--fs-caption);
  color: var(--ink-soft);
}

.stat.pulse .bignum {
  animation: num-pulse 160ms var(--ease-pop);
}

@keyframes num-pulse {
  50% {
    transform: scale(1.06);
  }
}

.stamp {
  width: 86px;
  height: 86px;
  border-radius: 50%;
  border: 2.5px dashed var(--coral);
  color: var(--coral);
  font-family: var(--font-head);
  font-size: 1rem;
  display: grid;
  place-items: center;
  text-align: center;
  transform: rotate(var(--tilt-3)) scale(0);
  flex-shrink: 0;
}

.stamp.show {
  animation: stamp-in var(--dur-pop) var(--ease-pop) both;
}

@keyframes stamp-in {
  from {
    transform: rotate(var(--tilt-3)) scale(1.4);
  }
  to {
    transform: rotate(var(--tilt-3)) scale(1);
  }
}

/* 형광펜 하이라이트 칩 */
.mark {
  font-weight: 700;
  font-size: var(--fs-caption);
  margin: 2px 0 10px;
}

/* 기술 배지 (§3-2) */
.badges {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 8px;
}

.badge {
  font-size: var(--fs-badge);
  font-weight: 700;
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: var(--radius-pill);
  box-shadow: var(--pop-shadow-small);
  padding: 4px 14px;
}

.badge:nth-child(3n + 1) {
  transform: rotate(var(--tilt-1));
}

.badge:nth-child(3n + 2) {
  transform: rotate(var(--tilt-2));
}

.badge:nth-child(3n) {
  transform: rotate(var(--tilt-3));
  border-color: var(--sky);
}

/* 카드 속 스티커 (§4) — 기본 우하단, 재사용 변주는 반전+85%+기울임 부호 반전 후 좌하단.
   음수 오프셋 삐져나옴은 overflow:auto에 항상 클리핑되며 유령 스크롤(~28px)만 만들던 것 → 0 기준 정착 (v1.9) */
.card .card-sticker {
  position: absolute;
  right: 4px;
  bottom: 0;
  height: 30vh;
  transform: rotate(var(--tilt-2)) scale(0);
  pointer-events: none;
  z-index: 3;
}

.card .card-sticker.show {
  animation: sticker-in var(--dur-pop) var(--ease-pop) both;
}

@keyframes sticker-in {
  from {
    transform: rotate(var(--tilt-2)) scale(0.6);
  }
  to {
    transform: rotate(var(--tilt-2)) scale(1);
  }
}

.card .card-sticker.reuse {
  right: auto;
  left: 4px;
  height: 25.5vh; /* 30vh × 85% */
  transform: rotate(var(--tilt-1)) scaleX(-1) scale(0);
}

.card .card-sticker.reuse.show {
  animation: sticker-in-reuse var(--dur-pop) var(--ease-pop) both;
}

@keyframes sticker-in-reuse {
  from {
    transform: rotate(var(--tilt-1)) scaleX(-1) scale(0.6);
  }
  to {
    transform: rotate(var(--tilt-1)) scaleX(-1) scale(1);
  }
}

/* 재사용이지만 반전 미적용(스티커3 — 정면 시선, §4 예외): 우하단 유지, 85% + 기울임 부호 반전만 */
.card .card-sticker.reuse.noflip {
  left: auto;
  right: 4px;
  transform: rotate(var(--tilt-1)) scale(0);
}

.card .card-sticker.reuse.noflip.show {
  animation: sticker-in-noflip var(--dur-pop) var(--ease-pop) both;
}

@keyframes sticker-in-noflip {
  from {
    transform: rotate(var(--tilt-1)) scale(0.6);
  }
  to {
    transform: rotate(var(--tilt-1)) scale(1);
  }
}

/* "함께한 사람 🤝" 뱃지 — 형광펜 옐로 리본 태그 (DESIGN_SPEC §3, 3곳 한 세트).
   카드 제목 바로 아래 고정 위치, 카드와 동시 등장(지연 애니메이션 없음). */
.partner-ribbon {
  display: inline-block;
  background: var(--sunny);
  border: var(--line) solid var(--ink);
  border-radius: 6px;
  box-shadow: var(--pop-shadow-small);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 10px 50%);
  font-family: var(--font-head);
  font-size: clamp(0.9rem, 1.2vw, 1.05rem);
  padding: 5px 16px 5px 24px;
  margin: 0 0 12px;
  transform: rotate(var(--tilt-1));
}

/* 말풍선 — 카드 상단 밖, 스티커 머리 위 (§3·목업 검증 규칙) */
.bubble {
  position: absolute;
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: 14px;
  box-shadow: var(--pop-shadow-small);
  font-family: var(--font-head);
  font-size: 1.05rem;
  padding: 10px 16px;
  max-width: 260px;
  transform: rotate(var(--tilt-2));
  white-space: pre-line;
}

.bubble::after {
  content: "";
  position: absolute;
  right: 34px;
  bottom: -14px;
  border: 7px solid transparent;
  border-top-color: var(--ink);
}

.card .bubble {
  right: 30px;
  top: -58px;
}


/* ============================================================
   모바일 대응 (DESIGN_SPEC §8, v1.5) — max-width 768px 단일 기준.
   세로 스크롤 길(자유 스크롤) + 카드 1열 스택 시트. 769px 이상 회귀 0.
   ============================================================ */
@media (max-width: 768px) {
  body.mobile {
    overflow-y: auto;
    overflow-x: hidden;
  }

  body.mobile .viewport {
    position: static;
    overflow: visible;
  }

  body.mobile .world {
    position: static;
    height: auto;
    width: auto !important;
    transform: none !important;
    transition: none;
  }

  body.mobile #road,
  body.mobile #year-layer,
  body.mobile .keys,
  body.mobile .progress {
    display: none;
  }

  /* ----- 오프닝: 1열 스택 (§8.2) ----- */
  body.mobile .opening {
    position: static;
    transform: none;
    width: auto;
    padding: 9vh 7vw 4vh;
    text-align: center;
  }

  body.mobile #opening-sticker {
    height: 32vh;
    margin: 0 auto 12px;
  }

  body.mobile .op-sub {
    margin-left: auto;
    margin-right: auto;
  }

  body.mobile .big-btn {
    min-height: 44px;
  }

  /* ----- 세로 길 + 정거장 (§8.2) ----- */
  body.mobile #stations {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 8vh;
    padding: 5vh 0 4vh;
  }

  body.mobile .road-v {
    position: absolute;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    width: 20px;
    height: 100%;
    pointer-events: none;
  }

  body.mobile .road-v path {
    fill: none;
    stroke: var(--ink);
    stroke-width: 3;
    stroke-dasharray: 2 12;
    stroke-linecap: round;
  }

  body.mobile .station.m {
    position: relative;
    left: auto;
    top: auto;
    opacity: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 0;
  }

  /* §8.7: 모바일 카드·노드에서 absolute 배치 금지 — 도트·스탬프도 정적 플로우 */
  body.mobile .station.m .dot {
    position: static;
    transform: none;
    margin: 4px auto 8px;
  }

  body.mobile .station.m .road-stamp {
    position: static;
    transform: rotate(var(--tilt-3));
    margin: 4px auto 8px;
    width: 64px;
    height: 64px;
    font-size: 0.95rem;
  }

  body.mobile .station.m .info {
    position: static;
    transform: none;
    width: min(86vw, 420px);
    min-height: 44px;
    padding: 6px 10px;
    background: var(--paper); /* 중앙 점선 길을 텍스트 뒤에서 가린다 */
    position: relative;
    z-index: 1;
  }

  body.mobile .station.m.terminal .info {
    transform: none; /* 115% 스케일 해제 — 모바일은 폭 통일 (§8.3) */
  }

  /* 연도 배경: 구간 배경 유지, 5rem 축소 (§8.2) — 인플로우 배치.
     absolute 배치 시 노드의 길-가림 배경 뒤로 깔려 절반 잘려 보이는 버그가 있었음(§8.7) */
  body.mobile .year-bg-m {
    position: static;
    display: block;
    width: 100%;
    text-align: center;
    font-family: var(--font-head);
    font-size: 5rem;
    line-height: 1;
    color: rgba(91, 97, 108, 0.12);
    pointer-events: none;
    user-select: none;
    margin-bottom: -14px;
  }

  /* ----- 클로징 ----- */
  body.mobile .closing {
    position: static;
    transform: none;
    width: auto;
    padding: 6vh 7vw 12vh;
  }

  body.mobile #closing-sticker {
    height: 20vh;
  }

  /* ----- 확대 카드: 1열 스택 시트 (§8.3) ----- */
  body.mobile .overlay {
    padding: 3vh 4vw;
  }

  .card.m {
    display: flex;
    flex-direction: column;
    width: 92vw;
    max-height: 88vh;
    overflow-y: auto;
    padding: 26px 20px 20px;
  }

  /* 핵심: max-height에 맞추려는 flex-shrink가 프레임 박스를 압축해
     내부 이미지가 본문 위로 흘러넘치는 버그 방지 — 압축 대신 내부 스크롤로 처리 */
  .card.m > * {
    flex-shrink: 0;
  }

  .card.m .close {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    top: 6px;
    right: 6px;
    font-size: 1.3rem;
  }

  /* §8.7: 기울임 요소 래퍼 상하 마진 12px + 스택 기울임 50% 축소
     (rotate 시각 박스가 레이아웃 박스를 넘는 분량을 마진으로 흡수) */
  .card.m .frame-stack {
    align-self: center;
    margin: 12px 0;
    min-height: 0;
    max-width: 100%;
  }

  .card.m .frame.front {
    transform: rotate(calc(var(--tilt-2) * 0.5));
  }

  .card.m .frame.back {
    transform: rotate(calc(var(--tilt-3) * 0.5)) translate(-6px, 5px);
  }

  .card.m .frame.back.b2 {
    transform: rotate(calc(var(--tilt-1) * 0.5)) translate(6px, -4px);
  }

  .card.m .badge:nth-child(3n + 1) {
    transform: rotate(calc(var(--tilt-1) * 0.5));
  }

  .card.m .badge:nth-child(3n + 2) {
    transform: rotate(calc(var(--tilt-2) * 0.5));
  }

  .card.m .badge:nth-child(3n) {
    transform: rotate(calc(var(--tilt-3) * 0.5));
  }

  body.mobile .frame .shot {
    min-height: 0;
  }

  /* v1.8: 카드 치수는 열림 시 고정되므로, 사진 로드 전에도 자리 예약(플레이스홀더) —
     콜드 캐시에서 카드가 사진 없는 높이로 굳는 것 방지 */
  body.mobile .frame .shot:not(.locked):not(.emoji) {
    min-height: min(32vh, 240px);
  }

  body.mobile .frame .shot img {
    max-height: min(32vh, 340px); /* 큰 화면 높이에서 vh 과대 방지 픽셀 상한 */
  }

  .card.m .desc {
    margin-top: 6px;
  }

  .card.m .numrow {
    margin: 14px 0 10px;
  }

  .card.m .badges {
    margin: 8px 0 0;
  }

  /* 스티커 16vh — 마지막 행 우측 별도 배치(오버랩 원천 차단, §8.3) */
  .card.m .m-sticker {
    height: min(16vh, 160px);
    align-self: flex-end;
    margin-top: 10px;
    transform: scale(0);
    pointer-events: none;
  }

  .card.m .m-sticker.show {
    animation: m-sticker-in var(--dur-pop) var(--ease-pop) both;
  }

  /* 책 진열: 사진 아래 가로 한 줄 16vh (§8.3) */
  .card.m .books {
    margin: 4px 0 10px;
  }

  body.mobile .book {
    height: min(16vh, 160px);
  }

  body.mobile .book + .book {
    margin-left: -1.1vh;
  }

  /* 말풍선은 모바일 미표시 (§8.3) */
  body.mobile .bubble {
    display: none;
  }
}

/* §8.7: 가로모드(높이 ≤500px) — 사진 240px로 추가 강등 */
@media (max-width: 768px) and (max-height: 500px) {
  body.mobile .frame .shot img {
    max-height: min(32vh, 240px);
  }
}

@keyframes m-sticker-in {
  from {
    transform: scale(0.6) rotate(var(--tilt-2));
    opacity: 0;
  }
  to {
    transform: scale(1) rotate(var(--tilt-2));
    opacity: 1;
  }
}

/* 힌트 토스트 (§8.5) — 첫 진입 1회 */
.hint-toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: var(--radius-pill);
  box-shadow: var(--pop-shadow-small);
  font-family: var(--font-head);
  font-size: 0.95rem;
  padding: 10px 20px;
  z-index: 40;
  animation: toast-in var(--dur-pop) var(--ease-pop) both;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(10px);
  }
}
