/* ===========================================================================
   보부상 게임 — 오프닝 카드 (페이퍼플리즈형)
   2026-08-13 · 발주서 pm/OPENING_TUTORIAL_2026-08-13.md 1절·1-2절

   그림 1장 + 아래 검은 띠에 글 한 줄. **글자를 그림 위에 얹지 않는다.**
   어떤 그림이 와도 글이 읽히게 하려고 영역을 완전히 나눈 것이다.

   화면 맨 위에 덮는 오버레이다(.bs-screen 밖, z-index 100).
   기존 화면들의 렌더 조건을 하나도 건드리지 않으려고 이렇게 했다.
   =========================================================================== */

.bs-op{
  position:fixed; inset:0; z-index:100;
  background:#000; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  font-family:"Galmuri11","DungGeunMo",system-ui,"Malgun Gothic",sans-serif;
  -webkit-font-smoothing:none;
}

/* 16:9 무대. 게임 본편(.bs-stage > .bs-screen)과 같은 방식으로 잡는다 */
.bs-op-frame{
  position:relative; width:min(100vw,177.78vh); aspect-ratio:16/9;
  overflow:hidden; background:#000; container-type:inline-size;
}

/* ── 그림 — 아이콘(사물) 한 개 ────────────────────────────────────────────
   2026-08-14 전면 교체. 장면 삽화 -> **사물 이미지 한 개**.

   왜 — 장면은 16:9 칸과 3:2 원본의 비율이 안 맞아 세로가 절반 넘게 잘렸다.
   칸을 키우면 글이 작아지고 칸을 줄이면 잘림이 커진다. 같은 손잡이라 둘 다 못 만족한다.
   **정사각 사물**은 그 문제가 원천적으로 없다. 칸 크기를 나중에 또 바꿔도 안 잘린다.
   그리고 오프닝이 공문서 낭독이라, 장면 삽화보다 사물 한 개가 글을 덜 방해한다.

   ⚠️ o1~o3 는 이제 **배경이 투명한 정사각 PNG** 다. object-fit:cover 를 걸지 마라 —
      cover 는 잘라내는 규칙이고, 잘리지 않으려고 이 형식으로 바꾼 것이다. */
.bs-op-pic{
  position:absolute; left:0; right:0; top:0; height:54%;
  display:flex; align-items:center; justify-content:center;
}
.bs-op-img{
  width:auto; height:82%; object-fit:contain;
  /* [2026-08-17 · GFX_BUGS #3] pixelated → auto. 카드 사물(850~959px 원본)이 319px
     (0.35배)로 뜨는데, 비정수 최근접 축소는 도트를 불규칙하게 버려 계단이 깨진다.
     아트가 320px 리샘플 세트를 주면 그때 pixelated 로 되돌릴 수 있다(지시서 3안) */
  image-rendering:auto; display:block;
}
/* ── 좌→우 픽셀 디더 와이프 ── 등장·퇴장이 **같은 문법**이다 (2026-08-14 확정)
   경계를 매끈한 직선이 아니라 점으로 흩어지게 하려고 디더 마스크 PNG 를 쓴다.
   CSS 그라데이션은 경계가 반듯하고, clip-path+steps() 는 시간만 끊길 뿐 선은 그대로다.
   마스크를 100% -> 0% 로 밀면 불투명 구간이 왼쪽에서 들어온다.
     wipe_in  [불투명|디더|투명]  -> 왼쪽부터 차오른다
     wipe_out [투명|디더|불투명]  -> 왼쪽부터 지워진다
   둘 다 같은 방향이라 등장과 퇴장의 손놀림이 같다.

   ⚠️ mask-position-x **롱핸드는 크롬에서 애니메이션이 안 걸린다.** 반드시 숏핸드
      (-webkit-mask-position) 를 써라. 롱핸드로 두면 currentTime 이 0에서 안 움직인다.
   ⚠️ 마스크는 **같은 출처**여야 한다. CSS 마스크는 CORS 를 타서, 교차 출처면 차단되고
      그 요소가 통째로 안 보인다. (그림 <img> 는 교차 출처가 되는데 마스크는 안 된다)
      게임 서버가 output/ 을 서빙하므로 아래 경로면 같은 출처다.
   ⚠️ steps() 를 빼지 마라. 매끈하게 흐르면 픽셀 느낌이 죽는다.
   ⚠️ .58s 는 game.jsx 의 OP_EXIT_MS(580) 와 **같은 값**이다. 한쪽만 고치면 그림이
      사라지기 전에 다음 카드가 들어오거나, 빈 화면이 남는다. 고칠 땐 둘 다 고쳐라. */
.bs-op-img.in{
  -webkit-mask-image:url(output/ui/wipe_in.png);
          mask-image:url(output/ui/wipe_in.png);
  -webkit-mask-size:300% 100%;  mask-size:300% 100%;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  animation:bs-op-wipein .72s steps(22) both;
}
@keyframes bs-op-wipein{
  from{ -webkit-mask-position:100% 0; mask-position:100% 0 }
  to  { -webkit-mask-position:0% 0;   mask-position:0% 0 }
}

.bs-op-img.out{
  -webkit-mask-image:url(output/ui/wipe_out.png);
          mask-image:url(output/ui/wipe_out.png);
  -webkit-mask-size:300% 100%;  mask-size:300% 100%;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  animation:bs-op-wipeout .58s steps(18) both;
}
@keyframes bs-op-wipeout{
  from{ -webkit-mask-position:100% 0; mask-position:100% 0 }
  to  { -webkit-mask-position:0% 0;   mask-position:0% 0 }
}

/* ── 글칸 — 아래 46%. 자리는 처음부터 고정, 한 줄씩 켜진다 ────────────────
   ⚠️ 줄을 나중에 DOM 에 추가하지 마라. 추가하면 레이아웃이 다시 잡히면서 글이 움직인다.
      카드가 열릴 때 모든 줄을 넣어 두고 클래스만 켠다.
      (글이 이 화면에서 가장 중요한 정보다. 위치가 흔들리면 읽는 눈이 매번 자리를 다시 찾는다) */
.bs-op-band{
  position:absolute; left:0; right:0; bottom:0; height:46%;
  /* [2026-08-17 · GFX_BUGS #6] 그라데이션 금지(STYLE_SPEC 5절) — veil 방식으로 교체.
     본체는 진한 단색, 위 가장자리만 ::before 한 단(반투명 단색)으로 물러난다.
     경계가 반듯한 두 단이라 도트 결과 어울린다 — 부드러운 번짐이 없다. */
  background:rgba(0,0,0,.88);
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:clamp(6px,1.1cqw,14px);
  padding:0 clamp(18px,6cqw,77px);
  pointer-events:none;
}
.bs-op-band::before{
  content:""; position:absolute; left:0; right:0; bottom:100%; height:18%;
  background:rgba(0,0,0,.5);
}
.bs-op-lines{
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(4px,.7cqw,10px); width:100%;
}
.bs-op-lines p{
  margin:0; text-align:center; color:#e8e8e0; line-height:1.42;
  font-size:clamp(13px,2.45cqw,32px);
  text-shadow:2px 2px 0 #000;
  opacity:0; transition:opacity .1s steps(2);
}
.bs-op-lines p.on{ opacity:1 }

/* 다음 버튼 — **글 바로 아래.** 화면 구석에 두면 글을 읽고 눈을 멀리 옮겨야 한다 */
.bs-op-next{
  margin-top:clamp(6px,1.2cqw,16px);
  font:inherit; font-size:clamp(11px,1.7cqw,22px); font-weight:700;
  background:#1a2612; color:#e6e4b4; border:2px solid #8b8f4c;
  box-shadow:3px 3px 0 #000;
  padding:.42em 1.3em; cursor:pointer; letter-spacing:.05em;
  opacity:0; pointer-events:none; transition:opacity .2s;
}
.bs-op-next.on{ opacity:1; pointer-events:auto }
.bs-op-next:hover{ background:#2e3a1c }

/* ── 진행 표시 · 건너뛰기 ─────────────────────────────────────────────────*/
.bs-op-dots{
  position:absolute; left:clamp(14px,4cqw,50px); bottom:clamp(10px,2.4cqw,30px);
  display:flex; gap:clamp(4px,.7cqw,9px);
}
.bs-op-dots i{
  display:block; width:clamp(5px,.8cqw,10px); height:clamp(5px,.8cqw,10px);
  background:#3a3a34;
}
.bs-op-dots i.on{ background:#e8e8e0 }

.bs-op-skip{
  position:absolute; right:clamp(10px,2.4cqw,30px); top:clamp(10px,2.4cqw,30px);
  z-index:3; font:inherit; font-weight:800; cursor:pointer;
  background:rgba(0,0,0,.55); color:#6b6b62; border:2px solid #3a3a34;
  padding:clamp(3px,.5cqw,6px) clamp(9px,1.4cqw,17px);
  font-size:clamp(9px,1.2cqw,14px); letter-spacing:.06em;
  white-space:nowrap; line-height:1.35;   /* 작은 창에서 「건너뛰기」가 잘리던 것 */
}
.bs-op-skip:hover{ color:#e8e8e0; border-color:#6b6b62 }

/* 2026-08-14: 타자기(한 자씩)를 걷어냈다. 공문서 낭독은 '읽어 주는' 게 아니라
   '보여 주는' 것이라, 문장이 통째로 켜지는 편이 또렷하다. 커서도 같이 뺐다. */


/* ══════════════════════════════════════════════════════════════════════════
   편지 화면 — 그림 리소스 (확정 2026-08-13 · 시안 graphics/letter_apply_preview.html)

   골격(.bs-let-paper / .bs-let-hd / .bs-let-seal 위치·크기)은 bobusang-build.css §10
   이 정한다. **거기는 빌드 관할이라 건드리지 않았다.**
   여기서는 그 위에 **그림만 얹고, 밝은 종이에 맞춰 글자색만 뒤집는다.**
   index.html 링크 순서상 이 파일이 build.css 보다 뒤라서 덮어쓸 수 있다.

   ⚠️ 종이를 다른 그림으로 바꾸면 글자색도 같이 봐야 한다. 어두운 양피지로 바꾸면
      아래 색 뒤집기를 통째로 지워야 한다 — 안 그러면 어두운 종이에 어두운 글자가 된다.
   ══════════════════════════════════════════════════════════════════════════ */

/* 편지지 — 반듯한 밝은 양피지. 시안 3안(쭈굴 / 어두움 / 반듯) 중 「반듯한 종이」 채택.
   ⚠️ 가장자리가 너덜너덜한 그림을 쓰면 제목 글자가 해진 테두리에 묻혀 안 읽힌다(A·B안에서 확인).
   ⚠️ padding 을 줄이지 마라 — 그림의 가장자리 얼룩 위로 글자가 올라간다 */
.bs-op.bs-let .bs-let-paper{
  border:0; box-shadow:none;
  background:url("/output/opening/letter_paper.png") center/100% 100% no-repeat;
  padding:clamp(26px,5.4cqw,68px) clamp(30px,6.2cqw,78px);
  /* ⚠️ overflow 를 auto 로 되돌리지 마라(build.css §10 의 기본값이 auto 다).
     스크롤이 생기면 글자가 종이 그림의 가장자리 얼룩 위를 지나간다.
     넘치는 글은 아래 .bs-let-body 가 장으로 나눠 넘긴다. */
  display:flex; flex-direction:column; overflow:hidden;
}
/* 본문만 담는 칸 — 제목·서명을 뺀 나머지 높이를 전부 차지한다.
   장 나누기(paginateParas)가 이 칸의 clientHeight 를 기준으로 잰다.
   ⚠️ min-height:0 이 없으면 flex 자식이 안 줄어들어 측정이 항상 「안 넘침」으로 나온다 */
.bs-op.bs-let .bs-let-body{ flex:1; min-height:0; overflow:hidden }
/* [2026-08-15 · TASK_TUTORIAL_LOBBY 2-2] 편지 문단 = 한 문장 한 줄. 문단 문자열 안의 \n 을
   줄바꿈으로 살린다(pre-line). 문단 사이는 줄 사이(line-height 1.7)보다 눈에 띄게 넓힌다 —
   "한 문장 한 줄 + 묶음 사이 이격"이 확정 의도다.
   paginateParas 의 측정용 <p> 도 이 칸(.bs-let-body) 안에 붙어 같은 규칙으로 재진다.
   \n 없는 기존 편지(하겐 협박장·6일차 통첩)는 pre-line 이 있어도 표시가 같다. */
.bs-op.bs-let .bs-let-body p{ white-space:pre-line; margin-bottom:clamp(13px,2.8cqw,32px); }
.bs-op.bs-let .bs-let-body p:last-child{ margin-bottom:0; }

/* 밝은 종이라 글자를 어둡게 뒤집는다 (build.css §10 은 어두운 종이 전제였다) */
.bs-op.bs-let .bs-let-hd{ color:#4a3d22; border-bottom-color:#8a7d52 }
.bs-op.bs-let .bs-let-paper p{ color:#2b2418 }
.bs-op.bs-let .bs-let-paper p b{ color:#8a5a10 }   /* 빚 300골드 · 열이틀 — 숫자 강조 */
.bs-op.bs-let .bs-let-sign{ color:#4a3d22 }

/* 하겐의 밀랍 인장 — CSS 사각형을 그림으로 갈아 끼운다.
   글자("하겐")는 그림에 이미 뭉갠 압인이 있어 지운다(color:transparent). */
.bs-op.bs-let .bs-let-seal{
  border:0; background:url("/output/opening/seal_red.png") center/contain no-repeat;
  color:transparent; transform:none;
  width:clamp(44px,7.6cqw,94px); height:clamp(44px,7.6cqw,94px);
  right:clamp(6px,1.4cqw,18px); bottom:clamp(4px,1.2cqw,14px);
}

/* ── 편지가 펴지는 연출 ──────────────────────────────────────────────────────
   ① 접힌 편지가 탁자에 놓인다 → ② 세로로 펴진다 → ③ 글씨가 드러난다.
   ⚠️ 전부 steps() 다. 부드럽게 움직이면 픽셀아트 화면에서 이 요소만 매끈해 보인다.
   ⚠️ 그라데이션·블러 금지 규칙에 맞춰 opacity 와 transform 만 쓴다. */

/* ① 접힌 편지 — 아래에서 살짝 올라오며 놓인다 */
.bs-op.bs-let .bs-let-closed{
  position:absolute; left:50%; top:50%; width:46%;
  transform:translate(-50%,-50%);
  image-rendering:pixelated;
  animation:bs-let-drop .42s steps(5) both;
}
@keyframes bs-let-drop{
  0%   { opacity:0; transform:translate(-50%,-42%) scale(.94) }
  100% { opacity:1; transform:translate(-50%,-50%) scale(1) }
}

/* ② 편지지 — 세로로 펴진다. 34% 는 셋으로 접힌 편지의 한 겹 높이다 */
.bs-op.bs-let .bs-let-paper{
  transform-origin:center center;
  animation:bs-let-unfold .34s steps(5) both;
}
@keyframes bs-let-unfold{
  0%   { transform:scaleY(.34) }
  100% { transform:scaleY(1) }
}

/* ③ 글씨 — 종이가 다 펴진 뒤에 드러난다. 종이보다 늦게 시작한다 */
.bs-op.bs-let .bs-let-paper > *{
  animation:bs-let-ink .5s steps(2) both;
}
@keyframes bs-let-ink{ 0%,62%{opacity:0} 100%{opacity:1} }

/* 페이지를 넘길 때는 종이를 다시 펴지 않는다 — 글씨만 갈아 끼운다.
   (JSX 가 페이지마다 key 를 바꾸면 이 클래스로 종이 애니메이션만 끈다) */
.bs-op.bs-let .bs-let-paper.turn{ animation:none }

/* ── 다음 장이 남았다는 표시 ─────────────────────────────────────────────────
   대사창(.bs-vn.more)과 같은 문법이다. 종이 **밖** 오른쪽 아래에 둔다 —
   종이 안에 두면 마지막 줄이 길 때 글자를 덮는다(대사창에서 겪은 것). */
/* ⚠️ 종이 **밖**(bottom 음수)에 두지 마라 — .bs-op-frame 이 overflow:hidden 이라 잘린다.
   종이 안쪽 아래 여백에 둔다. 본문(.bs-let-body)은 padding 안쪽까지만 쓰므로 글자와 안 겹친다. */
.bs-op.bs-let .bs-let-paper.more::after{
  content:"▼"; position:absolute;
  right:clamp(14px,3cqw,36px); bottom:clamp(10px,2cqw,24px);
  color:#8a5a10; font-size:clamp(10px,1.5cqw,18px); line-height:1.2;
  animation:bs-let-more 1s steps(1) infinite;
}
@keyframes bs-let-more{ 0%,55%{opacity:1} 56%,100%{opacity:0} }

/* 몇 장 중 몇 장째인가 — 두 장 이상일 때만 JSX 가 붙인다 */
.bs-op.bs-let .bs-let-pg{
  position:absolute; left:clamp(14px,3cqw,36px); bottom:clamp(10px,2cqw,24px);
  color:#8a7d52; font-weight:700; white-space:nowrap;
  font-size:clamp(8px,1.15cqw,13px); line-height:1.4;
}

@media (prefers-reduced-motion: reduce){
  .bs-op.bs-let .bs-let-closed,
  .bs-op.bs-let .bs-let-paper,
  .bs-op.bs-let .bs-let-paper > *{ animation:none }
  .bs-op.bs-let .bs-let-paper.more::after{ animation:none }
}
