/*
 * next-bridge.css — **퍼블리싱 원본이 아니다.** Next 쪽 전역 CSS 와 design.css 사이를 잇는 다리다.
 *
 * design.css 는 관리자(`/mng`)와 섞이면 안 되므로 사용자 사이트 레이아웃에서만 `<link>` 로 문다
 * (`app/(site)/layout.tsx`). 그런데 루트 레이아웃이 들고 있는 `globals.css` 는 앱 전체에 깔리고,
 * 아래 둘을 design.css 보다 **높은 명시도**로 잡고 있어서 그대로 두면 사이트 화면이 어긋난다.
 *
 *   1. `body { font-family: Outfit; background: #f9fafb; color: #344054 }` — 명시도는 같지만
 *      번들 CSS 와 이 <link> 의 순서가 보장되지 않는다. `html body` 로 올려서 순서 싸움을 끝낸다.
 *   2. `html.dark body { background: #101828 }` — 사용자 사이트는 **라이트 고정**이다
 *      (2026-08-31 사용자 확정). design.css 에는 다크 토큰이 아예 없어서(0건), 관리자가 `/mng`
 *      에서 다크를 켜 둔 채로 넘어오면 배경만 검게 뜬다.
 *
 * design.css 자체는 손대지 않는다 — 원본(relaxrimm/dmonster_project_maxwell)이 단일 진실이고
 * 고칠 일이 생기면 거기서 고쳐 rsync 로 당겨 온다 (docs/publishing/_이관_안내.md).
 */
html body {
	font-family: var(--font-family-base);
	font-size: var(--font-size-md);
	color: var(--color-text-primary);
	background: var(--color-bg-surface-default);
}

/*
 * 🔴 **`body` 에 붙은 Tailwind 유틸이 위 규칙을 이긴다** (2026-09-02 발주사 지적 —
 *    *"폰트가 다르다"*).
 *
 * 루트 레이아웃이 `<body className="antialiased font-outfit">` 로 연다. Tailwind 가
 * 그것을 `.font-outfit { font-family: Outfit, Pretendard, … }` 로 뽑는데, **클래스는
 * 명시도 0,1,0** 이라 위의 `html body`(0,0,2)를 이긴다. 그래서 사용자 사이트가
 * 퍼블리싱의 **Wanted Sans 가 아니라 Outfit** 으로 그려지고 있었다.
 *
 * Outfit 에는 **한글 글리프가 없어** 한글은 그다음 후보로 떨어진다 — `Pretendard` 는
 * 이 프로젝트에 실려 있지 않으므로 결국 macOS 는 Apple SD Gothic Neo, Windows 는
 * 맑은 고딕이 된다. **보는 사람마다 한글 폰트가 달라진다.**
 *
 * 자산이 빠진 것이 아니다 — `WantedSansVariable.woff2` 는 실려 있고 `@font-face` 도
 * design.css 에 있다(1.2MB · 200 응답 실측). 순전히 명시도 문제다.
 *
 * ⚠️ `layout.tsx` 에서 `font-outfit` 을 떼지 않는다. **관리자(`/mng`)와 선생님 화면은
 *    Outfit 이 맞다** — 목업이 그렇다. 뗄 수 있는 것은 사용자 사이트뿐인데, 그쪽만
 *    걸러 내는 조건을 루트 레이아웃에 넣는 것보다 이 파일이 되받는 편이 낫다.
 *    이 파일은 **사용자 사이트 레이아웃에서만** 물리기 때문이다.
 */
html body.font-outfit {
	font-family: var(--font-family-base);
}

/* 이중 클래스(html.dark.dark)는 globals.css 의 `html.dark body` 와 **명시도를 벌리기 위함**이다.
   같은 명시도면 로드 순서로 이기고 지는데, 번들 CSS 와 이 <link> 의 순서는 보장되지 않는다.
   design.css 가 Tailwind 의 .container 를 `.container.container` 로 막아 둔 것과 같은 수법이다. 제거 금지 */
html.dark.dark body {
	color: var(--color-text-primary);
	background: var(--color-bg-surface-default);
}

/*
 * 아직 안 만든 화면으로 가는 자리 — 회원가입 · 아이디/비밀번호 찾기 · 수강 상담 · GNB 6메뉴.
 * 「비활성 모양만」으로 두기로 했다 (2026-08-31 사용자 확정). 링크가 아니라 <span> 이라
 * 클릭이 안 되고, 화면을 옮길 때마다 <Link> 로 바꿔 끼우면 된다. 404 를 만들지 않는 게 요점.
 */
[data-todo] {
	cursor: default;
}

/*
 * 로그인 여부가 확정되기 전의 헤더 — **두 벌을 다 실어 두고 여기서 고른다.**
 *
 * 서버가 보내는 HTML 은 로그인 여부를 모른다(캐시되어 누구에게나 같다). 첫 페인트 전에
 * 도는 인라인 스크립트(`app/layout.tsx` 의 `sessionHintScript`)가 `<html data-session="1">`
 * 을 달아 두면, 브라우저는 **그리기도 전에** 맞는 쪽만 남긴다.
 *
 * ⚠️ `display: contents` 라야 `.hdr_act` 의 flex 배치가 그대로 산다 — 감싸는 상자가
 *    끼어들면 간격이 틀어진다.
 * ⚠️ `.hdr_act.is_user` 는 `gap` 하나만 바꾼다(design.css 2417). 확정 전에는 그 클래스를
 *    못 붙이므로 같은 값을 여기서 준다. 🚫 design.css 를 고치지 말 것 — 원본이 단일 진실이다.
 */
/*
 * 🔴 **규칙은 여기 없다 — `app/layout.tsx` 의 인라인 `<style>` 이다** (2026-09-01
 *    사용자 지적: *"새로고침할 때 마다 알림 아이콘이 왼쪽으로 갔다가 오른쪽으로 돌아와"*).
 *
 * 이 파일은 `<link>` 로 온다. 첫 페인트를 막아 주는 것이 원칙이지만, 실제로는 두 벌이
 * 잠깐 함께 보이는 순간이 있었다 — 그때 종 아이콘이 왼쪽에 찍혔다가 제자리로 갔다.
 * 스타일시트가 도착하기를 기다릴 이유가 없는 규칙이라 **문서와 함께 오는 자리**로 옮겼다.
 *
 * 옮긴 규칙: `.hdr_boot` · `.hdr_boot_user` · `.hdr_boot_guest` · `.hdr_act_boot`.
 * 🚫 여기에 되살리지 말 것 — 두 곳에 있으면 어느 쪽이 이기는지가 로딩 순서에 달린다.
 */

/*
 * 푸터를 화면 아래쪽에 붙여 둔다 — 내용이 짧아도 헤더 밑으로 따라 올라오지 않게.
 *
 * 헤더는 `position: fixed`(design.css 2304) 라 흐름 밖이다. 세로로 쌓이는 것은 본문과
 * 푸터뿐이라, 화면을 옮기는 동안 본문이 「확인 중…」 한 줄로 줄면 푸터가 곧장 위로 붙는다
 * (2026-09-01 사용자 지적).
 *
 * ⚠️ 본문에 `min-height: 100vh` 를 주는 흔한 방법을 쓰지 않았다 — 그러면 내용이 짧은
 *    화면에서 푸터가 **화면 밖으로** 밀려나 스크롤해야 보인다.
 * ⚠️ `flex-shrink: 0`(`1 0 auto`) 이라야 한다. 기본값 1 이면 내용이 넘칠 때 본문이
 *    도로 찌그러진다.
 * 🚫 `body` 를 flex 로 바꾸지 않았다 — `(site)` 밖(관리자 · 선생님 · 결제)까지 함께
 *    흔들린다. 이 라우트 그룹 안에서만 감싼다.
 */
.site_shell {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
}

.site_body {
	flex: 1 0 auto;
}

/*
 * 화면을 옮기는 동안의 자리 지킴 (`RequireStudent` 등).
 *
 * 🔴 헤더가 `position: fixed`(design.css 2304) 라 **위쪽 여백을 직접 잡아야 한다.**
 *    실제 화면은 `.sub_top`(184 · design.css 2911) · `.my_top`(140 · 8980) 이 그 몫을
 *    하는데 자리 지킴에는 그 머리가 없다. 없으면 글자가 헤더 뒤에 깔려 **화면이 통째로
 *    빈 것처럼 보인다** (2026-09-01 사용자 지적).
 *    여백은 `.sub_top` 쪽에 맞춘다 — 둘 중 여유 있는 값이라 어느 화면을 대신하든 안전하다.
 */
.site_boot {
	padding: 184px 0 140px;
}

/* ─────────────────────────────────────────────────────────────────────────
   교재 뷰어 (D-2b) — 플립북을 붙이면서 자리표시자 치수를 덮는다 (2026-09-02)

   `design.css` 는 이 칸을 **회색 자리표시자**로 잡아 뒀다 (`aspect-ratio: 1120/400`
   · `background:#6e6e6e` · 가운데 정렬 안내 문구). 안내 문구 한 줄에 맞춘 치수라
   실제 책을 넣으면 **가로로 납작해서 글자가 안 읽힌다** — 펼친 책은 세로가 필요하다.

   🚫 `design.css` 를 고치지 말 것 (원본이 단일 진실이다). 여기서 덮는다.
   ⚠️ 1024 이하는 원본이 이미 «남은 높이 전부» 로 풀페이지 처리를 해 둔다 —
      그 규칙을 건드리지 않으려고 이 블록은 **1025 이상에만** 건다.
   ───────────────────────────────────────────────────────────────────────── */
@media (min-width: 1025px) {
	.modal_viewer .viewer_area {
		aspect-ratio: auto;
		/* 헤더 · 푸터 · 딤 여백을 뺀 나머지. 화면이 낮으면 같이 낮아진다 */
		height: min(72vh, 760px);
		min-height: 420px;
		padding: 0; /* 플러그인이 자기 여백을 갖는다 */
	}
}

/*
   ⚠️ 원본은 이 칸을 `display:flex` + 가운데 정렬로 잡아 뒀다 — **안내 문구 한 줄**을
      가운데 두려던 것이다. 뷰어를 넣으면 플러그인이 스스로 가운데를 맞추므로 그
      정렬이 오히려 방해가 된다(가로가 내용 폭만큼 줄어든다). 블록으로 되돌린다.
   🚫 실패 안내(`.viewer_fail`)는 여전히 가운데여야 한다 — 그쪽은 자기 규칙에서
      따로 가운데를 잡는다. 여기서 flex 를 살려 두고 예외를 파는 것보다 단순하다.
*/
.modal_viewer .viewer_area:has(> ._df_book) {
	display: block;
}

.modal_viewer .viewer_area > ._df_book {
	width: 100%;
	height: 100%;
}

/* 플러그인이 못 뜬 경우의 안내 — 자리표시자와 같은 톤을 쓴다 */
.modal_viewer .viewer_fail {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100%;
	font-size: var(--font-size-xl);
	font-weight: var(--font-weight-semibold);
	line-height: 1.6;
	text-align: center;
}

.modal_viewer .viewer_fail span {
	font-size: var(--font-size-md);
	font-weight: var(--font-weight-regular);
	opacity: 0.8;
}

/* 플립북 자체는 배경이 없다 — 회색 판 위에 그대로 얹는다 */
.modal_viewer .viewer_area .df-container {
	background: transparent;
}

/*
 * ## 에디터 안에서 서식이 보이지 않던 것 (2026-09-02 사용자 지적 —
 *    *"에디터에 입력했을때부터 수정된 변화가 안보이는거같아서 물어보는거야"*)
 *
 * `RichTextEditor` 는 본문에 `.tiptap-content` 를 단다. 그 서식 규칙이 **`mng.css` 에만**
 * 있어서, 관리자 · 선생님 화면에서는 제대로 보이지만 **사용자 사이트에서는 아무 규칙도
 * 걸리지 않았다** — `mng.css` 는 `/mng` 와 `/teacher` 레이아웃에서만 물기 때문이다.
 * 그래서 `/board/write` 에서 [H2] 를 눌러도 글자가 그대로였다.
 *
 * 🔴 **저장은 멀쩡했다.** 태그는 제대로 만들어지고 서버 정화도 통과한다(회귀
 *    `editor-roundtrip.spec.ts` 17건). **보이기만** 안 됐다 — 그래서 더 나빴다.
 *    「반영이 안 된 줄 알고 다시 누르는」 화면이 된다.
 *
 * 저장된 글은 `.editor_content` 로 그린다(design.css). 에디터 안도 **같은 모양**이어야
 * 「보이는 대로 저장」이 성립하므로, 그 규칙을 `.tiptap-content` 에 그대로 얹는다.
 *
 * ⚠️ design.css 를 고치지 않는다 — 퍼블리싱 원본이고 두 벌이 byte-identical 이어야 한다.
 *    `.tiptap-content` 는 우리가 붙인 클래스라 여기(다리)가 맞는 자리다.
 * ⚠️ `mng.css` 쪽 규칙은 그대로 둔다. 관리자 화면은 design.css 를 물지 않으므로
 *    거기서는 그 규칙이 유일한 서식이다. **두 곳이 각자 자기 화면을 맡는다.**
 */
.tiptap-content {
	font-size: var(--font-size-lg);
	line-height: 1.6;
	color: var(--color-text-secondary);
	overflow-wrap: break-word;
}

.tiptap-content h2,
.tiptap-content h3,
.tiptap-content h4 {
	margin: var(--space-3xl) 0 var(--space-md);
	font-size: var(--font-size-xl);
	font-weight: var(--font-weight-semibold);
	line-height: 1.5;
	color: var(--color-text-primary);
}

.tiptap-content p {
	margin: 0;
}

/* 첫 요소의 위 여백은 지운다 — 40px 이 그대로 붙으면 입력칸 위쪽만 벌어진다 */
.tiptap-content > :first-child {
	margin-top: 0;
}

.tiptap-content p + p {
	margin-top: var(--space-sm);
}

.tiptap-content ol,
.tiptap-content ul {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
	padding-left: 30px;
	margin: 0;
	list-style: decimal;
}

.tiptap-content ul {
	list-style: disc;
}

.tiptap-content a {
	color: var(--color-text-link-default);
	text-decoration: underline;
}

.tiptap-content strong,
.tiptap-content b {
	font-weight: var(--font-weight-semibold);
}

/* 아래 넷은 `.editor_content` 에 없다 — 저장된 본문에는 design.css 의 다른 규칙이
   걸리지만 에디터 안에는 걸리는 것이 없어서 따로 적는다 */
.tiptap-content blockquote {
	margin: 0;
	padding-left: var(--space-md);
	border-left: 3px solid var(--color-border-disabled);
	color: var(--color-text-quaternary);
}

.tiptap-content pre {
	padding: var(--space-md);
	border-radius: var(--radius-md);
	background: var(--color-bg-surface-muted);
	font-size: var(--font-size-md);
	overflow-x: auto;
}

.tiptap-content img {
	max-width: 100%;
	height: auto;
	border-radius: var(--radius-lg);
}

.tiptap-content table {
	width: 100%;
	border-collapse: collapse;
}

.tiptap-content th,
.tiptap-content td {
	border: 1px solid var(--color-border-default);
	padding: var(--space-xs) var(--space-sm);
}

/* 빈 첫 단락의 안내 문구 — `mng.css` 와 같은 규칙 (에디터가 그 속성을 심는다) */
.tiptap-content p.is-editor-empty:first-child::before {
	content: attr(data-placeholder);
	color: var(--color-text-placeholder);
	float: left;
	height: 0;
	pointer-events: none;
}

/*
 * 480 이하에서 검색범위 · 정렬 드롭다운이 **화면 절반 폭**으로 늘어나던 것
 * (2026-09-03 사용자 지적 — 밑줄이 칸보다 길게 뻗었다).
 *
 * `design.css:5995` 의 `.cc_dd:not(.tz_dd) { width: calc(50% - …) }` 는 **회원가입
 * 전화 행의 국가번호 칸**을 위한 규칙이다 (2026-08-26 · 이름 2열 한 칸과 폭을 맞춘다).
 * 퍼블리싱 원본에서는 검색범위 · 정렬이 네이티브 `select` 라 `.cc_dd` 가 붙을 일이
 * 없었는데, 우리가 커스텀 드롭다운으로 바꾸면서 **같은 클래스를 달아** 걸려들었다.
 *
 * 두 자리만 제 폭으로 되돌린다 — 검색범위는 칸(96px)을 채우고, 정렬은 내용 폭이다
 * (≤768 의 `.board_sort .sel_dd { width: auto }` 를 480 에서도 잇는다).
 * 🚫 `design.css` 의 국가번호 규칙을 고치지 말 것 — 그쪽은 원본이 맞다.
 */
@media (max-width: 480px) {
	.search_wrap .scope .sel_dd {
		flex: 1 1 auto;
		width: 100%;
	}

	.board_sort .sel_dd {
		flex: 0 0 auto;
		width: auto;
	}
}

/*
 * 약관 · 정책 문서 제목 아래 여백 (2026-09-03 발주사 QA — *"제목 하단에 여백을 40px
 * 정도 추가 부탁드립니다. 제가 퍼블리싱하면서 놓친 것 같습니다"*).
 *
 * `design.css:5138` 의 `.terms_doc .doc_tit` 에는 `margin` 이 없어 제목과 본문이 붙는다.
 * 퍼블리싱 쪽 누락임을 작성자가 확인해 주었으므로 여기서 채운다 — `design.css` 는
 * 원본 그대로 둔다 (이 파일 머리주석).
 *
 * ⚠️ 세 탭에 모두 건다. QA 는 이용약관 · 개인정보처리방침 둘을 짚었지만 오픈소스
 *    라이선스도 같은 `.doc_tit` 이라, 빼면 탭을 옮길 때 제목 위치가 어긋난다.
 */
.terms_doc .doc_tit {
	margin-bottom: var(--space-3xl);
}

/*
 * [회원 탈퇴] — 밑줄 · 글자색이 디자인과 달랐다 (2026-09-03 발주사 QA).
 *
 * 퍼블리싱은 `<a>` 라 `.profile_withdraw a` 로 스타일을 건다 (design.css 9902).
 * 우리는 모달을 여는 동작이라 `<button>` 으로 냈고 — 그게 맞다, `<a href="#">` 는
 * 키보드·보조기기에게 «어딘가로 간다» 고 거짓말한다 — 그래서 규칙이 안 걸렸다.
 * 버튼도 같은 모양이 되게 한다 (브라우저 기본 테두리·배경·글꼴을 걷어낸다).
 */
.profile_withdraw button {
	padding: 0;
	font: inherit;
	color: inherit;
	text-decoration: underline;
	text-underline-position: from-font;
	cursor: pointer;
	background: none;
	border: 0;
}

/*
 * 수업 칩 · 상태 범례 — Figma `Class chip` 302:11951 **개정본** (2026-09-07 발주사 QA).
 *
 * 발주사: *"슬롯에 폰트 색으로 보더 넣어주실 수 있나요 (현재 슬롯이 밋밋해서) / 글자가
 * 연해보여서 조금 더 굵게 / 예정은 이전처럼 흰색바탕으로 하고 회색 보더로."*
 *
 * `design.css:10353` 은 **개정 전** 컴포넌트를 그린다 — 테두리가 아예 없고, 예정 칩
 * 바탕이 회색(`--color-bg-neutral-default` #f1f3f4)이다. 개정본은 다섯 상태 모두
 * **글자색과 같은 계열의 1px 보더**를 갖고, 예정 · 수업취소는 흰 바탕이다.
 *
 *   상태       바탕       보더       글자
 *   예정       #ffffff    #e5e7e9    #4c5156
 *   출석       #edfbf9    #47d0be    #2fae9e
 *   결석       #fbeded    #d04747    #d04747
 *   휴강       #fff4ef    #ff9869    #f57d4d
 *   수업 취소  #ffffff    #e5e7e9    #4c5156 (취소선)
 *
 * 값은 전부 design.css 가 이미 갖고 있는 토큰이다 — 새 색을 만들지 않았다.
 *
 * ⚠️ 높이는 건드리지 않는다. Tailwind Preflight 의 `box-sizing: border-box` 위에
 *    얹히므로 1px 보더가 30px · 64px **안쪽**으로 들어간다. 주간 뷰의 절대배치
 *    (`.wk_slot .class_chip`, design.css 10451)도 그대로 맞는다.
 *
 * 🚫 `design.css` 를 고쳐서 맞추지 말 것 — 원본이 단일 진실이다 (이 파일 머리주석).
 *    퍼블리싱 쪽이 개정본을 반영하면 이 블록은 지우면 된다.
 */
.class_chip {
	font-weight: var(--font-weight-medium);
	background: var(--color-bg-surface-default);
	border: var(--border-width-thin) solid var(--color-border-default);
}

.class_chip.is_attend {
	border-color: var(--color-border-brand-default);
}

.class_chip.is_absent {
	border-color: var(--color-border-danger-default);
}

.class_chip.is_off {
	border-color: var(--color-border-secondary-default);
}

/*
 * 범례 5종도 같은 규칙이다 — 칩과 색이 갈리면 «이 색이 무슨 상태인지» 알려 주는
 * 역할을 못 한다. 여기서만 건다(`.cal_legend .badges`) — `.badge` 는 사이트 전역이
 * 함께 쓰는 부품이라 통째로 고치면 다른 화면 배지에 전부 테두리가 생긴다.
 */
.cal_legend .badges .badge {
	border: var(--border-width-thin) solid var(--color-border-brand-default);
}

.cal_legend .badges .badge_neutral {
	background: var(--color-bg-surface-default);
	border-color: var(--color-border-default);
}

.cal_legend .badges .badge_danger {
	border-color: var(--color-border-danger-default);
}

.cal_legend .badges .badge_secondary {
	border-color: var(--color-border-secondary-default);
}

/*
 * 회원 탈퇴 모달의 잔여 안내 두 줄 — Figma `Remaining row` 530:44365 (2026-09-07 발주사 QA).
 *
 * 발주사: *"수강 남은 회차와 사용 가능한 보충 쿠폰 사이의 라인을 삭제하고, 사이 여백을
 * 8px 로 수정 부탁드립니다."*
 *
 * `design.css:10080` 은 `.remain` **한 줄마다** `border-top` + `padding-top:16` 을 두고,
 * 부모(`.withdraw_box`)의 `gap:16` 이 그 위에 또 얹힌다 — 그래서 두 줄 사이에도 선이
 * 그어지고 간격이 32 로 벌어졌다. 개정본은 **두 줄을 한 묶음**으로 보고 선을 묶음 위에
 * 한 번만 긋는다 (Figma: 묶음 프레임에 stroke #e5e7e9 · padTB 16 · gap 8).
 *
 * 값은 토큰 그대로다 — `--space-lg` 16 · `--space-sm` 8 이 피그마 수치와 같다.
 *
 * 🚫 `design.css` 를 고치지 말 것 — 원본이 단일 진실이다 (이 파일 머리주석).
 */
.withdraw_box .remain_group {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
	padding-top: var(--space-lg);
	border-top: var(--border-width-thin) solid var(--color-border-default);
}

/* 선과 여백은 묶음이 갖는다 — 줄 자신은 아무것도 갖지 않는다 */
.withdraw_box .remain_group .remain {
	padding-top: 0;
	border-top: 0;
}
