/* site/common.css — 누리집 전역 공통 (베이스 · 공용 컴포넌트)
   원천: 목업 추출 샘플 CSS(docs/archive/설계/화면/샘플/샘플-공통-누리집.css)를
   common / layout / pages 로 분리한 것. 토큰은 design-system/design-system/tokens.css(정본)를 먼저 링크한다. */

/* ── 베이스 ── */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; overflow-x: hidden; /* 닫힌 드로어(translateX 100%)가 가로 스크롤 영역을 만들지 않도록 */ }
body {
  font-family: var(--font-sans);
  color: var(--c-text);
  background: var(--c-white);
  margin: 0;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
.container { max-width: var(--container); margin: 0 auto; padding-left: var(--gutter); padding-right: var(--gutter); }
/* grid 컬럼이 내부 nowrap 텍스트(min-content)에 밀려 화면 밖으로 늘어나지 않도록 — 새 grid를 만들면 여기에 추가한다 */
.home-widgets > *, .gallery-grid > *, .program-grid > *, .apply-layout > *, .site-nav > *, .megamenu__cols > * { min-width: 0; }
/* 표 칸과 라벨-값 목록의 값 칸 — 한글은 띄어쓰기에서만 줄을 나눈다.
   표 칸은 break-word로 긴 덩어리를 칸 끝에서만 꺾는다 — anywhere는 자동 표 배치가 글자 열의 최소 폭을 한 글자로 잡아 글자마다 꺾이게 한다.
   값 칸은 표가 아니라 그 걱정이 없으므로 anywhere로 긴 영문·이메일·URL을 칸 끝에서 꺾어 칸 밖으로 넘치지 않게 한다.
   에디터로 쓴 본문 안의 표는 .editor-body가 따로 정하므로 뺀다. :where로 우선순위를 0으로 두어 화면 CSS가 칸마다 정한 줄바꿈이 늘 이긴다 */
:where(td:not(.editor-body td)) { word-break: keep-all; overflow-wrap: break-word; }
:where(dd:not(.editor-body dd)) { word-break: keep-all; overflow-wrap: anywhere; }
/* 줄 전체가 진입 링크인 표 목록 — 단순 조회용 목록의 표에 .row-linked를 붙이고, 줄마다 상세로 가는 링크 하나에 .row-link를 붙인다.
   링크의 누르는 영역을 ::after로 줄 크기만큼 넓혀 줄의 빈 곳을 눌러도 그 링크를 누른 것이 된다 — 진짜 링크라 새 탭 열기·키보드 이동이 그대로 된다.
   좁은 폭에서 줄이 카드로 바뀌어도 같다. 마우스를 올리면 줄 바탕이 바뀌어 누를 수 있음을 보인다 */
table.row-linked tbody tr:has(.row-link) { position: relative; cursor: pointer; }
table.row-linked .row-link::after { content: ''; position: absolute; inset: 0; }
table.row-linked tbody tr:has(.row-link):hover,
table.row-linked tbody tr:has(.row-link):hover td { background: var(--c-surface-tint); }
/* 표 열 폭 — 관리자 표(admin.css)와 같은 이름의 클래스를 머리글 th에 붙인다. 화면 CSS나 style 속성으로 열 폭을 주지 않는다.
   형식이 정해진 값(날짜·일시·기간·상태·여부·첨부 표시·코드·구분·수)의 열은 그 형식의 가장 긴 값이 누리집 글자 크기(15px)와 칸 여백까지 한 줄에 드는 폭이고,
   칸은 줄을 바꾸지 않는다. 상태·여부는 세 언어 문구 가운데 가장 긴 배지가 든다 — 가장 긴 값이 다른 접수 상태와 처리 상태는 클래스를 나눈다.
   머리글이 그보다 길면 머리글이 줄을 바꾸지 않아 그만큼 넓어진다.
   길이가 정해지지 않은 값(이름·학교명·제목·내용·항목 이름·기관명·연락처)의 열도 열 종류마다 폭을 고정하고, 그 폭 안에서 칸 끝에서 꺾는다 —
   폭을 width와 min-width에 함께 두어 표가 틀보다 넓을 때 자동 표 배치가 그 열을 폭 아래로 누르지 않게 한다.
   게시판형 목록(.board)과 정적 페이지 표(.data-table) 둘 다에 걸린다 */
:is(table.board, table.data-table) .c-date { width: 110px; }            /* 날짜 yyyy.MM.dd — 게시일·작성일·제출일 */
:is(table.board, table.data-table) .c-datetime { width: 150px; }        /* 일시 yyyy.MM.dd HH:mm — 신청·지원 일시 */
:is(table.board, table.data-table) .c-month { width: 90px; }            /* 달 yyyy.MM — 등록 월 */
:is(table.board, table.data-table) .c-date-range { width: 200px; }      /* 날짜 기간 yyyy.MM.dd ~ yyyy.MM.dd */
:is(table.board, table.data-table) .c-datetime-range { width: 300px; }  /* 일시 기간 yyyy.MM.dd HH:mm ~ yyyy.MM.dd HH:mm — 접수 기간 */
:is(table.board, table.data-table) .c-status { width: 130px; }          /* 상태 배지 — 공고의 접수 상태(준비·진행·마감)·강의시간 상태. 베트남어 Đang nhận이 든다 */
:is(table.board, table.data-table) .c-apply-status { width: 230px; }    /* 신청·지원의 처리 상태 배지 — 마이페이지. 영어 Passed document screening이 든다 */
:is(table.board, table.data-table) .c-yn { width: 160px; }              /* 여부 배지 — 답변 여부 */
:is(table.board, table.data-table) .c-attach { width: 72px; }           /* 첨부 표시 — 클립 아이콘 하나 */
:is(table.board, table.data-table) .c-code { width: 90px; }             /* 짧은 코드 — 반 구분 코드·TOPIK 등급(영어 Level 1이 든다) */
:is(table.board, table.data-table) .c-type { width: 120px; }            /* 구분 — TOPIK I·TOPIK II */
:is(table.board, table.data-table) .c-count { width: 80px; }            /* 수 — 조회수·차시 */
:is(table.board, table.data-table) .c-name { width: 200px; min-width: 200px; }     /* 사람 이름 — 작성자. 네 낱말 베트남어 이름이 한 줄에 든다 */
:is(table.board, table.data-table) .c-school { width: 250px; min-width: 250px; }   /* 학교명 — Trường THPT Lê Quý Đôn이 한 줄에 들고 그보다 긴 이름은 꺾는다 */
:is(table.board, table.data-table) .c-title { width: 280px; min-width: 280px; }    /* 제목·공고명·강좌명·상담 내용 */
:is(table.board, table.data-table) .c-content { width: 280px; min-width: 280px; }  /* 내용·설명 — 보유 기간·위탁 업무·하는 일·등급 결정처럼 문장으로 적는 값 */
:is(table.board, table.data-table) .c-item { width: 240px; min-width: 240px; }     /* 항목 이름 — 처리방침의 자료·구분 */
:is(table.board, table.data-table) .c-org { width: 270px; min-width: 270px; }      /* 기관·업체명 — 영어 Privacy Infringement Report Centre가 한 줄에 든다 */
:is(table.board, table.data-table) .c-contact { width: 310px; min-width: 310px; }  /* 연락처 — 전화와 URL을 함께 적은 값이 한 줄에 든다 */
/* 남는 자리 — 열 폭의 합이 표 상자보다 좁으면 표마다 길이가 정해지지 않은 값의 열 하나가 남는 자리를 받는다.
   그 열의 머리글에 폭 클래스와 함께 c-fluid를 붙이고(한 표에 하나), 고르는 순서는 제목·내용·주소·이메일·학교명·이름이다.
   붙은 열은 폭 클래스의 값을 최소 폭으로 지키고, 나머지 열은 정한 폭 그대로다. 카드로 바뀌는 좁은 폭에서는 머리글이 없으므로 표로 보일 때만 건다 */
@media (min-width: 768px) {
  :is(table.board, table.data-table) .c-fluid { width: auto; }
}
/* 칸의 줄바꿈 — 열 폭 클래스는 머리글에만 붙으므로, 칸은 같은 자리(n번째) 머리글의 클래스로 고른다.
   좁은 폭에서 카드로 바뀌는 게시판형 목록은 카드의 줄바꿈을 화면 CSS가 정하므로 표로 보일 때(768px 이상)만 건다 */
table.data-table:has(> thead > tr > th:nth-child(1):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(1):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(2):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(2):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(3):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(3):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(4):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(4):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(5):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(5):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(6):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(6):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(7):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(7):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(8):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(8):not([colspan]) { white-space: nowrap; }
table.data-table:has(> thead > tr > th:nth-child(1):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(1):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(2):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(2):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(3):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(3):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(4):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(4):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(5):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(5):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(6):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(6):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(7):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(7):not([colspan]) { overflow-wrap: anywhere; }
table.data-table:has(> thead > tr > th:nth-child(8):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(8):not([colspan]) { overflow-wrap: anywhere; }
@media (min-width: 768px) {
  table.board:has(> thead > tr > th:nth-child(1):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(1):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(2):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(2):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(3):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(3):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(4):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(4):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(5):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(5):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(6):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(6):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(7):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(7):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(8):is(.c-date, .c-datetime, .c-month, .c-date-range, .c-datetime-range, .c-status, .c-apply-status, .c-yn, .c-attach, .c-code, .c-type, .c-count)) > tbody > tr > td:nth-child(8):not([colspan]) { white-space: nowrap; }
  table.board:has(> thead > tr > th:nth-child(1):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(1):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(2):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(2):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(3):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(3):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(4):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(4):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(5):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(5):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(6):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(6):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(7):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(7):not([colspan]) { overflow-wrap: anywhere; }
  table.board:has(> thead > tr > th:nth-child(8):is(.c-name, .c-school, .c-title, .c-content, .c-item, .c-org, .c-contact)) > tbody > tr > td:nth-child(8):not([colspan]) { overflow-wrap: anywhere; }
}
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
[hidden] { display: none !important; }

/* ── 에디터로 쓴 본문 — 누리집 본문, 에디터 작성 영역, 메일 발송 때 박는 인라인이 모두 이 블록을 쓴다 ── */
.editor-body { font-family: var(--font-sans); font-size: var(--fs-16); line-height: var(--lh-loose); color: var(--c-text); overflow-wrap: anywhere; }
.editor-body p { margin: 0 0 var(--sp-6); }
.editor-body a { color: var(--c-accent); text-decoration: underline; }
.editor-body ul, .editor-body ol { margin: 0 0 var(--sp-6); padding-left: var(--sp-6); }
.editor-body img { max-width: 100%; height: auto; }
.editor-body table { border-collapse: collapse; max-width: 100%; margin: 0 0 var(--sp-6); }
.editor-body th, .editor-body td { border: var(--bd); padding: var(--sp-2) var(--sp-3); }

/* ── 버튼 (components/buttons.html) ── */
/* 버튼은 담긴 상자의 폭을 넘지 않는다 — 글이 상자보다 길면 버튼 안에서 줄을 바꾸고 그만큼 키가 커진다(높이는 최소값만 둔다).
   한 줄에 드는 버튼은 줄 안에서 눌려 꺾이지 않게 줄어들지 않는다(flex-shrink: 0) — 자리가 모자라면 묶음이 버튼을 다음 줄로 내린다 */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); flex-shrink: 0; min-height: 48px; max-width: 100%; padding: var(--sp-2) var(--sp-6); border-radius: var(--r-md); font-size: var(--fs-16); font-weight: var(--fw-semibold); line-height: var(--lh-snug); border: none; text-align: center; word-break: keep-all; overflow-wrap: break-word; }
.btn-sm { min-height: 40px; padding: var(--sp-1) var(--sp-4); font-size: var(--fs-14); }
/* 버튼을 가로로 늘어놓은 묶음은 자리가 모자라면 버튼을 다음 줄로 내린다. :where로 우선순위를 0으로 두어 화면 CSS가 정한 줄바꿈이 늘 이긴다 */
:where(:has(> .btn)) { flex-wrap: wrap; }
.btn-navy { background: var(--c-primary); color: var(--c-on-primary); }
.btn-navy:hover { background: var(--c-primary-hover); }
.btn-teal { background: var(--c-accent); color: var(--c-on-accent); }
.btn-teal:hover { background: var(--c-accent-hover); }
.btn-outline { background: var(--c-white); border: var(--bd); color: var(--c-primary); }
.btn-outline:hover { border-color: var(--c-primary); background: var(--c-surface-tint); }
/* 되돌릴 수 없는 동작(탈퇴 등) — 관리자 .btn-danger와 같은 모양이고, 옅은 빨강 비율은 로그인 오류 띠(login.css)와 같다 */
.btn-danger { background: var(--c-white); border: 1px solid color-mix(in srgb, var(--c-danger) 40%, var(--c-white)); color: var(--c-danger); }
.btn-danger:hover { border-color: var(--c-danger); background: color-mix(in srgb, var(--c-danger) 8%, var(--c-white)); }
/* 되돌릴 수 없는 동작을 마지막으로 확정하는 버튼(확인창의 탈퇴 등) — 빨강으로 채운다 */
.btn-danger-solid { background: var(--c-danger); color: var(--c-white); }
.btn-danger-solid:hover { background: color-mix(in srgb, var(--c-danger) 85%, black); }
.btn-block { width: 100%; }
.btn:disabled { opacity: .5; cursor: default; }
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: var(--r-full); border: var(--bd); background: var(--c-white); color: var(--c-text-mute); padding: 0; }
.icon-btn.accent { border-color: var(--c-accent); color: var(--c-accent); }
/* 패널 머리의 「더보기」 글자 단추 — 버튼 카드의 「우측 +」 변형을 패널 머리 높이(32px)에 맞추고 icon-btn.accent의 테두리·색을 입혔다 */
.more-btn { display: inline-flex; align-items: center; gap: 4px; height: 32px; padding: 0 var(--sp-3); border-radius: var(--r-full); border: var(--bd); border-color: var(--c-accent); background: var(--c-white); color: var(--c-accent); font-size: var(--fs-14); font-weight: var(--fw-semibold); text-decoration: none; white-space: nowrap; }
.more-btn:hover { border-color: var(--c-accent-hover); color: var(--c-accent-hover); }

/* ── 폼 프리미티브 (components/forms.html) ── */
.field { display: flex; flex-direction: column; gap: 8px; margin-bottom: var(--sp-6); }
/* .field-label은 라디오 그룹처럼 칸이 여럿이라 label로 묶을 수 없는 자리의 항목 이름이다 */
.field > label, .field > .field-label { font-size: var(--fs-16); font-weight: var(--fw-semibold); color: var(--c-text); }
.req { color: var(--c-danger); }
.input { height: 47px; border-radius: var(--r-sm); border: var(--bd); padding: 0 16px; font-family: var(--font-sans); font-size: var(--fs-15); color: var(--c-text); background: var(--c-white); width: 100%; }
.input::placeholder { color: var(--c-gray-500); }
.input:focus { outline: none; border-color: var(--c-accent); box-shadow: var(--shadow-focus); }
.input[readonly] { background: var(--c-gray-100); color: var(--c-text-soft); }
.textarea { min-height: 130px; padding: 14px 16px; border-radius: var(--r-sm); border: var(--bd); font-family: var(--font-sans); font-size: var(--fs-15); color: var(--c-text); resize: none; width: 100%; }
.textarea:focus { outline: none; border-color: var(--c-accent); box-shadow: var(--shadow-focus); }
.help { font-size: var(--fs-13); color: var(--c-text-mute); }
/* 휴대전화 칸 — 나라 고르기와 번호 칸을 한 줄에 둔다(fragments/phone-field). 나라 칸은 이름이 길어도 번호 칸을 밀어내지 않게 폭을 묶고,
   아주 좁은 폭에서는 나라 칸이 위로 쌓인다 */
.phone-input { display: flex; gap: var(--sp-2); }
.phone-input .select-wrap { flex: 0 1 42%; min-width: 0; max-width: 220px; }
.phone-input .select-wrap select { width: 100%; min-width: 0; height: 47px; text-overflow: ellipsis; }
.phone-input .input { flex: 1 1 0; min-width: 0; }
@media (max-width: 479px) {
  .phone-input { flex-direction: column; }
  .phone-input .select-wrap { flex-basis: auto; max-width: none; }
  .phone-input .input { flex: none; }
}
/* 글자 수 표시 — char-count.js가 data-max-chars 칸 바로 아래에 붙인다. 상한을 넘으면 지금 글자 수만 경고색이다 */
.char-count { margin: var(--sp-1) 0 0; font-size: var(--fs-13); color: var(--c-text-mute); text-align: right; }
.char-count.is-over .char-count__now { color: var(--c-danger); font-weight: var(--fw-semibold); }
.checkbox { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: var(--fs-15); color: var(--c-text); font-weight: var(--fw-semibold); }
.checkbox input { position: absolute; opacity: 0; }
.chk { width: 20px; height: 20px; border-radius: 4px; border: var(--bd); display: inline-flex; align-items: center; justify-content: center; background: var(--c-white); flex-shrink: 0; color: transparent; }
.checkbox input:checked + .chk { background: var(--c-primary); border-color: var(--c-primary); color: var(--c-on-primary); }
.checkbox input:focus-visible + .chk { box-shadow: var(--shadow-focus); }
/* 비밀번호 보기 — common.js가 비밀번호 칸을 감싸고 버튼을 넣는다 */
.pw-wrap { position: relative; display: block; }
.pw-wrap .input { padding-right: 46px; }
/* 엣지가 비밀번호 칸 안에 그리는 자체 보기 버튼은 전부 숨긴다 — 보기 버튼은 우리 것 하나만 둔다 */
::-ms-reveal { display: none; }
.pw-toggle { position: absolute; top: 0; right: 0; height: 100%; width: 42px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; background: none; color: var(--c-gray-500); cursor: pointer; }
.pw-toggle:hover { color: var(--c-primary); }
.pw-toggle:focus-visible { outline: none; border-radius: var(--r-sm); box-shadow: var(--shadow-focus); }
.radio { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-15); cursor: pointer; }
.radio input { accent-color: var(--c-primary); width: 18px; height: 18px; }
/* 라디오 여럿은 한 줄에 놓고 좁은 폭에서만 접힌다 */
.radio-group { display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-5); padding: var(--sp-1) 0; }
.select-wrap { position: relative; }
.select-wrap select {
  appearance: none; -webkit-appearance: none;
  height: 46px; min-width: 120px;
  border: var(--bd); border-radius: var(--r-sm);
  padding: 0 38px 0 14px;
  font-size: var(--fs-15); font-family: inherit; color: var(--c-text); background: var(--c-white);
}
.select-wrap select:focus { outline: none; border-color: var(--c-accent); box-shadow: var(--shadow-focus); }
.select-wrap > svg { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--c-text-mute); }

/* ── 하위 메뉴 탭 (navigation/tabs.html) — 대분류 하위 페이지 이동 · 게시판 전환. 실서비스에서 링크라 a·button 모두 받는다 ── */
.board-tabs { display: flex; gap: var(--sp-2); border-bottom: var(--bd); margin-bottom: var(--sp-8); flex-wrap: wrap; }
.board-tabs a, .board-tabs button {
  background: none; border: none; border-bottom: 3px solid transparent;
  padding: var(--sp-3) var(--sp-5);
  font-size: var(--fs-17); font-weight: var(--fw-semibold); color: var(--c-text-mute);
}
.board-tabs .is-active { color: var(--c-primary); border-bottom-color: var(--c-primary); }

/* ── 상태 배지 (components/badges-labels.html의 상태 배지 pill) — 진행·완료는 틸, 마감·대기는 회색 ── */
.badge {
  display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 14px;
  border-radius: var(--r-full); font-size: var(--fs-13); font-weight: var(--fw-bold);
  color: var(--c-on-primary); white-space: nowrap;
}
.badge--notice  { background: var(--c-state-notice); }
.badge--ongoing { background: var(--c-state-ongoing); }
.badge--closed  { background: var(--c-state-closed); }

/* ── 모달 (<dialog> 표준) ── */
dialog.modal { border: none; padding: 0; background: transparent; max-width: calc(100vw - var(--sp-5) * 2); }
dialog.modal::backdrop { background: rgba(2, 29, 54, .5); }
/* × 닫기 줄 — 모달 머리의 첫 줄로, 제목 위에 따로 한 줄을 차지하고 × 버튼을 그 줄의 오른쪽 끝에 둔다 */
.modal-x-row { display: flex; justify-content: flex-end; }
.modal-x {
  display: flex; align-items: center; justify-content: center; width: 32px; height: 32px;
  border: 0; border-radius: var(--r-full); background: none; color: var(--c-text-mute); cursor: pointer;
}
.modal-x:hover { background: var(--c-surface-mute); color: var(--c-text); }
.modal-x:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.confirm-modal { background: var(--c-white); border-radius: var(--r-xl); box-shadow: var(--shadow-lg); width: 420px; max-width: 100%; padding: var(--sp-8); text-align: center; }
/* h3는 공용 확인 모달(fragments/confirm.html, showConfirm)의 제목이다 */
.confirm-modal h3, .confirm-modal h4 { margin: 0 0 var(--sp-3); font-size: var(--fs-20); font-weight: var(--fw-bold); color: var(--c-text); }
.confirm-modal p { margin: 0 0 var(--sp-6); font-size: var(--fs-15); color: var(--c-text-soft); line-height: var(--lh-base); }
.confirm-modal .cm-actions { display: flex; justify-content: center; gap: var(--sp-3); }
/* 확인·안내 모달의 × 닫기 줄은 상자 안쪽 여백을 거슬러 × 버튼을 모서리에서 --sp-3만큼 떨어진 자리에 둔다 */
.confirm-modal > .modal-x-row { margin: calc(var(--sp-3) - var(--sp-8)) calc(var(--sp-3) - var(--sp-8)) var(--sp-1) 0; }
/* 공용 안내 모달 — <dialog>는 fragments/notice.html, 열기는 common.js의 showNotice.
   아이콘·결과 제목·할 일 문장을 가운데에 쌓고 확인 버튼이 가로를 채운다. 아이콘은 조각의 두 SVG 가운데 하나만 보인다 */
.notice-modal { background: var(--c-white); border-radius: var(--r-xl); box-shadow: var(--shadow-lg); width: 420px; max-width: 100%; padding: var(--sp-8) var(--sp-7) var(--sp-6); text-align: center; word-break: keep-all; }
.notice-modal > .modal-x-row { margin: calc(var(--sp-3) - var(--sp-8)) calc(var(--sp-3) - var(--sp-7)) 0 0; }
.notice-modal .nm-icon { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; margin: 0 auto var(--sp-3); border-radius: var(--r-full); background: var(--c-teal-50); color: var(--c-teal-500); }
.notice-modal .nm-icon svg[hidden] { display: none; }
.notice-modal h3 { margin: 0; font-size: var(--fs-18); font-weight: var(--fw-bold); color: var(--c-text); }
.notice-modal .nm-message:not(:empty) { margin-top: var(--sp-2); }
.notice-modal .nm-message p { margin: 0; font-size: var(--fs-15); color: var(--c-text-soft); line-height: var(--lh-base); }
.notice-modal .btn { width: 100%; height: 44px; margin-top: var(--sp-6); }

/* ── 토스트 — 컨테이너는 fragments/toast.html, 표시는 common.js의 showToast.
   관리자(admin.css)와 같은 클래스·같은 배치이고 글자 크기만 누리집 토큰을 쓴다.
   컨테이너는 화면 폭의 90%(넓은 화면은 720px)로 폭을 정해 두고 좌우 자동 여백으로 가운데에 둔다 —
   폭 없이 left: 50%로 밀면 내용에 맞춰 줄어드는 폭의 상한이 화면의 오른쪽 절반이 되어, 좁은 화면에서 긴 문구가 반 폭으로 접힌다.
   알림 하나는 그 안에서 내용 폭만큼 그려진다 ── */
.toast-c {
  position: fixed; left: 0; right: 0; bottom: 40px; margin: 0 auto; width: min(90vw, 720px);
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  z-index: var(--z-toast); pointer-events: none;
}
.toast {
  background: var(--c-navy-800); color: var(--c-on-primary); font-size: var(--fs-15); font-weight: var(--fw-medium);
  padding: 11px var(--sp-6); border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  max-width: 100%; line-height: var(--lh-base); word-break: keep-all;
  opacity: 0; transform: translateY(16px); text-align: center;
  /* 메시지 안의 줄바꿈 문자에서 줄을 나눈다 — 두 문장 이상인 메시지는 문장이 끝날 때마다 줄바꿈 문자를 넣어 온다 */
  white-space: pre-line;
  transition: opacity var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}
.toast.is-show { opacity: 1; transform: none; }
.toast.is-success { background: var(--c-success); }
.toast.is-error { background: var(--c-danger); }
.toast.is-warning { background: var(--c-warning); color: var(--c-navy-900); }

/* ── 페이지 번호 (components/table-pagination.html) — 마크업은 fragments/pagination.html의 site 조각이다 ── */
.pager { display: flex; align-items: center; justify-content: center; gap: var(--sp-2); padding: var(--sp-6) 0 var(--sp-2); }
.pager a, .pager .is-disabled {
  display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  border-radius: var(--r-full); color: var(--c-text-mute); font-size: var(--fs-14);
}
.pager a:hover { color: var(--c-primary); }
.pager a.is-current { background: var(--c-surface-tint); color: var(--c-primary); font-weight: var(--fw-bold); }
/* 첫 쪽의 맨 앞·이전, 마지막 쪽의 다음·맨 뒤 — 자리는 지키고 누를 수 없음을 옅은 색으로 보인다 */
.pager .is-disabled { color: var(--c-gray-400); cursor: default; }
