/* ─────────────────────────────────────────────
   조선시대 직업 카드
   주인공은 카드 일러스트다. UI는 조용한 액자 역할만 한다.
   ───────────────────────────────────────────── */

:root {
  --paper: #f2e3ca; /* 바탕 한지 타일(public/ui/paper-bg.webp)의 평균색 — analyze_kit 측정값. 타일이 못 뜰 때의 바탕색 */
  --paper-2: #fbf6ee;
  --sheet: #f6e8c5; /* 결과지 종이면. 테두리 그림(card-frame.webp) 띠 안쪽 색 측정값 — 띠와 이어지게 */
  --ink: #2a2118;
  --ink-2: #4a3d30; /* 본문 두 번째 먹. 이야기 앞 문장(현대 착지 문장보다 한 단계 연하게) */
  --ink-soft: #6b5d4d;
  --line: #d9cbb6;
  --accent: #8a6a3f;
  --accent-soft: #f0e4d0;
  --seal: #a8261e; /* 인주. 인장에만 쓴다 — 버튼·강조에는 쓰지 않는다 */
  --gold: #c99a2e; /* 금색. 카드 아래 능력치의 채운 별에만 쓴다 */
  --kakao: #fee500; /* 카카오 노랑. 카톡 공유 버튼(.btn--kakao)에만 — 카카오 공유 버튼의 관례색이라 브랜드 색으로 예외(명세 15) */
  --kakao-ink: #191919; /* 카카오 노랑 위의 글자색(카카오 지정) */
  --frame-line: #cdc093; /* 결과지 테두리 그림(card-frame.webp)의 녹갈색 괘선 측정색(analyze_kit 팔레트). 받침 판 테두리 */
  /* 결과 카드 패널의 테두리 — 오행별 카드 테두리 띠 측정색(명세 13, 2026-10-08: public/cards-square 60장, 왼쪽 띠 x 8~28·y 500~560 평균, 오행별 12장 평균, 표준편차 ≤3.6).
     카드의 일부인 패널(좌·우·아래 세 변과 안쪽 괘선)에만 쓴다. 패널 밖(강점·약점·이야기·버튼)에는 쓰지 않는다 */
  --edge-wood: #a09161;  /* 목 (160,145,97) */
  --edge-fire: #b7744e;  /* 화 (183,116,78) */
  --edge-earth: #c79950; /* 토 (199,153,80) */
  --edge-metal: #c1ad90; /* 금 (193,173,144) */
  --edge-water: #8b7d68; /* 수 (139,125,104) */
  --card-shadow: 0 10px 26px rgba(42, 33, 24, 0.24); /* 결과 카드와 그 아래 패널이 한 몸으로 드리우는 그림자 */
  --card-shadow-sm: 0 4px 11px rgba(42, 33, 24, 0.24); /* 19: 궁합 카드 두 장(약 140px) — 위 그림자를 카드 크기 비율로 줄인 것 */
  /* 궁합의 실 3색(04 명세). 등급은 실의 상태(팽팽+매듭 / 처짐 / 끊김)가 말하고 색은 거든다. 등급 글자도 같은 색.
     인주색은 실에 쓰지 않는다(인장에만). 신호등 원색 금지 */
  --thread-good: #5f7f43; /* 녹갈색 — 천생연분·찰떡·잘 굴러가는 사이 */
  --thread-mid: #b8862f;  /* 금색 — 무난한 사이 */
  --thread-low: #c2706a;  /* 연지 — 티격태격·결이 다른 사이(인주보다 연하게) */
  --serif: "Gowun Batang", "Noto Serif KR", serif;
  --shadow: 0 2px 0 rgba(42, 33, 24, 0.16);
  --radius: 14px;
  /* 카드 틀 비율(명세 12, 2026-10-08 확정): 결과 카드는 정사각 square(public/cards-square, 1080×1080 전부 같은 크기 — 원본 위쪽 픽셀 그대로, 아래 띠만 닫은 것).
     입력 화면 카드 더미·뽑기 무대·문·개봉 앞면·결과 틀이 모두 이 비율. crop으로 떨어진 카드(1050~1111)는 app.js가 _bounds.json·onload 값을 인라인으로 적는다.
     2:3 그림(cards-scene-big·cards-scene-bg, D 방식 — 기각)은 --card-ratio-23, 3:4 그림(cards·cards-big·cards-bg)은 --card-ratio-34, 각 스타일에서만. */
  --card-ratio: 1 / 1;
  --card-ratio-23: 1080 / 1627;
  --card-ratio-34: 1086 / 1448;
}

/* 오행별 테마. 결과 화면에서 data-element로 갈아끼운다. */
[data-element="WOOD"]  { --accent: #5f7f43; --accent-soft: #e6ecdd; }
[data-element="FIRE"]  { --accent: #b0503c; --accent-soft: #f6e3dc; }
[data-element="EARTH"] { --accent: #9a7a33; --accent-soft: #f3e9d4; }
[data-element="METAL"] { --accent: #6f6c63; --accent-soft: #ecebe6; }
[data-element="WATER"] { --accent: #44607d; --accent-soft: #e0e7ee; }

* { box-sizing: border-box; }

/* .check{display:flex} 같은 선언이 [hidden]을 이기지 못하게 한다. */
[hidden] { display: none !important; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

html {
  -webkit-text-size-adjust: 100%;
  /* 인장이 크게 내려오는 0.5초와 인주 링이 퍼지는 0.5초 동안 오른쪽 가장자리를 9~39px 넘는다(2026-10-05 헤드리스 측정).
     그 순간 가로로 밀리지 않게 뷰포트의 가로 넘침을 막는다. 세로 스크롤은 그대로. */
  overflow-x: hidden;
}

body {
  margin: 0;
  min-height: 100vh;
  /* 바탕은 한지 타일 그림(에셋/바이럴/ui-kit/paper-bg → public/ui, 1024 이음매 없음). 512px로 깔아 2배 화면에서도 결이 잘다 */
  background: var(--paper) url("/public/ui/paper-bg.webp") repeat center / 512px 512px;
  color: var(--ink);
  font-family: Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* 결과 화면 뒤로 아주 느리게 흘러가는 서운. 카드가 떠 있는 하늘처럼 보이게 한다. */
.skycloud {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}

.skycloud.is-on { opacity: 1; }

.skycloud i {
  position: absolute;
  left: -30%;
  width: 160%;
  height: 46%;
  background: url("/public/fx/서운.webp") no-repeat center / 100% 100%;
}

.skycloud i:nth-child(1) { top: 2%; opacity: 0.28; animation: sky-slide 54s linear infinite; }
.skycloud i:nth-child(2) { bottom: -4%; opacity: 0.2; transform: scale(-1); animation: sky-slide 82s linear infinite reverse; }

@keyframes sky-slide {
  from { translate: -6% 0; }
  to   { translate: 12% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skycloud i { animation: none; }
}

/* 종이 질감은 이제 body의 한지 타일 그림이 맡는다(2026-10-06, 03 명세). CSS 점무늬 흉내는 쓰지 않는다.
   요소는 index.html·공유 페이지 템플릿에 남아 있어 그리지만 않는다. */
.grain { display: none; }

.app {
  position: relative;
  z-index: 1;
  max-width: 440px;
  margin: 0 auto;
  padding: 28px 20px calc(40px + env(safe-area-inset-bottom));
}

.screen[hidden] { display: none; }
/* fill은 backwards만: forwards로 남는 (identity) transform이 화면 안 position: fixed(.dock)의 기준을 뷰포트가 아니라 이 section으로 바꿔
   공유 페이지의 "내 카드 받기" 막대가 화면 바닥이 아닌 section 바닥에 붙었다(2026-10-08, 14 검증 중 발견). 끝 상태는 자연 상태와 같아 보이는 건 그대로다 */
.screen.is-active { animation: rise 0.36s ease backwards; }

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

/* ── 입력 ───────────────────────────────── */

.intro { margin: 6px 0 0; }

.intro__title {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(29px, 8.4vw, 33px);
  line-height: 1.3;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* 봉인된 카드: 뒷면 다섯 장을 부채꼴로. 뒷면 무늬는 창호 문살, 가운데 한 장에만 인장.
   움직이지 않는다(움직임은 뽑기 연출과 결과 인장 두 번뿐). */
.sealed {
  position: relative;
  width: 250px;
  height: 124px;
  margin: 18px auto 20px;
}
.sealed__card {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 78px;
  aspect-ratio: var(--card-ratio);
  margin-left: -39px;
  box-sizing: border-box;
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  background-color: var(--paper-2);
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 11px 11px;
  background-position: 5px 5px;
  box-shadow: inset 0 0 0 5px var(--paper-2), inset 0 0 0 6px var(--line);
  transform-origin: 50% 120%;
}
.sealed__card:nth-child(1) { transform: rotate(-22deg); }
.sealed__card:nth-child(2) { transform: rotate(-11deg); }
.sealed__card:nth-child(4) { transform: rotate(11deg); }
.sealed__card:nth-child(5) { transform: rotate(22deg); }
.sealed__card--front { z-index: 1; display: grid; place-items: center; }
.sealed__seal {
  padding: 5px 4px;
  border: 1.5px solid var(--paper-2);
  outline: 1.5px solid var(--seal);
  border-radius: 4px;
  background: var(--seal);
  color: var(--paper-2);
  font-family: var(--serif);
  font-style: normal;
  font-weight: 700;
  font-size: 13px;
  line-height: 1.05;
  writing-mode: vertical-rl;
  transform: rotate(-6deg);
}

.intro__sub {
  margin: 10px 0 0;
  font-size: 14px;
  color: var(--ink-soft);
}

/* 입력 패널도 결과지와 같은 종이 한 장(04 명세): 거친 한지 가장자리 + 녹갈색 괘선 테두리 그림(card-frame.webp, 9-slice), 속은 --sheet.
   956px 그림의 띠 82px을 22px로 깐다. 그림이 못 떠도 --sheet 면과 투명 띠로 화면은 선다 */
.panel {
  background: var(--sheet);
  background-clip: padding-box;
  border: 22px solid transparent;
  border-image: url("/public/ui/card-frame.webp") 82 / 22px / 0 stretch;
  border-radius: 0;
  padding: 4px 2px 6px;
}

.seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-bottom: 20px;
}

.seg__btn {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 10px 0;
  border-radius: 7px;
  font: inherit;
  font-weight: 700;
  font-size: 15px;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.seg__btn.is-on {
  background: var(--ink);
  color: var(--paper-2);
}

.field-label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-soft);
  margin-bottom: 8px;
}

.birth {
  display: block;
  width: 100%;
  box-sizing: border-box;
  appearance: none;
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: 10px;
  padding: 13px 14px;
  font: inherit;
  font-size: 20px; /* 16px 미만이면 iOS가 포커스 때 화면을 확대한다 */
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--ink);
}
.birth::placeholder { color: var(--ink-soft); opacity: 0.55; font-weight: 500; letter-spacing: 0.04em; }
.birth:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.birth.is-error { border-color: var(--ink); background: #fff; }
/* 친구 이름 칸: 숫자 칸과 같은 모양, 글자 간격만 보통으로. 아래 칸과 붙지 않게 띄운다 */
.birth--name { letter-spacing: 0; font-size: 18px; margin-bottom: 14px; }
.field-label small { font-weight: 500; color: var(--ink-soft); }

.birth-read {
  min-height: 20px; /* 문구가 없을 때도 자리를 지켜 화면이 밀리지 않게 */
  margin: 7px 2px 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.birth.is-error + .birth-read { color: var(--ink); text-decoration: underline wavy var(--line); text-underline-offset: 4px; }

/* 궁합 입력은 아직 드롭다운(2단계에서 바꾼다) */
.date-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.date-row select {
  flex: 1;
  min-width: 0;
  appearance: none;
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: 10px;
  padding: 12px 8px;
  font: inherit;
  font-size: 16px; /* 16px 미만이면 iOS가 포커스 시 화면을 확대한다 */
  font-weight: 600;
  color: var(--ink);
  text-align: center;
  cursor: pointer;
}

.date-row select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.date-row select:disabled { opacity: 0.45; cursor: default; }

.unit { font-size: 13px; color: var(--ink-soft); flex: none; }

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 13px;
  color: var(--ink-soft);
  cursor: pointer;
}

.check input { width: 17px; height: 17px; accent-color: var(--accent); }

/* ── 버튼 ───────────────────────────────── */

.btn {
  appearance: none;
  width: 100%;
  border: 0;
  border-radius: 12px;
  padding: 16px 18px;
  font: inherit;
  font-size: 16px;
  font-weight: 800;
  cursor: pointer;
  transition: transform 0.1s, opacity 0.15s;
}

.btn:active { transform: translateY(1px) scale(0.995); }

.btn--primary {
  margin-top: 20px;
  /* 버튼면은 먹색 한지 그림(에셋/바이럴/ui-kit/ink-face → public/ui). 모양(둥근 모서리)만 CSS, 질감은 그림 */
  background: var(--ink) url("/public/ui/ink-face.webp") no-repeat center / cover;
  color: var(--paper-2);
  box-shadow: 0 3px 0 rgba(42, 33, 24, 0.28);
}

.btn--ghost {
  background: var(--paper-2);
  color: var(--ink);
  border: 1.5px solid var(--line);
  font-size: 15px;
  padding: 14px 12px;
}

.btn--line {
  margin-top: 10px;
  background: transparent;
  color: var(--ink);
  border: 1.5px solid var(--ink);
  font-size: 16px;
  padding: 14.5px 18px;
}

/* 카톡 보내기 버튼(명세 15): 카카오 노랑 면에 검정 글자 — 카카오 공유 버튼의 관례색. 다른 어디에도 쓰지 않는다.
   먹색 강조 버튼과 같은 모양(둥글기·높이), 테두리 없음. 눌림은 밝기 92% */
.btn--kakao {
  margin-top: 10px;
  background: var(--kakao);
  color: var(--kakao-ink);
  font-weight: 700;
  border: 0;
}
.btn--kakao:active { filter: brightness(0.92); }

.btn-text {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 10px 6px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.btn-text[hidden] { display: none; }

.btn:focus-visible,
.btn-text:focus-visible,
.seg__btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.btn--dark {
  margin-top: 14px;
  background: var(--accent);
  color: #fff;
  font-size: 15px;
  padding: 14px 18px;
}

.btn--text {
  margin-top: 22px;
  background: transparent;
  color: var(--ink-soft);
  font-weight: 600;
  font-size: 14px;
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 8px;
}

/* ── 개봉 ───────────────────────────────── */

[data-screen="open"] {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 74vh;
  gap: 22px;
}

[data-screen="open"][hidden] { display: none; }

.open__status {
  position: relative;
  z-index: 2;
  margin: 0;
  font-family: "Gowun Batang", serif;
  font-size: 21px;
  color: var(--ink-soft);
}

/* 세월이 거꾸로 갈수록 화면 전체가 누렇게 바랜다 */
.age {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  will-change: opacity; /* 다가가는 동안 매 프레임 바뀐다. 제 층이 없으면 폰에서 화면 전체를 매 프레임 다시 그린다 */
  background:
    radial-gradient(ellipse at 50% 40%, transparent 30%, rgba(92, 64, 30, 0.28) 100%),
    rgba(196, 160, 104, 0.32);
}

/* ── 현판: 연도가 거꾸로 흐르고, 멈추면 시대가 새겨진다 ── */
.plaque {
  position: relative;
  z-index: 2;
  min-width: min(72vw, 268px);
  padding: 8px 18px 7px;
  border-radius: 6px;
  background: linear-gradient(180deg, #4a2f17, #2e1c0c);
  box-shadow: inset 0 0 0 2px #8a6236, inset 0 0 0 5px #2e1c0c, inset 0 0 0 6px #c79a5e, 0 8px 18px rgba(42, 33, 24, 0.35);
  color: #f1d58a;
  text-align: center;
  line-height: 1.2;
}

.plaque__year {
  display: block;
  font-size: clamp(24px, 7.5vw, 30px);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.plaque__era {
  display: block;
  margin-top: 2px;
  font-size: 17px;
  font-weight: 800;
  color: #f6e2b0;
  letter-spacing: 0.04em;
}

/* ── 무대 ── */
.draw {
  --w: min(72vw, 268px);
  position: relative;
  width: var(--w);
  aspect-ratio: var(--card-ratio);
}

.scene {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  overflow: hidden;
  background: linear-gradient(180deg, #151a33 0%, #252b4f 32%, #4a4868 50%, #7a6a6a 56%);
  box-shadow: 0 14px 30px rgba(42, 33, 24, 0.32);
}

/* 별: 점 몇 개를 배경으로 흩뿌린다 */
.scene::before {
  content: "";
  position: absolute;
  inset: 0 0 45%;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, #fff, transparent),
    radial-gradient(1px 1px at 28% 8%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 44% 30%, #dfe8ff, transparent),
    radial-gradient(1px 1px at 63% 12%, #fff, transparent),
    radial-gradient(2px 2px at 81% 22%, #fff7d6, transparent),
    radial-gradient(1px 1px at 90% 40%, #fff, transparent),
    radial-gradient(1px 1px at 7% 42%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 55% 4%, #fff, transparent),
    radial-gradient(1px 1px at 36% 46%, #fff, transparent);
  opacity: 0.85;
}

/* ── 환생: 빛이 번진 자리부터 카드가 피어난다 ── */
.rebirth {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  overflow: hidden;
  background: var(--paper-2);
  box-shadow: 0 14px 30px rgba(42, 33, 24, 0.28);
  clip-path: circle(0% at 50% 52%);
  z-index: 5;
}

.rebirth img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rebirth .inkcard { border-width: 5px; padding: 9px; gap: 7px; }

/* ── 기본 무대(g): 세월의 문 → 문구멍 ──
   창호문 하나가 멀리서 다가오고, 문 위 현판의 연도가 거슬러 오른다.
   문 앞에 멈추면 위쪽 현판에 시대가 새겨진다. 창호지에 문구멍을 뚫으면 그 너머 방에
   봉인된 카드가 비치고, 구멍이 찢어지듯 번져 카드가 드러난다. 인장이 떨어지고 카드가 뒤집힌다. */
.draw[data-mode="g"] { --cy: 50%; }

/* 문은 화면 한가운데, 카드 크기로 선다. */
.gate {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(74vw, 290px);
  aspect-ratio: var(--card-ratio);
  transform-style: preserve-3d;
  will-change: transform, opacity;
  backface-visibility: hidden;
}


.draw[data-mode="g"] .scene { display: none; }

/* 문이 다가오는 어두운 공간. 화면 전체를 덮는다. 뽑기 중에만 켠다(app.js). */
.corridor {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: none;
  opacity: 0;
  overflow: hidden;
  background: radial-gradient(ellipse at 50% 50%, #6b5a46 0%, #3a3026 45%, #241d16 100%);
  /* 그라데이션 밑의 단색. 폰이 이 층의 일부를 미처 못 그린 프레임엔 그 칸을 이 색으로 채운다.
     없으면 투명으로 비어 그 아래 베이지 페이지가 번쩍였다(결과로 넘어가는 순간, 2026-10-04 영상). */
  background-color: #2f271f;
  perspective: 760px;
  perspective-origin: 50% 50%;
  pointer-events: none;
}

.gates {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

/* 발밑 돌길: 바닥에 눕혀 두고 무늬를 카메라 쪽으로 흘려보낸다(app.js moveWorld). 멀수록 어둠에 묻힌다. */
.road {
  position: absolute;
  left: 50%;
  bottom: -2%;
  /* 240%×200%였을 때 이 층(+마스크 층)이 폰 GPU 메모리를 대부분 차지해, 연출 중 다른 층이 밀려나 비어 보였다(2026-10-04).
     눕혀 놓으면 보이는 건 화면 아래쪽뿐이라 150%×120%로도 바닥은 다 덮인다. */
  width: 150%;
  height: 120%;
  overflow: hidden;
  transform: translateX(-50%) rotateX(80deg);
  transform-origin: 50% 100%;
  -webkit-mask-image: linear-gradient(to top, #000 25%, transparent 92%);
  mask-image: linear-gradient(to top, #000 25%, transparent 92%);
  opacity: 0.75;
  backface-visibility: hidden;
}
/* 무늬를 든 바닥판. 위로 한 칸(110px) 더 길게 깔고 app.js moveWorld가 transform으로 한 칸씩 흘려보낸다.
   (배경 위치를 바꾸면 폰에서 매 프레임 바닥 전체를 다시 그려 깜빡였다 — 2026-10-03) */
.road__bed {
  position: absolute;
  left: 0;
  right: 0;
  top: -110px;
  bottom: 0;
  background:
    linear-gradient(90deg, transparent 39%, rgba(30, 22, 14, 0.35) 39.6%, transparent 40.4%, transparent 59.6%, rgba(30, 22, 14, 0.35) 60.4%, transparent 61%),
    repeating-linear-gradient(0deg, rgba(28, 20, 12, 0.5) 0 4px, transparent 4px 110px),
    linear-gradient(90deg, #2e251c 0%, #5b4a37 40%, #67543e 50%, #5b4a37 60%, #2e251c 100%);
  will-change: transform;
}

/* 길가 초롱: 따뜻한 한지 등불. 카메라가 나아가면 양옆으로 스쳐 지나간다(app.js moveWorld). */
.lantern {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 34px;
  border-radius: 9px / 12px;
  border-top: 4px solid #3b2a19;
  border-bottom: 4px solid #3b2a19;
  background: radial-gradient(ellipse at 50% 45%, #ffe2a4 0%, #f2b25c 45%, #b9662a 100%);
  box-shadow: 0 0 22px 6px rgba(255, 176, 88, 0.45), 0 0 60px 18px rgba(255, 150, 60, 0.18);
  opacity: 0;
  will-change: transform, opacity;
  backface-visibility: hidden;
}
.lantern::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 100%;
  width: 1.5px;
  height: 60px;
  margin-bottom: 4px;
  background: rgba(30, 22, 14, 0.8);
}

.gate__frame {
  position: absolute;
  inset: 0;
  border: 7px solid #5a3a1c;
  border-radius: 6px;
  overflow: visible; /* 자르는 건 구멍 틀이 한다. 문틀이 자르면 연출 끝에 풀 때 층이 다시 그려지며 깜빡였다 */
  box-shadow: 0 0 0 3px #8a6236, 0 10px 24px rgba(0, 0, 0, 0.45);
}

/* 창호지: 한지에 문살. 가운데 굵은 살 하나, 나머지는 가는 격자. */
.gate__paper {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, transparent calc(50% - 2px), #5a3a1c calc(50% - 2px) calc(50% + 2px), transparent 0),
    repeating-linear-gradient(90deg, rgba(90, 58, 28, 0.75) 0 2px, transparent 2px 26px),
    repeating-linear-gradient(0deg, rgba(90, 58, 28, 0.75) 0 2px, transparent 2px 26px),
    radial-gradient(ellipse at 50% 42%, #f6ecd6, #e3cfa6);
  background-position: 0 0, 12px 0, 0 12px, 0 0;
}

/* 창호지 너머의 방. 처음엔 보이지 않고, 문구멍 크기만큼만 드러난다(app.js가 clip-path를 키운다). */
.gate__room {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  clip-path: circle(0px at 50% 50%);
}

/* 방의 어둠. 다 찢긴 뒤엔 걷혀서 복도 어둠과 하나가 된다. */
.gate__dark {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, #5a4632 0%, #2c2219 55%, #17110b 100%);
}

/* 문구멍 가장자리: 찢어진 한지 섬유. 둥글지 않게, 가장자리는 보풀처럼 흐리게. */
.gate__tear {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  border: 2px solid rgba(248, 239, 220, 0.95);
  border-radius: 46% 54% 42% 58% / 55% 44% 56% 45%;
  box-shadow: 0 0 3px 2px rgba(240, 226, 196, 0.8), inset 0 0 5px rgba(0, 0, 0, 0.55);
  opacity: 0;
  pointer-events: none;
}

/* 문 너머에 선 사람(내 캐릭터). 자리와 크기는 app.js standCharacter가 px로 넣는다.
   원점을 왼쪽 위로 두어야 "눈을 구멍에 댄다"·"카드 속으로 들어간다" 계산이 맞는다. */
.gate__char,
.gate__shadow {
  position: absolute;
  max-width: none;
  transform-origin: 0 0;
  pointer-events: none;
  user-select: none;
}

.gate__char { z-index: 2; }

/* 창호지에 비친 그림자. 등불 앞에 선 사람처럼 검고 흐리게. 종이 결이 비치게 곱하기로 얹는다. */
.gate__shadow {
  opacity: 0;
  filter: brightness(0) blur(3px);
  mix-blend-mode: multiply;
}

/* 캐릭터가 뛰어들 빈 카드 틀. 그림 칸과 글 칸만 그어 둔 한지 카드 — 들어가면 완성된 카드로 바뀐다.
   비율은 square 기준 1:1(--card-ratio). app.js settleBg가 square 층(cards-square-bg)이면 1080/1080, crop 층(cards-crop-bg)이면 _bounds.json의, D 층(cards-scene-bg)이면 _layout.json의 정확한 w/h를, 3:4 폴백(cards-bg)이면 --card-ratio-34를 인라인으로 적는다. */
.peek-card {
  position: relative;
  z-index: 1;
  width: 84%;
  aspect-ratio: var(--card-ratio);
  opacity: 0;
}

.peek-card__front,
.peek-card__back {
  position: absolute;
  inset: 0;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 0 34px rgba(255, 214, 150, 0.22), 0 10px 22px rgba(0, 0, 0, 0.45);
}

.peek-card__front {
  background: var(--paper-2);
  opacity: 0;
}

.peek-card__front img { width: 100%; height: 100%; object-fit: cover; display: block; }
.peek-card__front .inkcard { border-width: 5px; padding: 9px; gap: 7px; }

.peek-card__back {
  box-sizing: border-box;
  border: 2px solid var(--ink);
  background:
    linear-gradient(var(--line), var(--line)) 12% 79% / 76% 1px no-repeat,
    linear-gradient(var(--line), var(--line)) 12% 87% / 56% 1px no-repeat,
    var(--paper-2);
}

/* 빈 배경 카드(캐릭터만 지운 진짜 카드)가 있으면 빈 틀 대신 그걸 깐다 */
.peek-card__back.has-bg { border: 0; background: var(--paper-2); }
.peek-card__back.has-bg::before { display: none; }
.peek-card__back img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* 그림 칸 */
.peek-card__back::before {
  content: "";
  position: absolute;
  left: 9%;
  right: 9%;
  top: 8%;
  height: 62%;
  border: 1.5px solid var(--line);
  border-radius: 6px;
}

/* 문 위 현판: 그 문이 가리키는 해 */
.gate__plaque {
  position: absolute;
  left: 50%;
  top: -13%;
  transform: translateX(-50%);
  padding: 4px 14px;
  border-radius: 4px;
  background: linear-gradient(180deg, #4a2f17, #2e1c0c);
  box-shadow: inset 0 0 0 1.5px #c79a5e, 0 4px 10px rgba(0, 0, 0, 0.5);
  color: #f1d58a;
  font-size: clamp(15px, 4.6vw, 19px);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  will-change: transform; /* 연도가 매 프레임 바뀐다. 제 층이 없으면 문 전체를 매 프레임 다시 그린다 */
}

/* 결과 화면 도착: 카드가 자리 잡는 동안 나머지는 숨었다가 뒤이어 떠오른다 */
[data-screen="result"] > *,
.hopae > *,
.hopae::before { transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1); }
[data-screen="result"].is-arriving > :not(.hopae),
[data-screen="result"].is-arriving .hopae > :not(.cardframe):not(.cardfx) { opacity: 0; transition: none; }
/* 종이(테두리 그림 + 속색)는 ::before 한 층이라, 카드가 날아드는 동안 opacity로 걷었다가 떠오른다(예전 border-color 트릭 대신, 04 명세) */
[data-screen="result"].is-arriving .hopae::before { opacity: 0; transition: none; }

/* 문 앞 안내. 누르면 바로 열리고, 안 눌러도 저절로 열린다. */
/* 문 아래 큰 안내. 눌러야만 열리니 놓치지 않게 크게, 숨 쉬듯 밝아졌다 흐려진다. */
.open__hint {
  position: absolute;
  z-index: 3;
  left: 16px;
  right: 16px;
  top: 72%; /* 실제 자리는 app.js가 문 바로 아래로 잡는다 */
  margin: 0;
  text-align: center;
  font-family: var(--serif);
  font-size: clamp(22px, 6.4vw, 27px);
  font-weight: 700;
  color: #f6ead2;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.open__hint.is-on { opacity: 1; animation: hint-breathe 1.6s ease-in-out infinite; }
@keyframes hint-breathe { 50% { opacity: 0.55; } }

/* 누를 곳: 그 사람 눈 앞의 창호지에 동그라미가 퍼졌다 사라지길 반복한다 */
.gate__tapmark {
  position: absolute;
  left: 50%;
  top: 45%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  border: 2px solid rgba(90, 58, 28, 0.75);
  pointer-events: none;
  animation: tapmark 1.4s ease-out infinite;
}
@keyframes tapmark {
  0% { transform: scale(0.5); opacity: 0.9; }
  100% { transform: scale(1.8); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .open__hint.is-on, .gate__tapmark { animation: none; } }
.draw.is-tappable { cursor: pointer; touch-action: manipulation; }
/* 폰에서 문을 누를 때 화면만 한 회색 네모(탭 하이라이트)가 떴다(2026-10-03). 누르는 대상이 화면 전체 .draw라서다. */
[data-screen="open"], .draw, .corridor { -webkit-tap-highlight-color: transparent; }

/* 문 밖을 눌렀을 때: 안내 글이 한 번 고개를 젓는다 */
.open__hint.is-nudge { animation: hint-nudge 0.42s ease-in-out; }

@keyframes hint-nudge {
  0%, 100% { translate: 0 0; }
  20% { translate: -7px 0; }
  45% { translate: 6px 0; }
  70% { translate: -3px 0; }
}

.open__skip {
  position: absolute;
  z-index: 3;
  top: 0;
  right: 0;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 8px;
  cursor: pointer;
}

/* ── 결과 ───────────────────────────────── */

/* ── 호패: 결과는 종이 한 장. 구역은 상자가 아니라 먹선과 여백으로 나눈다 ── */
.hopae {
  position: relative;
  isolation: isolate; /* 종이 층(::before, z-index -1)이 호패 밖으로 내려가지 않게 */
  padding: 28px 28px 24px; /* 테두리 띠 22px + 괘선 안쪽 여백 6px(궁합 결과지와 같다) */
}
/* 종이는 그림이다(04 명세, 궁합 결과지와 같은 재료): 거친 한지 가장자리 + 녹갈색 괘선 테두리(card-frame.webp 9-slice), 속은 --sheet.
   호패 자신이 아니라 ::before 한 층에 그린다 — 카드가 날아드는 동안 카드는 두고 종이만 걷었다 떠오르게(opacity). */
.hopae::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border: 22px solid transparent;
  border-image: url("/public/ui/card-frame.webp") 82 / 22px / 0 stretch;
  background: var(--sheet);
  background-clip: padding-box;
  pointer-events: none;
}

/* 머리글은 보조다(명세 10): 카드 안에 제목이 있으니 눈썹·직업명·일주 줄을 바짝 붙인다.
   명세 25: 묶음 전체를 카드와 같은 가운데 축에 세운다(예전엔 왼쪽 정렬 + 오른쪽 인장 자리 104px이라 직업명이 좁은 칸에서 "…밀어붙이는 / 대행수"처럼 한 낱말만 떨어졌다).
   연도 인장은 글줄 옆이 아니라 종이 오른쪽 위 모서리(테두리 띠 위)에 찍는다 — 직업명이 폭 전체(375에서 279px, 60장 중 가장 긴 것 241px)를 한 줄로 쓴다.
   인장 높이만큼 위를 비운다(padding-top). 간격은 한 리듬: 줄 사이 6px, 묶음과 카드 사이 12px(= 6 × 2). 인장이 없는 공유 페이지는 위를 비우지 않는다. */
.hopae__head { position: relative; margin-bottom: 12px; text-align: center; }
.hopae__head:has(> .seal:not([hidden])) { padding-top: 26px; }

.hopae__lead { margin: 0; font-size: 14px; line-height: 1.3; color: var(--ink-soft); }

.hopae__job {
  margin: 6px 0 0;
  font-family: var(--serif);
  font-size: 23px;
  line-height: 1.25;
  font-weight: 700;
  letter-spacing: -0.01em;
  word-break: keep-all;
  text-wrap: balance; /* 더 좁은 폭에서 두 줄로 접힐 때 두 줄 길이를 맞춘다 */
}
/* 320px: 머리 폭 224px — 23px로는 가장 긴 직업명(경인 241px)이 "…밀어붙이는 / 대행수"로 한 낱말만 떨어진다. 21px이면 60장 모두 한 줄(최대 220px) */
@media (max-width: 359px) { .hopae__job { font-size: 21px; } }

.hopae__kicker {
  appearance: none;
  margin: 6px 0 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14px;
  line-height: 1.3;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline dotted;
  text-underline-offset: 4px;
  cursor: pointer;
}

.hopae__gloss {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-soft);
  word-break: keep-all;
  text-wrap: balance;
}
.hopae__gloss[hidden] { display: none; }

/* 인장: 이 화면에서 유일하게 대담한 것. 세로쓰기 두 줄, 살짝 기울어져 찍힌다.
   명세 25: 종이 오른쪽 위 모서리 — 테두리 띠(22px)에 반쯤 걸쳐 찍힌다(옛 문서의 관인이 괘선에 걸치듯). 아래 끝이 직업명 첫 줄보다 위 */
.seal {
  position: absolute;
  top: -26px;
  right: -20px;
  margin: 0;
  display: flex;
  flex-direction: column; /* 낙관처럼 가로 네모: 윗줄 연도, 아랫줄 임금 */
  align-items: center;
  gap: 1px;
  padding: 8px 10px 7px;
  border: 2.5px solid var(--paper-2);
  outline: 2.5px solid var(--seal);
  border-radius: 5px;
  background: var(--seal);
  color: var(--paper-2);
  font-family: var(--serif);
  font-weight: 700;
  font-size: 20px;
  line-height: 1.1;
  transform: rotate(-5deg);
  box-shadow: 0 1px 0 rgba(90, 20, 14, 0.25);
  z-index: 2; /* 인주 링(.seal-ring) 위에 찍힌다 */
}
.seal[hidden] { display: none; }
.seal span { letter-spacing: 0.04em; }
#seal-year { font-size: 22px; letter-spacing: 0.02em; }
#seal-king { font-size: 19px; letter-spacing: 0.25em; margin-right: -0.25em; }
/* 쾅: 위에서 크게 비스듬히 내려와(가속) 60%에서 종이에 닿아 눌렸다가 제 크기로. app.js STAMP_MS·STAMP_HIT와 맞출 것 */
.seal.is-pending { opacity: 0; }
.seal.is-stamped { animation: stamp 0.52s linear both; }
@keyframes stamp {
  0%   { opacity: 0; transform: translateY(-28px) rotate(-16deg) scale(2.4); box-shadow: 0 22px 26px rgba(90, 20, 14, 0); animation-timing-function: cubic-bezier(0.55, 0, 0.9, 0.45); }
  25%  { opacity: 1; }
  60%  { opacity: 1; transform: translateY(0) rotate(-5deg) scale(0.88); box-shadow: 0 1px 0 rgba(90, 20, 14, 0.25); animation-timing-function: cubic-bezier(0.2, 0.8, 0.3, 1); }
  80%  { transform: rotate(-5deg) scale(1.05); }
  100% { opacity: 1; transform: rotate(-5deg) scale(1); }
}
/* 찍히는 순간 튀는 인주 방울 — app.js stampSeal이 만들고 지운다 */
.seal__ink {
  position: absolute;
  left: 50%;
  top: 50%;
  pointer-events: none;
  border-radius: 50%;
}
.seal__ink--dot { background: var(--seal); }
/* 인주 번짐 링: 그림(에셋/바이럴/fx/ink-ring → public/ui). 도장이 내려오기 시작할 때 도장 자리에서 한 번 퍼진다. app.js inkRing */
.seal-ring {
  position: absolute;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background: url("/public/ui/ink-ring.png") no-repeat center / 100% 100%;
  transform: translate(-50%, -50%) scale(0.4);
}

/* 호패 안 구분선은 전부 붓 괘선 그림(divider.png, .rule과 같은 재료). 요소 위쪽에 배경으로 깐다 — 마크업을 늘리지 않는다 */
.hopae__why,
.cardnote__desc,
.pillar-reading {
  border-top: 0;
  background: url("/public/ui/divider.png") no-repeat 50% 0 / 100% auto;
}
.hopae__why {
  margin: 14px 0 0;
  padding-top: 18px;
  padding-right: 44px; /* 오른쪽 아래 낙관(34px, 06) 자리 */
  font-size: 12px; /* 각주 12px — 궁합 각주(.bond__note)와 같은 크기(04 명세) */
  color: var(--ink-soft);
}
.hopae__why[hidden] { display: none; }

/* 카드 아래 "카드에 적힌 것"(02 명세 → 13 명세): 능력치 3줄과 카드 글 4줄은 카드 바로 아래 틈 없이 붙는 패널(.cardpanel) 안에,
   강점·약점은 그 아래 종이 위에. 패널은 예전 3:4 카드의 아래 칸이다 — 카드와 패널이 한 몸으로 읽힌다. */
.cardnote { margin-top: 0; }
.cardnote:empty { display: none; }

/* 패널(명세 13): 카드의 오행별 테두리 띠 색(--edge-*, 측정값)으로 좌·우·아래 세 변을 두른다. 위 변은 카드 그림의 아래 띠가 겸한다.
   한 몸의 비밀은 상자 하나다 — 패널 상자는 카드 꼭대기까지 올라가(margin-top -100%: 정사각 카드라 높이 = 폭 = 담는 상자 폭) 카드 뒤에
   깔리고, 그림자(--card-shadow)도 이 상자 하나가 드리운다. 카드(.cardframe, z-index 1)는 그 위에 얹힌 그림이라 제 그림자가 없다.
   카드 그림은 네 모서리가 둥근 카드가 검은 바탕 위에 그려진 것이라(모서리 밖 쐐기가 불투명 검정) 결과 틀의 12px 둥글기로 아래 모서리도
   잘라내고, 그 쐐기 자리는 뒤의 패널 테두리 색이 채운다 — 카드 띠가 모서리를 돌고 그 바깥은 패널 테두리가 그대로 이어진다.
   속은 크림 종이면(--paper-2), 안쪽 6px 들여 가는 괘선 하나(.cardpanel__in, 띠보다 어두운 같은 계열 — 카드 띠 안쪽 괘선처럼).
   오행색은 이 패널 안에서만 — 패널 밖(강점·약점·이야기·버튼)에는 쓰지 않는다.
   3:4 폴백 카드(data-card-fallback)는 카드가 폭보다 길어 상자가 카드 중간부터 시작하지만, 보이는 부분(카드 아래 모서리 뒤·패널)은 같다. */
.cardpanel {
  --card-edge: var(--edge-wood);
  position: relative;
  margin-top: -100%;
  padding: calc(100% + 6px) 6px 6px; /* 위: 카드 높이 + 괘선까지 6px. 좌·우·아래: 테두리에서 괘선까지 6px */
  border: 9px solid var(--card-edge); /* 카드 아래 띠(그림 안 36px ≈ 375px에서 9px)와 같은 굵기 */
  border-top: 0;
  border-radius: 12px; /* 카드 틀과 같은 둥글기(위 모서리는 카드 뒤에 숨는다) */
  background: var(--paper-2);
  box-shadow: var(--card-shadow);
}
.cardpanel[data-element="목"] { --card-edge: var(--edge-wood); }
.cardpanel[data-element="화"] { --card-edge: var(--edge-fire); }
.cardpanel[data-element="토"] { --card-edge: var(--edge-earth); }
.cardpanel[data-element="금"] { --card-edge: var(--edge-metal); }
.cardpanel[data-element="수"] { --card-edge: var(--edge-water); }
.cardpanel__in {
  padding: 10px 12px 10px;
  border: 1px solid color-mix(in srgb, var(--card-edge) 72%, var(--ink));
  border-radius: 0 0 5px 5px;
}

/* 1 능력치: 라벨 명조 16px + 별 다섯(19px, 명세 14 — 설명이 더 잘 들어오게). 별은 글자 ★☆ 그대로, 채운 별만 금색·빈 별은 괘선색. 카드 안 상자와 같은 순서. */
.cardnote__stats {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 14px;
  row-gap: 6px;
  align-items: baseline;
  margin: 0;
  padding: 0 0 6px;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
}
.cardnote__stats dt { font-weight: 700; }
.cardnote__stats dd { margin: 0; }
.stars { color: var(--line); font-size: 19px; letter-spacing: 2px; }
.stars b { font-weight: inherit; color: var(--gold); }

/* 2 카드 글 4줄: 한 문장씩 한 행. 위에 붓 괘선 하나로 능력치와 나눈다(02의 세로 괘선은 패널 테두리가 대신한다 — 13).
   375px 패널 안폭(237px)에는 글 네 줄이 한 줄씩 들어가지 않아 능력치 위·글 아래 세로 두 단으로 둔다.
   글자는 17px(명세 14: 17.5px에서 접히는 줄이 생겨 17px까지만 줄였다 — 그 아래로는 내리지 않고 접히는 줄은 둔다). */
.cardnote__desc {
  margin: 0;
  padding: 12px 0 0;
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink);
  word-break: keep-all;
}
.cardnote__desc span { display: block; }

/* 3 강점·약점은 14에서 결과 화면에서 뺐다(traits.js 데이터는 그대로). 패널 바로 아래는 "내 일주 이야기" —
   15에서 이 묶음을 종이 위의 한 묶음으로 세웠다: 패널에서 14px 띄우고 위에 붓 괘선(다른 블록과 같은 재료)을 긋는다. */
.cardnote + .pillar-reading { margin-top: 14px; padding-top: 14px; }

/* 행동(명세 15): 먹색은 궁합 하나, 카톡 보내기는 카카오 노랑, 인연 지도는 선 버튼, 나머지는 작은 글자 버튼 */
.actions { margin-top: 18px; }
.actions .btn--primary { margin-top: 0; }
/* 인연 지도 칸: 항상 보인다(bond-map.js refreshEntry). 아직 0명이면 "만들기" — 같은 선 버튼, 색만 연하게(아직 없음을 암시).
   1명 이상이면 먹색 글자, "(N명)"은 갈색 */
#btn-map[data-empty="true"] { color: var(--ink-soft); border-color: var(--ink-soft); }
#map-count { color: var(--accent); }
#map-count:empty { display: none; }
.actions__ask {
  margin: 0 0 10px;
  text-align: center;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 700;
}
.actions__minor { display: flex; justify-content: center; gap: 18px; margin-top: 6px; }
/* 아래에 붙는 막대(명세 14). 장식 없이 한지 바탕 + 먹선 한 줄. 결과 화면에서만(부모가 숨으면 같이 숨는다).
   상태 A: 먹색 "내 일주 이야기 읽기" 하나. 상태 B(.dock--open, 이야기를 한 번 펼친 뒤): 선 "풀이 사전 예약 3,900원"(24) | 먹색 "친구랑 궁합 보기"
   가로 나란히(글 길이만큼 나눔(24), 사이 10px, 최대 400px 가운데) — 강조 먹색은 궁합 하나. 두 상태의 높이는 같다(버튼 한 줄).
   공유 페이지의 막대는 "내 카드 받기"(#dock-get) 하나라 A·B가 없다. */
.dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  padding: 10px 20px calc(10px + env(safe-area-inset-bottom));
  background: var(--paper);
  border-top: 1px solid var(--line);
}
.dock[hidden] { display: none; }
.dock .btn { display: block; max-width: 400px; margin: 0 auto; }
/* 명세 24: 왼쪽 글이 가격까지 길어져("풀이 사전 예약 3,900원") 두 칸을 글 길이만큼 나누고 남는 폭을 반씩 더한다(auto auto) —
   375px에서 왼쪽 약 188·오른쪽 약 137, 320px에서도 둘 다 한 줄. 높이는 그대로 한 줄(A와 같다) */
.dock__pair {
  display: grid;
  grid-template-columns: auto auto;
  gap: 10px;
  max-width: 400px;
  margin: 0 auto;
}
/* 두 버튼이 375px에서 한 줄씩 들어가게 좌우 안쪽 여백만 줄인다. 글자·위아래 여백은 그대로라 높이는 A와 같다 */
.dock__pair .btn { padding-left: 6px; padding-right: 6px; }
@media (max-width: 359px) {
  .dock { padding-left: 12px; padding-right: 12px; }
  .dock__pair .btn { font-size: 15px; }
}
.dock:not(.dock--open) .dock__pair,
.dock--open .dock__reading { display: none; }
/* 상태 전환은 버튼 교체만 — 들어오는 쪽이 150ms 페이드(14의 허용 모션) */
.dock__reading, .dock__pair { animation: dock-in 150ms ease-out; }
@keyframes dock-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .dock__reading, .dock__pair { animation: none; } }
/* 막대가 마지막 문구를 가리지 않게 결과 화면 아래를 비워 둔다 — 막대 실제 높이(app.js updateDock이 --dock-h에 적는다) */
[data-screen="result"] { padding-bottom: var(--dock-h, 76px); }

.actions__note { margin: 8px 0 0; text-align: center; font-size: 13px; color: var(--ink-soft); }
.btn--link { display: block; box-sizing: border-box; text-align: center; text-decoration: none; }

.cardframe {
  width: 100%; /* 명세 11·12: 정사각 카드는 3:4보다 짧아 폭을 02의 100%(375px에서 279px)로 되돌려도 능력치 첫 줄이 첫 화면 안에 든다(10의 95%는 2:3 때만) */
  position: relative;
  z-index: 1; /* 바로 아래 패널(.cardpanel)이 카드 뒤까지 올라와 한 상자가 된다(명세 13) — 카드가 위 */
  margin: 0 auto;
  border-radius: 12px; /* 아래 모서리도 둥글게 둔다: 그림의 모서리 밖 검정 쐐기를 잘라내고 그 자리는 뒤의 패널 테두리 색이 채운다(13) */
  overflow: hidden;
  /* 그림자는 카드+패널 상자(.cardpanel)가 하나로 드리운다(13). 날아드는 동안은 그림자 없이 종이처럼 내려앉고, 패널이 떠오르며 그림자가 생긴다 */
  animation: pop 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}

/* 결과 카드: 손가락을 따라 기울어진다. 기울기 값은 app.js가 --rx/--ry로 넣는다. */
.cardframe.is-tiltable {
  animation: none;
  transform: perspective(760px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
  touch-action: pan-y;
}

@keyframes pop {
  from { opacity: 0; transform: scale(0.94); }
  to   { opacity: 1; transform: none; }
}

/* 틀은 square 기준 1:1(--card-ratio, 명세 12). square는 60장이 다 1080×1080이라 인라인도 1080/1080. crop(1050~1111)·scene-big(약 1627)은 카드마다 높이가 몇 px 다르니
   app.js renderResult가 표(_bounds.json·_layout.json)의 정확한 w/h를 먼저 인라인으로 적고, cardFaceHTML의 onload가 실제 naturalWidth/Height로 덮어 쓴다. scene-big은 표가 오기 전 --card-ratio-23.
   3:4 그림이 들어가는 경우 — big·scene 설정, 또는 그림이 아직 없어 big으로 떨어져 onerror가 data-card-fallback을 심은 카드(인라인은 지운다) — 만 --card-ratio-34. */
.cardframe__inner { aspect-ratio: var(--card-ratio); }
[data-card-style="scene-big"] .cardframe__inner { aspect-ratio: var(--card-ratio-23); }
[data-card-style="big"] .cardframe__inner,
[data-card-style="scene"] .cardframe__inner,
.cardframe__inner[data-card-fallback] { aspect-ratio: var(--card-ratio-34); }

/* 완료 순간 카드 둘레에서 튀는 한지 조각(에셋/바이럴/fx/paper-bits → public/ui, 1024×256 스프라이트).
   층은 화면 폭 전체를 덮고 카드 위아래로 넉넉히 내려온다(app.js scatterBits가 자리를 잰다). 밖으로 나가는 조각은 잘라 가로 스크롤이 안 생기게.
   transform·opacity만 움직인다. */
.cardfx {
  position: absolute;
  z-index: 2;
  pointer-events: none;
  overflow: hidden; /* clip-path는 스크롤 영역을 줄이지 않는다 — 가로 넘침 39px가 났다(2026-10-05 헤드리스 측정) */
  transition: none;
}
.cardfx__bit {
  position: absolute;
  background: url("/public/ui/paper-bits.png") no-repeat;
  will-change: transform, opacity;
}

/* 결과 카드를 누르면 그 카드 오행의 붓 그림 조각이 터진다(명세 16; public/ui/fx-<오행>.webp 띠, 조각 자리는 fx-pieces.json).
   층은 화면 전체를 덮는 고정 층 — 카드 밖으로 나가도 잘리지 않고, 화면 밖은 잘라 가로 스크롤이 안 생긴다.
   z-index는 카드(호패 안)보다 위, 막대(.dock 20)보다 아래. 그림은 app.js burstElement가 조각마다 background로 잘라 넣는다. */
/* 누르는 카드: 폰의 파란 터치 강조·연타 글자 선택·길게 눌러 그림 메뉴·두 번 눌러 확대를 끈다(2026-10-09 사용자: "터치하면 전체가 선택돼 파랗게 바뀐다") */
.cardframe {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
.cardframe img { -webkit-user-drag: none; pointer-events: none; }
/* 궁합 결과의 두 사람도 누르면 터진다(아래) — 같은 처리 */
.bond__stage { -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.bond__char { -webkit-user-drag: none; }
.bond:not(.is-revealing) .bond__side { cursor: pointer; }
.elfx {
  position: fixed;
  inset: 0;
  z-index: 15;
  pointer-events: none;
  overflow: hidden;
}
.elfx__bit {
  position: absolute;
  background-repeat: no-repeat;
  opacity: 0; /* 키프레임(fill backwards)이 들어오기 전 한 프레임도 보이지 않게 */
  will-change: transform, opacity;
}

/* 움직임 줄이기 설정에서는 분위기 층을 아예 걷는다. */
@media (prefers-reduced-motion: reduce) {
}
.cardframe img { width: 100%; height: 100%; display: block; object-fit: cover; }

/* 일러스트 미완성 일주용 먹지 카드. 빈 화면 대신 완결된 카드를 보여준다. */
.inkcard {
  width: 100%;
  height: 100%;
  background: var(--paper-2);
  border: 8px solid var(--accent);
  display: flex;
  flex-direction: column;
  padding: 16px;
  gap: 12px;
}

.inkcard__title {
  margin: 0;
  text-align: center;
  font-size: 19px;
  font-weight: 800;
  border: 2px solid var(--ink);
  border-radius: 8px;
  padding: 8px 6px;
  line-height: 1.25;
}

.inkcard__stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1.5px dashed var(--line);
  border-radius: 8px;
  padding: 12px;
  min-height: 0;
}

.inkcard__hanja {
  font-size: clamp(52px, 17vw, 76px);
  font-weight: 800;
  color: var(--accent);
  line-height: 1;
  letter-spacing: 0.04em;
}

.inkcard__symbol {
  margin: 0;
  font-family: "Gowun Batang", serif;
  font-size: 16px;
  color: var(--ink-soft);
  text-align: center;
}

.inkcard__note {
  margin: 0;
  font-size: 11px;
  color: var(--ink-soft);
  opacity: 0.8;
  text-align: center;
}

.inkcard__bottom {
  display: grid;
  grid-template-columns: 38fr 62fr; /* MASTER 템플릿의 하단 분할 비율을 따른다 */
  gap: 10px;
}

.inkcard__stats { display: flex; flex-direction: column; gap: 6px; }

.inkcard__stat {
  display: flex;
  justify-content: space-between;
  gap: 4px;
  font-size: 12px;
  font-weight: 700;
  border-bottom: 1px dashed var(--line);
  padding-bottom: 4px;
}

.inkcard__stars { color: var(--accent); letter-spacing: -0.04em; }

.inkcard__desc {
  margin: 0;
  font-family: "Gowun Batang", serif;
  font-size: 16px;
  line-height: 1.45;
  border-left: 2px solid var(--line);
  padding-left: 10px;
}

.share { margin-top: 20px; }

/* 내 일주 이야기(명세 21): 읽는 글 — 접힌 채로 요약 + "이어서 읽기", 펼치면 모습 세 줄(점 목록) → 사주로 보면(글 한 문단)
   → 강점·조심할 점 → 이번 주 해 볼 것 → upsell. 흰 상자 없이 종이 위에 바로. 20의 체크·글자 칩은 걷어냈다.
   막대 A "내 일주 이야기 읽기"는 펼치고 묶음 머리로 스크롤(app.js). 옛 이야기(.is-legacy)는 15 그대로 */
.pillar-reading {
  margin-top: 0;
  padding-top: 6px;
  scroll-margin-top: 12px;
}
.pillar-reading[hidden] { display: none; }

/* 머리(15 그대로): 붓 괘선 아래 제목 명조 18px 먹. 이름표 "무료 풀이"는 뺐다(2026-10-08 사용자: "무료라는 단어를 빼 달라") */
.pillar-reading__title {
  display: block;
  margin: 0;
  padding-top: 6px;
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.3;
  font-weight: 700;
  color: var(--ink);
}
/* 요약(첫 문장): 명조 700, 제목 아래 띄워 "제목 → 한 줄 결과"로 읽히게. 완결된 문장이라 끝("…사람이에요")이 잘리면 안 된다.
   명세 27(v5 요약 두 문장): 첫 문장이 29~61자로 길어져 18px 세 줄 말줄임이면 375에서 1개·320에서 24개가 잘렸다.
   말줄임을 없애고(절대 안 잘림) 17px로 — 375에서 60개 모두 3줄 이내(2줄 19·3줄 41), 320은 4줄 14개(말줄임 없이 다 보임) */
.pillar-reading__sum {
  margin: 10px 0 0;
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--ink);
  word-break: keep-all;
}
.pillar-reading__sum:empty { display: none; }
/* 옛 이야기(15): 첫 문장 Pretendard 16px 두 줄 말줄임, 펼치면 숨는다 */
.pillar-reading.is-legacy .pillar-reading__sum {
  margin-top: 4px;
  font-family: inherit;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.pillar-reading.is-legacy:has(details[open]) .pillar-reading__sum { display: none; }
/* 요약의 둘째 문장(명세 27, v5 "그런데 …"): 첫 문장(명조 굵게)이 결론이고 이건 그 다른 면 — 보통 굵기 Pretendard 16px/1.6 두 번째 먹.
   두 문장을 다 굵게 두면 맨 위가 굵은 글씨 네 줄 덩어리가 된다. 말줄임 없음(완결된 문장) */
.pillar-reading__sum-rest {
  margin: 6px 0 0;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.6;
  color: var(--ink-2);
  word-break: keep-all;
}
.pillar-reading__sum-rest[hidden] { display: none; }
/* B 묶음: 둘째 문장과 장면이 같은 글자 모양이라 붙으면 한 문단으로 뭉친다 — 문단 사이만 띄운다 */
.pillar-reading__sum-rest:not([hidden]) + .pillar-reading__scene { margin-top: 12px; }

/* "이 문장 보내기"(명세 22): 요약 바로 아래 오른쪽, 작은 글자 버튼 13px 700 강조 갈색. 밑줄·아이콘 없음.
   글자는 작아도 누르는 자리는 위아래 8px씩 — 요약과 장면 사이의 숨 */
.pillar-reading__send {
  appearance: none;
  display: block;
  margin: 0 0 0 auto;
  padding: 8px 0 8px 12px;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--accent);
  cursor: pointer;
}
.pillar-reading__send[hidden] { display: none; }
.pillar-reading__send:active { opacity: 0.7; }
.pillar-reading__send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* B 묶음의 장면 두 문장: Pretendard 16px/1.6 두 번째 먹 — 요약(명조)이 결론, 이건 그 결론의 한 장면 */
.pillar-reading__scene {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-2);
  word-break: keep-all;
}
.pillar-reading__scene[hidden] { display: none; }

/* "이어서 읽기 ▾"(15의 선 버튼 모양), 펼치면 "접기 ▴" */
.pillar-reading__toggle {
  list-style: none;
  cursor: pointer;
  display: block;
  width: fit-content;
  padding: 10px 0;
}
.pillar-reading__toggle::-webkit-details-marker { display: none; }
.pillar-reading__more {
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding: 0 18px;
  border: 1.5px solid var(--ink);
  border-radius: 12px;
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
}
.pillar-reading__more b { font-weight: inherit; }
.pillar-reading__more b::before { content: attr(data-closed); }
.pillar-reading__more b::after { content: " ▾"; }
[open] > .pillar-reading__toggle .pillar-reading__more b::before { content: attr(data-open); }
[open] > .pillar-reading__toggle .pillar-reading__more b::after { content: " ▴"; }
.pillar-reading__toggle:focus-visible { outline: none; }
.pillar-reading__toggle:focus-visible .pillar-reading__more { outline: 2px solid var(--accent); outline-offset: 2px; }

.pillar-reading__body {
  padding: 4px 0 14px;
  font-size: 15px;
  line-height: 1.75;
  color: var(--ink);
  text-align: left;
}
/* 옛 이야기 세 문단 */
.pillar-reading__body > p { margin: 0 0 12px; }
.pillar-reading__body > p:last-child { margin-bottom: 0; }

/* 펼친 글 ① "이런 모습, 익숙하죠?"(14px 700 두 번째 먹) → 모습 세 줄: 글머리 작은 먹 점, 16px/1.6, 줄 사이 6px */
.pillar-reading__body > .rbody__head.rbody__head--m {
  margin: 0 0 8px;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink-2);
}
.rbody__list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.rbody__list li {
  position: relative;
  padding-left: 16px;
  font-size: 16px;
  line-height: 1.6;
  word-break: keep-all;
}
.rbody__list li + li { margin-top: 6px; }
/* 먹 점: 5px, 둥글기를 조금씩 어긋나게 — 붓끝으로 찍은 점 */
.rbody__list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: calc(0.8em - 2.5px); /* 첫 줄(16px × 1.6)의 가운데 */
  width: 5px;
  height: 5px;
  border-radius: 55% 45% 50% 50% / 50% 55% 45% 50%;
  background: var(--ink);
}
/* ② 사주로 보면(명조 16px 700) → 글 한 문단 16px/1.7. 칩·화살표 없음 */
.pillar-reading__body > .rbody__head {
  margin: 20px 0 6px;
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
}
.pillar-reading__body > .rbody__head:first-child { margin-top: 0; }
.pillar-reading__body > .rbody__text {
  margin: 0;
  font-size: 16px;
  line-height: 1.7;
  word-break: keep-all;
}
/* ③ 강점 / 조심할 점: 두 줄, 줄 앞에 이름표(13px 700 — 강점은 강조 갈색, 조심할 점은 두 번째 먹) */
.rfacts { margin: 16px 0 0; }
.rfacts__row { margin: 0; }
.rfacts__row + .rfacts__row { margin-top: 10px; }
.rfacts dt {
  display: inline;
  margin-right: 6px;
  font-size: 13px;
  font-weight: 700;
}
.rfacts__row--k dt { color: var(--accent); }
.rfacts__row--c dt { color: var(--ink-2); }
.rfacts dd {
  display: inline;
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  word-break: keep-all;
}
/* ④ 이번 주 해 볼 것: 이름표 + 본문, 아래에 붓 괘선 조각(07·17의 전생 꼬리와 같은 재료) — 상자 없음 */
.rweek { margin: 16px 0 0; }
.rweek__label {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--accent);
}
.rweek__text {
  margin: 2px 0 0;
  font-size: 16px;
  line-height: 1.6;
  word-break: keep-all;
}
.rweek::after {
  content: "";
  display: block;
  width: 40px;
  height: 7px;
  margin: 14px 0 0;
  background: url("/public/ui/divider.png") no-repeat -8px 50% / 171px auto;
}

/* ⑤ 다 읽은 뒤 "이 풀이, 나랑 맞나요?"(명세 22): 질문 14px 700 두 번째 먹 + 작은 선 버튼 둘(15의 선 버튼을 높이 36으로 줄인 것).
   누르면 버튼 둘이 "알려 줘서 고마워요"(13px 보조 먹)로 — 줄 높이는 36 그대로 둬서 아래 upsell이 튀지 않게 */
.rfeel { margin: 6px 0 0; }
.rfeel[hidden] { display: none; }
.rfeel__q {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink-2);
}
/* 명세 25: 버튼 줄(36px)은 누른 뒤에도 남고, 다리 한 줄은 그 아래 자리(14px × 1.5, 두 줄까지 = 42px)를 처음부터 차지한다 — 누를 때 아래 upsell이 튀지 않는다 */
.rfeel__row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.rfeel__btn {
  appearance: none;
  height: 36px;
  padding: 0 16px;
  border: 1.5px solid var(--ink);
  border-radius: 10px;
  background: transparent;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
}
.rfeel__btn:active { transform: translateY(1px); }
.rfeel__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 고른 쪽: 먹 바탕 + 종이 글자(눌린 도장처럼). 다른 쪽은 그대로 선 버튼 — 바꿔 누를 수 있다 */
.rfeel__btn[aria-pressed="true"] { background: var(--ink); color: var(--paper-2); }
.rfeel__thanks {
  min-height: 42px;
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
  word-break: keep-all;
  text-wrap: balance;
}
.rfeel + .upsell { margin-top: 10px; }

.share .btn--primary,
.share .btn--kakao { margin-top: 0; }

/* 궁합 결과 맨 아래 글자 버튼 둘(명세 15): "다른 사람이랑 보기 | 내 카드 다시 입력" 한 줄 가운데, 사이 가는 세로 괘선 14px */
.pair-end {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 22px;
}
.pair-end .btn--text { position: relative; width: auto; margin-top: 0; padding: 8px 14px; }
.pair-end .btn--text + .btn--text::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 14px;
  background: var(--line);
  transform: translateY(-50%);
}

.share__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); /* 카카오 키가 없으면 "링크 복사"가 숨어 "이미지 저장" 하나가 전체 폭 */
  gap: 10px;
  margin-top: 10px;
}
.share__row > [hidden] { display: none; }
/* 궁합 결과: 창호지가 아직 덮여 있는 동안(.bond.is-closed)은 아래 버튼이 전부 없다 — 터져서 점수가 찍힌 뒤에 선다(2026-10-08 사용자) */
.bond.is-closed ~ .share,
.bond.is-closed ~ #btn-pair-map,
.bond.is-closed ~ .pair-end { display: none; }
#btn-pair-map[hidden] { display: none; }
/* 궁합 결과의 먹색 자리는 인연 지도(2026-10-08 사용자: "궁합 결과에서는 인연 지도를 강조"). 카톡 보내기는 노랑 그대로 */
#btn-pair-map { margin-top: 20px; }
#btn-pair-map + .share { margin-top: 10px; }

.upsell {
  margin: 4px 16px 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.upsell__lead { margin: 0; font-size: 15px; font-weight: 800; }
.upsell__lead strong { color: var(--accent); }
.upsell__body { margin: 6px 0 0; font-size: 14px; color: var(--ink-soft); }
/* 명세 24: 가격 버튼("전체 풀이 3,900원 · 사전 예약") 바로 아래 한 줄 — 결제가 아직이고 채널 친구 가격이 따로 있다는 것만 */
.upsell__note {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-soft);
  word-break: keep-all;
  text-wrap: balance; /* 두 줄로 접힐 때 "1,900원." 한 낱말만 떨어지지 않게 */
}
/* 320px에서는 가격 버튼이 두 줄 — 낱말 가운데("사/전")에서 끊기지 않게 */
.upsell .btn { word-break: keep-all; }
/* 명세 25: 보조 "초대 코드가 있어요" — 선 버튼(주)보다 약한 글자 링크. 테두리·바탕 없이 밑줄만, 누르는 자리는 위아래 10px(높이 약 40) */
.upsell__invite {
  display: block;
  width: fit-content;
  margin: 2px auto 0;
  padding: 10px 6px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  -webkit-tap-highlight-color: transparent;
}
.upsell__invite:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.fineprint {
  margin: 18px 0 0;
  text-align: center;
  font-size: 11px;
  color: var(--ink-soft);
  opacity: 0.75;
}

.fineprint--tight { margin-top: 12px; }
.panel .fineprint { text-align: left; }

/* ── 토스트 ─────────────────────────────── */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom));
  transform: translate(-50%, 14px);
  background: var(--ink);
  color: var(--paper-2);
  padding: 12px 20px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s, transform 0.22s;
  z-index: 50;
  max-width: calc(100vw - 40px);
  text-align: center;
}

.toast.is-on { opacity: 1; transform: translate(-50%, 0); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── 궁합 ───────────────────────────────── */

.intro__sub b { color: var(--ink); }

/* 궁합 한 장(호패식, 03 명세): 두 사람 → 제목 → 이야기 → 괘선 → 역할 표 → 각주를 종이 한 장에 담는다. 점수·등급은 오른쪽 위 인장.
   종이는 그림이다: 가장자리가 거친 한지 한 장 + 녹갈색 괘선 테두리(에셋/바이럴/ui-kit/card-frame → public/ui, 9-slice).
   956px 그림의 띠 82px을 22px로 깐다(괘선 4px → 1px). 띠 안쪽 색 --sheet로 속을 채워 이음새가 없다.
   상자를 겹겹이 쌓지 않고, 구역은 괘선 그림 하나로만 나눈다(결과 화면의 호패와 같은 원칙). */
.bond {
  position: relative;
  padding: 6px 6px 12px;
  border: 22px solid transparent;
  border-image: url("/public/ui/card-frame.webp") 82 / 22px / 0 stretch;
  border-radius: 0;
  background: var(--sheet);
  background-clip: padding-box; /* 띠 바깥의 거친 가장자리 아래로 속색이 비치지 않게 */
  text-align: center;
  --gap: 8%;
}

/* 등급은 실의 상태(팽팽+매듭 / 처짐 / 끊김)가 말하고, 색은 3색(--thread-*)이 거든다(04 명세). 등급 글자도 같은 색.
   두 사람 사이 거리도 등급을 따라 조금 달라진다(점수보다 먼저 읽힌다). */
.bond[data-tone="best"]  { --thread: var(--thread-good); --gap: 6%; }
.bond[data-tone="good"]  { --thread: var(--thread-good); --gap: 9%; }
.bond[data-tone="mid"]   { --thread: var(--thread-mid); --gap: 12%; }
.bond[data-tone="low"]   { --thread: var(--thread-low); --gap: 16%; }
.bond[data-tone="worst"] { --thread: var(--thread-low); --gap: 20%; }
/* 조각을 맞춰 보기 전(봉인): 등급을 알 수 없다. 거리는 중간, 둘은 마주 본다. 실·인장은 없다 */
.bond[data-tone="sealed"] { --thread: var(--ink); --gap: 12%; }

.bond__stage {
  position: relative;
  display: flex;
  justify-content: center;
  gap: var(--gap);
  align-items: flex-start; /* 머리 위 이름표를 같은 높이에 둔다. 그림 칸이 모두 같은 비율(4:5)이라 발·받침도 맞는다 */
  /* 06 명세: 맨 윗줄은 "나 · [점수 인장] · 지현". 이름표(알약, --who-h)와 인장(--seal-h)의 세로 중심을 맞춘다 — 인장이 top: 0에 앉으니
     이름표 줄을 그 차이의 절반만큼 내린다 */
  --who-h: 24px;
  --seal-h: 46px;
  padding-top: calc((var(--seal-h) - var(--who-h)) / 2);
  cursor: default;
  touch-action: manipulation; /* "?"를 두 번 빨리 눌러도 화면이 확대되지 않게 */
  /* 받침 줄 높이(두 받침 판의 세로 가운데). pair-ui.js가 잰 값을 넣는다. 실은 이 높이에서 두 받침의 안쪽 끝을 잇는다.
     06부터 인장은 여기 앉지 않는다(이름표 사이로 올라갔다) — 처진 실·매듭·끊긴 끝이 전부 보인다 */
  --thread-y: 196px;
}

/* 터져 나오는 조각은 종이 한 장 밖으로는 나가지 않는다(폰에서 가로 스크롤이 생기지 않게) */
.bond { overflow: hidden; }

/* 실이 이어지는 동안: 등급이 드러나는 순간 두 사람이 다가서거나 물러서고(앙숙은 돌아선다) — 봉인이 풀린 뒤에만 서서히 */
.bond.is-revealing .bond__stage { cursor: pointer; transition: gap 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
.bond.is-revealing .bond__char { transition: transform 0.55s ease-in-out; }
/* 봉인된 종이는 무대뿐이다 — 인장·제목·이야기·표·각주는 터진 뒤 점수가 찍힐 때 선다(빈자리가 "기다리는 중"처럼 보이지 않게) */
.bond.is-closed .bond__seal,
.bond.is-closed .bond__thread,
.bond.is-closed .pair-tale,
.bond.is-closed .bond__rule,
.bond.is-closed .pair-roles,
.bond.is-closed .bond__note,
.bond.is-closed .nakgwan { display: none; }

/* 05 명세: 각 사람의 칸은 무대 폭의 44%. 두 칸 + 등급별 틈이 무대를 넘치면(low·worst) 칸이 줄어들지 틈은 줄지 않는다 — 거리가 점수보다 먼저 읽힌다 */
.bond__side {
  position: relative;
  z-index: 1;
  margin: 0;
  flex: 0 1 44%;
  min-width: 0;
  max-width: 170px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* 그림 칸은 칸 폭 가득, 비율 4:5로 고정(06. 05까지는 3:4). 그림은 그 안에서 발을 바닥에 붙이고 선다(object-position 아래) —
   폭이 다른 60장이 와도 머리 위 이름표·발밑 받침이 같은 높이에 놓이고, 그림이 뜨기 전에도 칸 크기가 흔들리지 않는다.
   칸 위 여백(margin-top): 이름표 사이에 앉은 인장(46px + 외곽선)과 그 아래 등급명(14px 명조, 한 줄)이 캐릭터 머리에 닿지 않을 만큼.
   그림(777×900)이 4:5 칸보다 넓어 머리 위에 칸 폭의 9%가 비므로, 칸 자체는 등급명 바로 아래부터 시작해도 된다 */
.bond__char {
  position: relative;
  z-index: 1; /* 받침 판 위에 선다 */
  display: block;
  width: 100%;
  height: auto;
  margin-top: 30px; /* 등급명 상자(14px×1.3) 아래로 칸이 시작하게 — 등급명·인장과 캐릭터 칸의 교차 0px(06 확인 항목) */
  aspect-ratio: 4 / 5;
  object-fit: contain;
  object-position: 50% 100%;
}

/* 받침 판(04 명세): 각 사람 발밑의 살짝 둥근 종이 조각. 나는 먹색 한지(버튼면과 같은 결), 친구는 크림 한지(결과지 속색)에 녹갈색 가는 테두리.
   그림의 발이 6px 겹쳐 그 위에 "서 있는" 느낌. 봉인 상태에서도 있다(색은 터진 뒤에 입는다 — 그림자와 같은 규칙) */
.bond__base {
  display: block;
  width: 110%; /* 그림 칸보다 1.1배 넓다(04·05). 칸 밖으로 5%씩 나간 만큼 음수 여백으로 흐름을 맞춘다 */
  height: 16px;
  margin: -6px -5% 0;
  border-radius: 46% 54% 50% 50% / 100% 100% 100% 100%;
  background: var(--sheet);
  border: 1px solid var(--frame-line);
  box-shadow: 0 1px 0 rgba(42, 33, 24, 0.12);
  transition: filter 0.55s ease, opacity 0.3s;
}
.bond__side--a .bond__base {
  border-color: var(--ink);
  background: var(--ink) url("/public/ui/ink-face.webp") no-repeat center / cover;
}
.bond.is-sealed .bond__base { filter: brightness(0) blur(2px); opacity: 0.4; }

/* 서로 마주 본다(오른쪽 사람을 뒤집는다). 앙숙은 서로 등을 돌린다. */
.bond__side--b .bond__char { transform: scaleX(-1); }
.bond[data-tone="worst"] .bond__side--a .bond__char { transform: scaleX(-1); }
.bond[data-tone="worst"] .bond__side--b .bond__char { transform: none; }

/* ── 19 명세: 터지면 각자의 카드가 펼쳐진다 ──────────────────────────
   그림 칸(.bond__frame)이 예전 .bond__char의 자리(칸 폭 100%, 4:5, 위 여백 30px — 등급명 아래)를 맡고, 캐릭터는 그 안을 채워 발을 바닥에 붙인다.
   카드(.bond__card)는 칸 위쪽 정사각(칸 폭 100%, 375px에서 약 140px) — 이름표·인장·등급명 아래에서 시작한다(교차 0px은 그대로).
   카드 모드(.bond.has-cards — pair-ui.js가 그림 넷과 card-fit 자리를 다 받았을 때만)에서는 칸 아래 1/4(4:5 − 1:1)이 접혀 한마디가 카드 바로 아래로 오고,
   받침 판은 사라진다(카드가 바닥이다). 캐릭터는 뒤집지 않는다(카드 그림 속 방향 그대로) — 앙숙의 "등 돌림"은 두 카드가 바깥으로 3° 기울기.
   .is-carded = 완성 카드(public/cards-square)가 위에 올라오고 캐릭터 층은 숨는다.
   펼쳐지는 동안만(.is-revealing) 접힘·기울기가 펼침과 같은 420ms·같은 곡선으로 움직인다. 바로 보기는 처음부터 끝 모습. */
.bond__frame {
  position: relative;
  z-index: 1; /* 받침 판 위 */
  width: 100%;
  aspect-ratio: 4 / 5;
  margin-top: 30px; /* .bond__char의 것과 같다 — 등급명 상자 아래로 칸이 시작하게 */
  transform-origin: 50% 40%; /* 카드 가운데(칸 높이 1.25 중 0.5) — 앙숙 기울기의 축 */
}
.bond__frame > .bond__char {
  position: absolute;
  inset: 0;
  height: 100%;
  margin: 0;
  aspect-ratio: auto;
}
.bond__card {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 8px;
  overflow: hidden;
  visibility: hidden;
  box-shadow: 0 0 0 rgba(42, 33, 24, 0); /* 카드가 서면 --card-shadow-sm. 같은 모양이라 펼쳐지는 동안 짙어진다 */
}
/* 오른쪽 카드는 붓질이 오→왼: 틀째 좌우를 뒤집고, 그림은 다시 뒤집어 제 방향 */
.bond__card-turn { position: absolute; inset: 0; }
.bond__side--b .bond__card-turn,
.bond__side--b .bond__card-in > img { transform: scaleX(-1); }
/* 붓질 닦아내기 — 결과 화면 .peek-card__wipe/__in과 같은 구조: 기울어진 틀(카드 폭 2배, 위아래 20% 여유)이 미끄러지며 드러내고 안쪽은 거꾸로 움직여 그림은 제자리.
   둘의 원점이 같아야(0 0) 상쇄된다. 움직임은 pair-ui.js unfoldCards(WAAPI, transform만) */
.bond__card-wipe {
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: -50%;
  right: -50%;
  overflow: hidden;
  transform-origin: 0 0;
}
.bond__card-in { position: absolute; inset: 0; transform-origin: 0 0; }
.bond__card-in > img {
  position: absolute;
  left: 25%;
  top: calc(100% / 7); /* 틀 높이 1.4 중 0.2 */
  display: block;
  width: 50%;
  height: calc(500% / 7);
  object-fit: cover;
}
.bond__card-face { opacity: 0; }
/* 실의 끝: 카드 안쪽 모서리, 세로 60% */
.bond__pin { position: absolute; top: 60%; width: 0; height: 0; }
.bond__side--a .bond__pin { right: 0; }
.bond__side--b .bond__pin { left: 0; }

.bond.has-cards .bond__card { visibility: visible; box-shadow: var(--card-shadow-sm); }
.bond.has-cards .bond__frame { margin-bottom: -25%; } /* 칸 폭의 25% = 4:5 칸에서 정사각 카드 아래 남는 몫 */
.bond.has-cards .bond__base { height: 0; margin-top: 0; border-width: 0; box-shadow: none; opacity: 0; }
.bond.has-cards[data-tone] .bond__side .bond__char { transform: none; }
/* 기울면 안쪽 위 모서리가 칸 폭의 2.6%(약 3px) 올라간다 — 등급명과 닿지 않게 그만큼 칸을 내린다(교차 0px) */
.bond.has-cards[data-tone="worst"] .bond__frame { margin-top: 34px; }
.bond.has-cards[data-tone="worst"] .bond__side--a .bond__frame { transform: rotate(-3deg); }
.bond.has-cards[data-tone="worst"] .bond__side--b .bond__frame { transform: rotate(3deg); }
.bond.is-carded .bond__card-face { opacity: 1; }
.bond.is-carded .bond__char { opacity: 0; }
.bond.is-revealing.has-cards .bond__frame { transition: margin 0.42s cubic-bezier(0.45, 0, 0.2, 1), transform 0.42s cubic-bezier(0.45, 0, 0.2, 1); }
.bond.is-revealing.has-cards .bond__base { transition: height 0.42s cubic-bezier(0.45, 0, 0.2, 1), margin 0.42s cubic-bezier(0.45, 0, 0.2, 1), opacity 0.2s; }
.bond.is-revealing.has-cards .bond__card { transition: box-shadow 0.3s ease-out 0.2s; }
.bond.is-revealing.has-cards .bond__char { transition: filter 0.3s ease, opacity 0.3s; } /* 그림자가 카드 속에 들어가기 전에 색을 다 입게 */

/* 누가 나고 누가 친구인지: 두 사람 머리 위의 작은 이름표 */
.bond__who {
  margin-bottom: 4px;
  padding: 2px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper);
  font-size: 12px;
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap;
}
.bond__side--a .bond__who { border-color: var(--ink); background: var(--ink); color: var(--paper-2); }
.bond__who[hidden] { display: none; }
/* 긴 이름표(공유 페이지의 "보낸 사람/받은 사람", 세 글자 이상의 이름): 375px에서 가운데 인장(폭 약 100px)과 닿아, 칸의 바깥쪽으로 붙인다.
   pair-ui.js renderPair가 이름표 길이를 보고 is-who-wide를 켠다. 짧은 "나/지현"은 머리 위 가운데 그대로 */
.bond.is-who-wide .bond__side--a .bond__who { align-self: flex-start; }
.bond.is-who-wide .bond__side--b .bond__who { align-self: flex-end; }

/* ── 23 명세: 봉인(창호지) 상태는 "한 사람 = 한 기둥" — 이름표 → 그림자 → 직업명이 칸 가운데 한 줄로 선다 ──
   이름표: 15px 800, 높이 28px. 두 그림자 중 더 높은 머리 바로 위 8px, 두 이름표는 같은 줄. 내려앉는 거리(--who-drop)는
   pair-ui.js placeWho가 그림 자리를 재서 무대에 넣는다(transform만 — 흐름 높이는 터진 뒤 이름표(12px, 줄 1.55 + 위아래 2 + 선 1 + 아래 4)와 같아
   그림 칸이 봉인 ↔ 결과 사이에 움직이지 않는다). 칸보다 넓은 이름(.is-who-tight)은 13px. 긴 이름도 봉인 중엔 칸 가운데(is-who-wide 안 씀).
   직업명: 받침 타원 아래 10px(받침의 흐림 2px을 빼면 눈으로 8px), 13px 700 --ink-2, 칸 가운데, 두 줄까지 고르게 접는다.
   창틀(무대 테두리 2px) 안쪽 아래 선과 10px 이상 — 모자라면 무대(창)가 늘어난다. 그림자 크기는 그대로 */
.bond.is-sealed .bond__stage { padding-bottom: 12px; }
.bond.is-sealed .bond__side .bond__who {
  align-self: center;
  height: 28px;
  max-width: 100%;
  margin-bottom: calc(12px * 1.55 + 2px * 2 + 1px * 2 + 4px - 28px);
  padding: 5px 14px;
  font-size: 15px;
  line-height: 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  transform: translateY(var(--who-drop, 0px));
}
.bond.is-sealed .bond__side .bond__who.is-who-tight { font-size: 13px; padding: 5px 10px; }
.bond.is-sealed .bond__side figcaption {
  align-self: center;
  max-width: calc(100% - 12px); /* 칸 끝 = 창틀 선이라 양쪽 6px 안으로 */
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.3;
  color: var(--ink-2);
  text-wrap: balance;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
/* 터질 때 이름표는 인장 양옆 제자리로 간다 — 위치를 움직이지 않고 160ms 사이에 새 자리에서 떠오른다(바꿔 끼우기) */
.bond.is-revealing:not(.is-sealed) .bond__who { animation: who-swap 160ms ease-out both; }
@keyframes who-swap { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .bond.is-revealing:not(.is-sealed) .bond__who { animation: none; }
}

.bond__side figcaption {
  /* 06 명세: 인장이 위로 갔으니 직업명은 캐릭터(받침) 바로 아래. 처진 실(11px)만 받침 아래로 지나간다 */
  margin-top: 8px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink-soft);
  word-break: keep-all;
}

/* 창호지: 터뜨리기 전엔 무대가 한지 한 장으로 덮여 있고, 두 사람은 그 앞에 그림자로 선다(뽑기의 문과 같은 장면).
   가운데 먹으로 쓴 "?"가 누를 곳. 누르면 pair-ui.js가 "?" 자리에서 clip-path로 찢어 낸다. */
.bond__paper {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 10px;
  background:
    repeating-linear-gradient(90deg, rgba(90, 58, 28, 0.32) 0 1.5px, transparent 1.5px 28px),
    repeating-linear-gradient(0deg, rgba(90, 58, 28, 0.32) 0 1.5px, transparent 1.5px 28px),
    radial-gradient(ellipse at 50% 40%, #f7eed9, #e6d3ab);
  background-position: 13px 0, 0 13px, 0 0;
  box-shadow: inset 0 0 0 2px #8a6236;
}

.bond__mark {
  position: absolute;
  left: 50%;
  /* 06 명세: "?"는 무대 세로 가운데(두 사람 사이). 터지면 인장이 여기서 위 이름표 사이로 날아올라 찍힌다(pair-ui.js stampBond) */
  top: 50%;
  transform: translate(-50%, -50%) rotate(-6deg);
  font-family: var(--serif);
  font-size: 88px; /* 무대가 커진 만큼(05) */
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  animation: mark-breathe 1.8s ease-in-out infinite;
}

@keyframes mark-breathe {
  0%, 100% { opacity: 0.72; }
  50% { opacity: 1; }
}

.bond.is-sealed .bond__stage { cursor: pointer; }

/* 종이 앞의 그림자: 등불 앞에 선 사람처럼 검고 흐리게. 종이가 터지면 색을 입는다. */
.bond__char { transition: filter 0.55s ease, opacity 0.3s; }
.bond.is-sealed .bond__char { filter: brightness(0) blur(2.5px); opacity: 0.62; }
/* 친구는 걸어 들어오기 전까지 보이지 않는다(먼저 보였다가 다시 등장하지 않게) */
/* 두 사람 다 페이드인 전에는 숨긴다. "나"만 CSS로 보이다가 그림 디코드 뒤 0에서 다시 떠올라 대기 화면이 꼭 한 번 깜빡였다(2026-10-09) */
.bond.is-revealing .bond__side--a,
.bond.is-revealing .bond__side--b { opacity: 0; }

/* 터져 나오는 것들: 한지 조각, 하트·꽃잎·먹물·번개. 자리는 pair-ui.js가 "?" 자리에 놓고 날린다. */
.bond__burst {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: visible;
  pointer-events: none;
}

.burst__bit {
  position: absolute;
  display: block;
  transform-origin: center;
  will-change: transform, opacity;
}

.burst__bit svg { display: block; width: 100%; height: 100%; }
.burst__bit--heart svg { filter: drop-shadow(0 1px 0 rgba(42, 33, 24, 0.25)); }
.burst__bit--ink svg { filter: blur(0.4px); }
.burst__bit--shard {
  background: linear-gradient(180deg, #f7eed9, #e6d3ab);
  border: 1px solid rgba(90, 58, 28, 0.45);
  border-radius: 2px 5px 3px 6px;
}

.burst__flash {
  position: absolute;
  width: 220px;
  height: 220px;
  border-radius: 50%;
  mix-blend-mode: screen;
}

/* ── 실(04·05 명세): 두 받침 판의 안쪽 끝을 받침 가운데 높이에서 잇는 SVG path(05에서 손 높이 → 발밑으로). 굵기 4px. 색은 등급 3색.
   best·good: 팽팽한 직선 + 가운데 매듭 / mid: 느슨하게 처진 곡선, 매듭 없음 / low·worst: 가운데가 끊어져 두 끝이 풀렸다.
   path는 pair-ui.js drawThread가 두 사람의 자리를 재서 넣고, 터지는 순간 양 끝에서 가운데로 그려진다(stroke-dashoffset 400ms, 한 번). */
.bond__thread {
  position: absolute;
  inset: 0;
  z-index: 2; /* 손에 쥔 실이라 그림 앞 */
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.bond__thread path {
  fill: none;
  stroke: var(--thread);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.bond__thread .thread__fray { stroke-width: 2; }
.bond__thread .thread__tail { stroke-width: 2.5; }
.bond__thread .thread__knot { fill: var(--thread); stroke: none; }
.bond__thread .thread__half { stroke-dasharray: 1; stroke-dashoffset: 0; } /* pathLength=1. 그리는 동안 pair-ui.js가 1→0 */
.bond.is-sealed .bond__thread { display: none; }

/* ── 점수 인장(06 명세): 무대 맨 위, "나"와 "지현" 이름표 사이 가운데(VS 배지 자리). 숫자 34px + "점" 12px, 폭 약 100px, 높이 46px, 인주색, -5도.
   결과 화면의 .seal과 같은 꼴. 터질 때 무대 가운데에서 여기로 날아올라 찍힌다(pair-ui.js stampBond, WAAPI 320ms) + 인주 번짐 링(.seal-ring).
   등급명은 인장 바로 아래 명조 14px, 등급 3색. 실·매듭·끊긴 끝은 이제 아무것도 가리지 않는다 */
.bond__seal {
  position: absolute;
  left: 50%;
  top: 0; /* 인장 세로 중심(23px) = 이름표 세로 중심(무대 padding-top 11px + 12px) */
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  transform: translateX(-50%);
  pointer-events: none;
}

.bond__stamp { position: relative; display: inline-block; }

.seal--bond {
  position: relative;
  top: auto;
  right: auto;
  flex-direction: row;
  justify-content: center;
  align-items: baseline;
  gap: 3px;
  min-width: 90px; /* 폭 약 100px(테두리 포함). 두 이름표 사이에 들어가야 해서 더 넓히지 않는다 — 공유 페이지의 "보낸 사람/받은 사람"도 */
  padding: 4px 8px 3px;
  font-size: 34px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.seal--bond small { font-size: 12px; font-weight: 700; }

/* 인주 번짐 링: 인장 한가운데서 인장 폭의 1.3배로 퍼진다. 인장의 변형(크게 내려오기)을 따라가지 않게 .bond__stamp에 둔다 */
.bond__ring {
  left: 50%;
  top: 50%;
  width: 130%;
  aspect-ratio: 1;
}

.bond__grade {
  margin: 5px 0 0; /* 인장 바로 아래(외곽선 2.5px 아래). 캐릭터 칸은 그 아래부터(.bond__char margin-top) */
  padding: 0;
  font-family: var(--serif);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--thread);
  white-space: nowrap;
}
.bond__grade:empty { display: none; }

/* 브랜드 낙관(06 명세): 두 종이(궁합 결과지·결과 화면 호패) 오른쪽 아래 모서리 안쪽, 각주 줄 오른쪽 끝. 34×34, 살짝 -3도.
   그림은 에셋/바이럴/ui-kit/brand-seal-sajucow → public/ui/brand-seal.png(512 투명, 인주색 네모에 한글 "사주카우" 2×2, 명세 25). 인주색은 인장·낙관에만 */
.nakgwan {
  position: absolute;
  right: 8px;
  bottom: 6px;
  width: 34px;
  height: 34px;
  background: url("/public/ui/brand-seal.png") no-repeat center / contain;
  transform: rotate(-3deg);
  pointer-events: none;
}
.hopae .nakgwan { right: 26px; bottom: 21px; } /* 호패는 테두리 22px이 ::before 그림이라 안쪽으로 그만큼 */

/* 제목이 주인공: 눈썹 "전생에 둘은" + 이야기 제목(명조). 그 아래 이야기는 한 문장 한 줄. */
.pair-tale { margin-top: 12px; }

/* 상황 꼬리표(08): 머리말 "지금 둘은" 위 한 줄, 고유 글의 상황 이름("막차 시간이 다가올 때"). 12px 보조 먹, 가운데.
   양옆에 붓 괘선 가운데 토막(divider.png 1536×64의 x 560~976, y 24~40)을 18×4px로 잘라 연하게 — 07 꼬리 장식과 같은 재료, 새 그림 없음.
   머리말과 한 줄로 합치지 않는다: 상황 이름이 길면(최대 14자) "지금 둘은"까지 한 줄에 안 들어가 꺾이고, 머리말→제목의 짝(호패와 같은 14px 눈썹)이 깨진다. */
.pair-tale__where {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin: 0 0 4px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-soft);
  word-break: keep-all;
}
.pair-tale__where[hidden] { display: none; }
.pair-tale__where::before,
.pair-tale__where::after {
  content: "";
  flex: none;
  width: 18px;
  height: 4px;
  opacity: 0.6;
  background: url("/public/ui/divider.png") no-repeat -24.2px -1px / 66.5px auto;
}

/* 눈썹은 결과 화면 호패의 .hopae__lead와 같은 글자(14px, 보조 먹) — 세 화면의 글자 크기 짝(04 명세) */
.pair-tale__lead {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
}

.pair-tale__title {
  margin: 2px 0 0;
  font-family: var(--serif);
  font-size: clamp(22px, 6vw, 24px);
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 이야기(07): 지금의 두 사람 2문장 + 전생 꼬리 1문장. 명조 16px, 줄 간격 1.7, 왼쪽 정렬. 단어 중간에서 끊지 않는다(keep-all), 한 단어가 폭을 넘을 때만 꺾는다(anywhere).
   앞 두 문장은 각각 한 단락(사이 9px, 한 단계 연한 먹). 마지막 줄(전생 꼬리)은 "옛 기록 한 줄" — 16px 띄우고 진한 먹, 1px 작게,
   그 위에 붓 괘선의 왼쪽 끝 조각(divider.png 1536×64에서 x 70~430)을 40×7px로 잘라 놓는다. 인주색은 인장·낙관에만이라 점은 쓰지 않는다. */
.pair-tale__text {
  margin: 12px 0 0;
  font-family: var(--serif);
  font-size: 16px;
  line-height: 1.7;
  text-align: left;
  word-break: keep-all;
  overflow-wrap: anywhere;
  color: var(--ink-2);
}
.pair-tale__line { margin: 0; }
.pair-tale__line + .pair-tale__line { margin-top: 9px; }
.pair-tale__line + .pair-tale__line--now { margin-top: 16px; }
.pair-tale__line--now {
  font-size: 15px;
  color: var(--ink);
}
.pair-tale__line--now::before {
  content: "";
  display: block;
  width: 40px;
  height: 7px;
  margin: 0 0 8px;
  background: url("/public/ui/divider.png") no-repeat -8px 50% / 171px auto;
}

/* 산문 한 줄: 화면에서 더 이상 쓰지 않는다(hidden). 공유 함수가 데이터를 읽는 것과는 무관 */
.pair-tale__now { display: none; }

/* 괘선: 붓으로 그은 갈색 가로선 그림(에셋/바이럴/ui-kit/divider → public/ui, 1536×64 투명). 1px CSS 선 대신 */
.rule {
  display: block;
  height: 12px;
  margin: 14px 0 0;
  border: 0;
  background: url("/public/ui/divider.png") no-repeat center / 100% auto;
}

/* 서로에게(18 명세): 역할 두 줄을 위 VS 줄처럼 좌우 두 칸으로. 칸의 좌우 = 위 캐릭터의 좌우(pair-ui.js roleCells가 방향을 맞춘다).
   머리글 "서로에게"(명조 15px 700, 가운데) → 두 칸(각 1fr, 사이 세로 먹선 1.5px 먹 40% — 반응 띠의 선과 같다).
   칸: 이름표 "나에게 지현은" / "지현에게 나는"(25, 11px 700 두 번째 먹, 공유 페이지의 긴 이름표는 두 줄까지) 아래 역할(명조 16px 700, 가운데, 단어 단위로 꺾임). 두 칸은 위쪽 정렬이라 이름표 줄 높이가 같다.
   바탕은 종이 그대로 — 붓칠 띠·흰 상자를 깔지 않는다(반응 띠와 구분). */
.pair-roles { margin: 12px 0 0; text-align: center; }
.pair-roles:empty,
.pair-roles[hidden] { display: none; }
.pair-roles__head {
  margin: 0;
  font-family: var(--serif);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
}
.pair-roles__pair {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 13px;
  align-items: start;
  margin-top: 10px;
}
.pair-roles__pair::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 2px;
  bottom: 2px;
  width: 1.5px;
  transform: translateX(-50%);
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  border-radius: 1px;
}
.pair-roles__side { min-width: 0; padding: 0 4px; }
.pair-roles__who {
  display: block;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink-2);
  word-break: keep-all;
  overflow-wrap: anywhere;
}
.pair-roles__word {
  display: block;
  margin-top: 4px;
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 각주: 이런 궁합은 100쌍 중 몇 쌍인지 — 종이 맨 아래 */
.bond__note {
  margin: 14px 0 0;
  padding: 0 40px; /* 오른쪽 아래 낙관(34px) 자리를 비운다. 가운데 정렬이 흔들리지 않게 양쪽 같이 */
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-soft);
  word-break: keep-all;
}
.bond__note:empty { display: none; }

/* 터지기 전까지는 등급·이야기·표·각주를 숨겨 둔다. 터진 뒤 결과 등장과 같은 페이드 한 번 */
.bond .bond__grade,
.bond .pair-tale,
.bond .bond__rule,
.bond .pair-roles,
.bond .bond__note,
.bond .nakgwan { transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1); }
.bond.is-revealing .bond__grade,
.bond.is-revealing .pair-tale,
.bond.is-revealing .bond__rule,
.bond.is-revealing .pair-roles,
.bond.is-revealing .bond__note,
.bond.is-revealing .nakgwan { opacity: 0; transition: none; }

/* ── 궁합 리포트(17 명세): 지어낸 이야기 대신 "같은 상황, 다른 반응" ─────────────────
   데이터(public/data/pair-report.json)가 있는 쌍이면 이야기 칸(.pair-tale) 대신 이 블록. 없으면 이야기 칸 그대로(pair-ui.js가 고른다).
   순서: 두 캐릭터 → 각자 밑 한마디(.bond__say) + 가운데 먹 동그라미 VS → 괘선 → 머리글 → 반응 띠 셋 → 더 보기(2번까지, 25) → 괘선 → 서로에게 → 전생 한 줄 → 각주.
   결과지 밖: 결과 버튼들 → 각주 → 궁합 심층판(25, 맨 아래).
   봉인·터지는 동안은 이야기 칸과 같은 규칙으로 숨겼다가 터진 뒤 같은 페이드로 떠오른다. 흰 상자·테두리·그림자는 쓰지 않는다. */
.bond.is-closed .bond__say,
.bond.is-closed .bond__vs,
.bond.is-closed .pair-report,
.bond.is-closed .pair-past,
.bond[data-tone="sealed"] .bond__say,
.bond[data-tone="sealed"] .bond__vs,
.bond[data-tone="sealed"] .pair-report,
.bond[data-tone="sealed"] .pair-past { display: none; }
.bond .bond__say,
.bond .bond__vs,
.bond .pair-report,
.bond .pair-past { transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1); }
.bond.is-revealing .bond__say,
.bond.is-revealing .bond__vs,
.bond.is-revealing .pair-report,
.bond.is-revealing .pair-past { opacity: 0; transition: none; }

/* 한마디: 각 캐릭터(받침) 바로 밑, 명조 16px 700, 가운데, 두 줄까지(데이터 10~16자 → 375px에서 두 줄. 넘쳐도 말줄임으로 자르지는 않는다). 가운데 먹 동그라미(40px)가 두 칸 사이에 앉으므로
   안쪽(동그라미 쪽)을 12px 비운다 — 375px의 가장 가까운 거리(best, 틈 6% ≈ 17px)에서도 글자와 동그라미가 닿지 않게.
   직업명(figcaption)은 그 아래 12px 보조 먹으로 내려간다 */
.bond__say {
  align-self: stretch;
  margin: 10px 0 0;
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
  word-break: keep-all;
  overflow-wrap: anywhere;
  /* 자르지 않는다(2026-10-10): 카드가 들어오며 칸이 122px로 좁아져 "마지막 말을 따라 정하는…"처럼 두 줄에서 잘렸다. 세 줄까지 그대로 보인다 */
}
.bond__side--a .bond__say { padding-right: 12px; }
.bond__side--b .bond__say { padding-left: 12px; }
.bond__say[hidden] { display: none; }
.bond__say + figcaption { margin-top: 4px; }

/* 가운데 먹 동그라미 VS: 붓으로 한 번에 그린 원(에셋/바이럴/ui-kit/ink-circle → public/ui/ink-circle.png, 512 투명) 안에 CSS 글자.
   세로 자리는 두 한마디 줄의 가운데 — pair-ui.js placeVs가 재서 --vs-y로 넣는다(캐릭터 칸 높이가 화면 폭·등급 거리에 따라 달라서).
   글자는 먹색: 원이 속이 빈 고리라(엔소) 한지 위에 크림 글자를 얹으면 안 보인다 */
.bond__vs {
  position: absolute;
  left: 50%;
  top: var(--vs-y, 0px);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  transform: translate(-50%, -50%);
  background: url("/public/ui/ink-circle.png") no-repeat center / contain;
  font-family: var(--serif);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink);
  pointer-events: none;
}
.bond__vs[hidden] { display: none; }

.pair-report { margin-top: 2px; }
.pair-report[hidden] { display: none; }
/* 같은 일주: 한마디 하나만 가운데("둘 다 ~하는 사람") */
.pair-report__both {
  margin: 12px 0 0;
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--ink);
  word-break: keep-all;
}
.pair-report__both[hidden] { display: none; }
.pair-report__head {
  margin: 12px 0 0;
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
}

/* 반응 띠: 한 상황 = 한 줄. 상황 이름(13px 700 강조 갈색, 가운데) 아래 가로 두 칸, 사이 세로 먹선(1.5px, 먹 40%).
   각 칸 뒤에 붓칠 띠(public/ui/brush-band.png를 mask로) — 그 사람 카드의 오행 테두리 색(--edge-*, 명세 13)을 0.2(수는 0.17)로 옅게.
   왼쪽 띠는 바깥에서 들어와 가운데 먹선 쪽에서 붓이 들리고, 오른쪽은 좌우를 뒤집었다. 띠마다 기울기·높이를 조금씩(손으로 칠한 느낌, --tilt ±1.5°) */
.pair-report__list { margin-top: 2px; }
.react { margin-top: 14px; }
.react__sit {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--accent);
  word-break: keep-all;
}
.react__pair {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 13px;
  text-align: left;
}
.react__pair::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 6px;
  bottom: 6px;
  width: 1.5px;
  transform: translateX(-50%);
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  border-radius: 1px;
}
.react__pair.is-one { grid-template-columns: 1fr; }
.react__pair.is-one::after { display: none; }
.react__side {
  position: relative;
  isolation: isolate; /* 띠(::before)를 글 뒤, 종이 위에 */
  min-width: 0;
  padding: 9px 8px 10px;
  --band: var(--edge-earth);
  --band-a: 0.2;
}
.react__side::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0 -4px 0 -6px; /* 바깥쪽(종이 가장자리 쪽)으로 조금 넘치게, 안쪽은 먹선 바로 앞에서 끝 */
  background: var(--band);
  opacity: var(--band-a);
  -webkit-mask: url("/public/ui/brush-band.png") no-repeat center / 100% 100%;
  mask: url("/public/ui/brush-band.png") no-repeat center / 100% 100%;
  transform: translateY(var(--shift, 0px)) rotate(var(--tilt, 0deg));
}
.react__side--b::before { inset: 0 -6px 0 -4px; transform: translateY(var(--shift, 0px)) rotate(var(--tilt, 0deg)) scaleX(-1); }
.react__side--both::before { inset: 0 -6px; }
.react__side[data-el="WOOD"]  { --band: var(--edge-wood); }
.react__side[data-el="FIRE"]  { --band: var(--edge-fire); }
.react__side[data-el="EARTH"] { --band: var(--edge-earth); }
.react__side[data-el="METAL"] { --band: var(--edge-metal); }
.react__side[data-el="WATER"] { --band: var(--edge-water); --band-a: 0.17; } /* 가장 진한 색이라 조금 덜 — 글 대비 */
/* 같은 오행 둘(색이 같다): 오른쪽 띠만 한 단계 어둡게 — 두 편이 구분되게 */
.react.is-same-el .react__side--b::before { background: color-mix(in srgb, var(--band) 62%, var(--ink)); }
.react__who {
  display: block;
  margin-bottom: 2px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink-2); /* 보조 먹(--ink-soft)은 가장 진한 띠 위에서 4.1~4.4:1이라 한 단계 진하게(7:1 이상) */
}
.react__line {
  display: block;
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--ink);
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 다른 상황 더 보기: 선 버튼. 누를 때마다 3개씩, 다 보면 사라진다 */
.pair-report__more { margin-top: 16px; padding: 12px 18px; font-size: 15px; }
.pair-report__more[hidden] { display: none; }

/* ── 궁합 심층판 · 준비 중(명세 24 → 25) ─────────────────
   명세 25: 결과지 밖, 화면 맨 아래(결과 버튼들·각주 다음) — "열리면 알림 받기"가 페이지의 마지막 버튼. 바탕 한지 위에 놓인 쪽지 한 장.
   사주 코어의 "접힌 종이 + 띠지"를 작게 — 바탕은 한지 타일 + 괘선 1px + 그늘, 목록과 가격 줄 사이에 가로로 접힌 자국(쪽지 폭 끝까지), 오른쪽 아래 귀가 접혀 바탕 한지(--paper)가 비친다.
   "준비 중" 띠지는 쪽지 오른쪽 가장자리를 감싸고 나가는 종이 띠(먹 선 위아래 + 결과지 종이색). 인주색 없음, 흰 상자 없음.
   봉인·연출 중엔 결과지 아래 버튼들과 같은 규칙으로 숨는다(아래 .bond.is-closed ~ / .is-revealing ~). pair-ui.js가 만든다 */
.pair-deep {
  position: relative;
  margin: 28px 4px 4px;
  padding: 16px 16px 16px 18px;
  text-align: left;
  background: var(--paper) url("/public/ui/paper-bg.webp") 0 0 / 512px repeat;
  border: 1px solid var(--line);
  border-radius: 2px;
  box-shadow: 0 7px 12px -10px color-mix(in srgb, var(--ink) 55%, transparent);
}
/* 오른쪽 아래 접힌 귀: 바깥 반은 결과지 색(종이가 접혀 비어 보임), 안쪽 반은 뒤집힌 종이 뒷면 + 접힌 선 */
.pair-deep::after {
  content: "";
  position: absolute;
  right: -1px;
  bottom: -1px;
  width: 18px;
  height: 18px;
  background: linear-gradient(to top left,
    var(--paper) calc(50% - 0.5px),
    var(--line) 50%,
    color-mix(in srgb, var(--paper-2) 50%, var(--paper)) calc(50% + 1px));
  box-shadow: -1px -1px 2px -1px color-mix(in srgb, var(--ink) 30%, transparent);
}
.pair-deep__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.pair-deep__title {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  line-height: 1.35;
  color: var(--ink);
}
.pair-deep__band {
  flex: none;
  margin-right: -17px; /* 쪽지 오른쪽 가장자리(안쪽 여백 16 + 테두리 1)를 넘어 감싸고 나간다 */
  padding: 3px 17px 3px 10px;
  border-top: 1.5px solid var(--ink);
  border-bottom: 1.5px solid var(--ink);
  background: var(--sheet);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0.02em;
  color: var(--ink);
  white-space: nowrap;
}
.pair-deep__list {
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}
.pair-deep__list li {
  position: relative;
  padding-left: 13px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-2);
  word-break: keep-all;
  text-wrap: balance;
}
.pair-deep__list li + li { margin-top: 3px; }
/* 짧은 먹 점(21의 점 목록과 같은 붓끝 점, 한 단계 작게) */
.pair-deep__list li::before {
  content: "";
  position: absolute;
  left: 1px;
  top: calc(0.775em - 2px);
  width: 4px;
  height: 4px;
  border-radius: 55% 45% 50% 50% / 50% 55% 45% 50%;
  background: var(--ink);
}
.pair-deep__note {
  position: relative;
  margin: 14px 0 0;
  padding-top: 13px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-soft);
  word-break: keep-all;
}
.pair-deep__price { display: block; }
.pair-deep__dot { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* 접힌 자국: 쪽지 양 끝(안쪽 여백 18·16 + 테두리 1)까지 가로지르는 골 — 위는 먹 그늘 한 줄, 아래는 종이 결이 받는 밝은 한 줄 */
.pair-deep__note::before {
  content: "";
  position: absolute;
  top: 0;
  left: -19px;
  right: -17px;
  height: 6px;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--ink) 4%, transparent),
    color-mix(in srgb, var(--ink) 13%, transparent) 2px,
    color-mix(in srgb, var(--paper-2) 80%, transparent) 3px,
    transparent);
}
.pair-deep__btn { margin-top: 10px; padding: 12px 18px; font-size: 15px; background: transparent; }
.bond.is-closed ~ .pair-deep,
.bond[data-tone="sealed"] ~ .pair-deep { display: none; }
.pair-deep { transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1); }
.bond.is-revealing ~ .pair-deep { opacity: 0; transition: none; }

/* 전생 한 줄(17): 역할 두 줄 아래, 07의 전생 꼬리 모양 그대로 — 붓 괘선 왼쪽 끝 조각(40×7) + 명조 15px 두 번째 먹, 왼쪽 정렬 */
.pair-past {
  margin: 16px 0 0;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-2);
  text-align: left;
  word-break: keep-all;
  overflow-wrap: anywhere;
}
.pair-past[hidden] { display: none; }
.pair-past::before {
  content: "";
  display: block;
  width: 40px;
  height: 7px;
  margin: 0 0 8px;
  background: url("/public/ui/divider.png") no-repeat -8px 50% / 171px auto;
}

/* ── 인연 지도 ─────────────────────────────
   나를 가운데 두고, 궁합을 본 사람들이 둘레에 선다. 점수가 높을수록 가깝다(bond-map.js).
   실은 궁합 화면과 같은 말: 팽팽+매듭 / 처짐 / 끊김. 색도 궁합의 실 3색(녹갈·금·연지). 인주색은 인장에만. */
.map__head { text-align: center; }
.map__lead { margin: 0; font-size: 13px; font-weight: 600; color: var(--ink-soft); }
.map__title { margin: 4px 0 0; font-family: var(--serif); font-size: clamp(22px, 6vw, 26px); font-weight: 700; line-height: 1.3; word-break: keep-all; }
.map__sub { margin: 6px 0 0; font-size: 14px; color: var(--ink-soft); }

.map__stage {
  position: relative;
  width: 100%;
  max-width: 440px;
  aspect-ratio: 1 / 1.15; /* 명세 25: 1.05 → 1.15 — 위아래 사람과 나 사이에 점수 알약이 들어갈 실 길이 */
  margin: 14px auto 0;
  border: 1.5px solid var(--line);
  border-radius: 14px;
  background: radial-gradient(circle at 50% 50%, var(--paper-2) 0%, var(--paper) 75%);
  overflow: hidden;
}

.map__threads { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.map__thread { fill: none; stroke: var(--thread-good); stroke-width: 2.2; stroke-linecap: round; }
.map__thread--mid { stroke: var(--thread-mid); stroke-opacity: 0.85; }
.map__thread--low { stroke: var(--thread-low); stroke-opacity: 0.85; }
.map__thread--worst { stroke: var(--thread-low); stroke-opacity: 0.7; }
.map__knot { fill: var(--thread-good); }

.map__person {
  position: absolute;
  z-index: 1;
  width: 16%;
  transform: translate(-50%, -50%);
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
}
.map__person img { width: 100%; height: auto; aspect-ratio: 1 / 1.1; object-fit: contain; }
.map__person--me { width: 22%; cursor: default; z-index: 2; }
.map__name {
  margin-top: 2px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  max-width: 120%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.map__person--me .map__name { background: var(--ink); color: var(--paper-2); }
/* 점수 알약(명세 25): 그 사람에게 가는 실 위 맨 앞 층. 종이색 바탕 + 괘선 1px이라 실이 알약 뒤로 지나간다.
   자리(실 길이의 35~65%, 겹침 없는 곳)는 bond-map.js placeScores가 고른다. 누르기는 사람 버튼이 받는다(알약은 통과) */
.map__pill {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, -50%);
  padding: 1px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 12px;
  font-weight: 800;
  line-height: 1.4;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
}
.map__person:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 10px; }

.map__hint { margin: 10px 0 0; text-align: center; font-size: 13px; color: var(--ink-soft); }
/* 궁합 입력 화면의 "내 인연 지도 보기"(명세 25): 입력 패널 아래 선 버튼 하나(먹색 "궁합 보기"보다 약하게), 지도에 사람이 있을 때만 */
.pair-input__map { margin-top: 16px; }
.map__actions { display: grid; gap: 10px; margin-top: 16px; }

/* ── 뽑기 연출의 GPU 층 규칙(2026-10-04) ──
   폰(카톡 인앱·크롬)에서 연출 중 문 안의 무엇이든 다시 그려지면 그 프레임의 타일이 빈 채 나가 깜빡였다(녹화 영상으로 확인).
   그래서 연출 중 바뀌는 건 transform·opacity뿐이고, 바뀔 요소는 처음부터 제 층(will-change)을 갖는다.
   clip-path·mask·filter·border 애니메이션은 쓰지 않는다. */
.gate__paper, .gate__dark, .peek-card__front { will-change: opacity; }
.gate__shadow, .gate__char, .peek-card, .gate__tapmark { will-change: transform, opacity; }
.open__hint { will-change: opacity; }

/* 문구멍 틀: 캐릭터 눈 자리에 아주 작게 섰다가(app.js) transform(scale)만으로 자란다. 안의 방은 역비율로 줄어 화면에선 제자리.
   is-hole이 붙기 전(움직임 줄이기 등)엔 그냥 문틀 전체다. */
.gate__hole { position: absolute; inset: 0; transform-origin: 50% 50%; will-change: transform; visibility: hidden; }
/* 구멍 틀이 서기 전엔 방이 보이면 안 된다(다가오는 동안 문에 캐릭터가 크게 비쳤다, 2026-10-04). 움직임 줄이기 경로는 app.js가 직접 보인다. */
.gate__hole.is-hole { visibility: visible; }
.gate__hole.is-hole { border-radius: 50%; overflow: hidden; }
/* 구멍 가장자리: 찢어진 한지 섬유. 틀의 자식이라 틀과 함께 자라고(transform), 찢길 때 opacity로 걷힌다(app.js).
   틀 자체에 border를 주면 다 자란 뒤 문틀 overflow를 풀 때 문 밖으로 큰 호가 드러났다. */
.gate__rim {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  border-radius: 50%;
  border: 2px solid rgba(248, 239, 220, 0.95);
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.55);
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}
.gate__room { will-change: transform; clip-path: none; } /* 구멍은 틀(.gate__hole)이 잘라낸다. 방 자체의 clip-path(circle 0)는 쓰지 않는다 */

/* 문틀 테두리는 이 겹층이 그린다. 찢길 때 사라지는 건 이 층의 opacity다(문틀 border를 애니메이션하면 창호지까지 매 프레임 다시 그린다). */
.gate__frame { border-color: transparent; box-shadow: none; } /* 그림자도 겹층이 든다. 문틀에 두면 찢긴 뒤에도 네모 그림자가 남았다 */
.gate__edge {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  border: 7px solid #5a3a1c;
  border-radius: 6px;
  box-shadow: 0 0 0 3px #8a6236, 0 10px 24px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  will-change: opacity;
}

/* 카드가 들어맞을 때 은은히 밝아지는 겹층(filter: brightness 대신) */
.peek-card__flash {
  position: absolute;
  inset: 0;
  border-radius: 10px;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}

/* 같은 이유로 문 안의 큰 면에도 단색을 깐다(그라데이션은 그대로 덮는다). */
.gate__paper { background-color: #ecdcb8; }
.gate__dark { background-color: #2c2219; }

/* 카드 붓질 닦아내기(app.js). 틀은 카드보다 넓고(폭 2배) 위아래로 40px 여유를 둬, 기울어져도 다 그려진 카드와 빛번짐을 자르지 않는다.
   안쪽은 틀과 같은 상자이고, 카드 면들은 그 가운데에 놓인다. 둘의 원점이 같아야(0 0) 거꾸로 움직임이 정확히 상쇄된다. */
.peek-card__wipe {
  position: absolute;
  top: -40px;
  bottom: -40px;
  left: -50%;
  right: -50%;
  overflow: hidden;
  transform-origin: 0 0;
  will-change: transform;
}
.peek-card__in { position: absolute; inset: 0; transform-origin: 0 0; will-change: transform; }
.peek-card__in > .peek-card__back,
.peek-card__in > .peek-card__front,
.peek-card__in > .peek-card__flash { top: 40px; bottom: 40px; left: 25%; right: 25%; }

/* 복사 실패 시 주소 칸(심사 ⑤, 2026-10-08): 토스트 대신 선택 가능한 입력칸. 화면 아래 고정, 한지 종이 한 장 */
.copybox {
  position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 60;
  padding: 14px 16px; background: var(--paper, #f3e7c9); border: 1px solid var(--line, #7f8c4b); border-radius: 12px;
  box-shadow: 0 6px 24px rgba(42, 33, 24, 0.18);
}
.copybox[hidden] { display: none; }
.copybox__lead { margin: 0 0 8px; font-size: 14px; color: var(--ink, #2a2118); }
.copybox__input {
  width: 100%; box-sizing: border-box; padding: 10px 12px; font-size: 14px; color: var(--ink, #2a2118);
  background: #fff; border: 1px solid var(--line, #7f8c4b); border-radius: 8px; -webkit-user-select: all; user-select: all;
}
.copybox__close { margin-top: 8px; padding: 6px 12px; font-size: 13px; color: var(--ink-soft, #6b5d4d); background: none; border: 1px solid var(--line, #7f8c4b); border-radius: 8px; }
/* 각주 안의 개인정보처리방침 링크(심사 ④) */
.fineprint__link { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* ── 명세 28: 화면 사이 연결 다듬기 ───────────────────────── */

/* 2. 접힌 "내 일주 이야기"의 "이어서 읽기" 바로 아래 가격 한 줄(13px 글자, 강조 버튼 아님). 사실은 보조 먹, 가는 길은 두 번째 먹 + 밑줄.
   폰 폭에선 두 줄이라 "·"에서 끊고 점은 숨긴다(넓으면 한 줄에 점까지). 펼친 뒤엔 upsell이 같은 말을 하니 숨는다 */
.pillar-reading__price {
  appearance: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0;
  row-gap: 2px;
  margin: -4px 0 0;
  padding: 4px 0 8px;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  line-height: 1.45;
  text-align: left;
  color: var(--ink-soft);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pillar-reading__price-fact { white-space: nowrap; }
.pillar-reading__price-dot { white-space: pre; }
.pillar-reading__price-go {
  white-space: nowrap;
  font-weight: 700;
  color: var(--ink-2);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
@media (max-width: 479px) {
  .pillar-reading__price { flex-direction: column; align-items: flex-start; }
  .pillar-reading__price-dot { display: none; }
}
.pillar-reading__price:active { opacity: 0.7; }
.pillar-reading__price:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.pillar-reading__more-box[open] + .pillar-reading__price,
.pillar-reading.is-legacy .pillar-reading__price { display: none; }

/* 4. 결과 화면 버튼: 저장·링크·다른 생일은 글자 링크 한 줄, 사이 가는 세로 괘선(궁합 결과 .pair-end와 같은 말) */
.actions__minor { gap: 0; margin-top: 10px; align-items: center; flex-wrap: wrap; }
.actions__minor .btn-text { position: relative; padding: 10px 12px; }
.actions__minor .btn-text:not([hidden]) ~ .btn-text:not([hidden])::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 14px;
  background: var(--line);
  transform: translateY(-50%);
}
/* 공유받고 들어온 사람의 "보낸 사람이랑 궁합 바로 보기": 15의 선 버튼 그대로(먹색 강조는 궁합 하나). 긴 직업명은 두 줄까지 낱말 단위 */
.actions__from-share { word-break: keep-all; text-wrap: balance; line-height: 1.35; }
.actions__from-share[hidden] { display: none; }

/* 1. 궁합 공유 페이지: 봉인 위 한 줄 안내. 터지면(봉인이 풀리면) 사라진다 */
.bond__hint {
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
  text-align: center;
}
.bond:not(.is-sealed) .bond__hint { display: none; }
/* 받은 사람의 주 행동(먹색 "내 카드부터 뽑기")이 결과지 바로 아래로 올라왔다(pair-ui.js가 자리를 옮김). 그 아래 공유·저장 */
.bond ~ .upsell + .share { margin-top: 14px; }

/* Q20. 점수 근거 한 줄: 점수 인장·등급명 바로 아래(무대 안), 14px 두 번째 먹, 가운데, 세 줄까지(28 후속).
   무대 안 절대 자리라 그만큼(--why-h, pair-ui.js가 잰다) 이름표 아래 = 그림 칸 위를 비운다. 봉인 중에도 같은 높이를 비워
   터지는 순간 카드가 튀지 않게(23). 봉인·연출 중엔 등급명과 같은 규칙으로 숨었다가 같은 페이드로 떠오른다 */
.bond__why {
  position: absolute;
  left: 7%;
  right: 7%;
  top: calc(var(--seal-h) + 5px + 14px * 1.3 + 4px); /* 인장(46) + 등급명 위 5 + 등급명 한 줄(14 × 1.3) + 4 */
  z-index: 4;
  margin: 0;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink-2);
  text-align: center;
  word-break: keep-all;
  text-wrap: balance;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; /* 28 후속: 근거 44자가 320px에서 세 줄 — 세 줄까지, 말줄임은 그 뒤 */
  overflow: hidden;
  pointer-events: none;
}
.bond__why[hidden] { display: none; }
.bond.is-closed .bond__why { visibility: hidden; }
.bond .bond__why { transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1); }
.bond.is-revealing .bond__why { opacity: 0; transition: none; }
.bond.has-why .bond__side .bond__who { margin-bottom: calc(4px + var(--why-h, 0px)); }
.bond.has-why.is-sealed .bond__side .bond__who { margin-bottom: calc(12px * 1.55 + 2px * 2 + 1px * 2 + 4px - 28px + var(--why-h, 0px)); }
