/*
 * 밀리AI 컴포넌트 스타일.
 * 1) Bootstrap 컴포넌트 재테마: 컴파일된 bootstrap.css가 컴포넌트별로 하드코딩한
 *    로컬 커스텀 프로퍼티(--bs-btn-bg 등)를 --mili-* 토큰으로 다시 선언한다.
 * 2) 밀리AI 전용 신규 컴포넌트: 히어로 배너, 섹션 헤더, 코스 카드, 인증 카드 등.
 * 자세한 사용법은 docs/design-system.md 참고.
 */

/* ===== 0) 수치 서체 유틸리티 =====
   시안은 대시보드에서 **홀로 읽히는 숫자**(레벨·점수·진행률·연속 학습일)에만 Orbitron 을 쓴다.
   날짜·차시·"3일차" 처럼 한글 문맥 안에서 읽는 숫자에는 쓰지 않는다(DESIGN.md 규약).
   굵기는 이 클래스가 정하지 않고 붙이는 컴포넌트가 정한다. */
.mili-metric {
    font-family: var(--mili-font-metric);
    font-optical-sizing: auto;
    font-style: normal;
    font-variant-numeric: tabular-nums;
}

/* ===== 1) Bootstrap 컴포넌트 재테마 ===== */

.btn {
    font-weight: 600;
}

.btn-primary {
    --bs-btn-bg: var(--mili-primary-500);
    --bs-btn-border-color: var(--mili-primary-500);
    --bs-btn-hover-bg: var(--mili-primary-600);
    --bs-btn-hover-border-color: var(--mili-primary-600);
    --bs-btn-active-bg: var(--mili-primary-600);
    --bs-btn-active-border-color: var(--mili-primary-600);
    --bs-btn-focus-shadow-rgb: 47, 111, 237;
    --bs-btn-disabled-bg: var(--mili-primary-300);
    --bs-btn-disabled-border-color: var(--mili-primary-300);
}

.btn-outline-primary {
    --bs-btn-color: var(--mili-primary-500);
    --bs-btn-border-color: var(--mili-primary-500);
    --bs-btn-hover-bg: var(--mili-primary-500);
    --bs-btn-hover-border-color: var(--mili-primary-500);
    --bs-btn-active-bg: var(--mili-primary-500);
    --bs-btn-active-border-color: var(--mili-primary-500);
    --bs-btn-focus-shadow-rgb: 47, 111, 237;
}

.form-control:focus,
.form-select:focus {
    border-color: var(--mili-primary-300);
    box-shadow: 0 0 0 0.25rem rgba(47, 111, 237, 0.25);
}

.form-check-input:checked {
    background-color: var(--mili-primary-500);
    border-color: var(--mili-primary-500);
}

.form-check-input:focus {
    border-color: var(--mili-primary-300);
    box-shadow: 0 0 0 0.25rem rgba(47, 111, 237, 0.25);
}

.pagination {
    --bs-pagination-active-bg: var(--mili-primary-500);
    --bs-pagination-active-border-color: var(--mili-primary-500);
}

.page-link {
    border-radius: 0;
    margin: 0 0.125rem;
}

.progress {
    --bs-progress-bar-bg: var(--mili-primary-500);
    border-radius: var(--mili-radius-pill);
}

.card {
    border-color: var(--mili-border);
    box-shadow: var(--mili-shadow-sm);
}

/* ===== 4) 섹션 헤더 ===== */

.section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 1.25rem;
}

.section-header__title {
    font-weight: 800;
    letter-spacing: -0.01em;
    margin: 0;
}

/* ===== 5) 코스 카드 ===== */

/*
 * 과정·프로젝트에는 개별 대표 이미지가 없어 공용 배경 40장(images/course-bg/1~40.webp)을 돌려 쓴다.
 * 어느 장을 쓸지는 서버(user.catalog.CardThumbnail)가 과정번호 % 40 으로 정해 --N 클래스로 내려주므로,
 * 같은 과정은 어느 화면에서도 늘 같은 배경이 나온다. 카드 컨테이너 자체는 catalog-card/pbl-card
 * (화면별 css)로 대체되어 여기 남은 건 배경 변수 지정자(--card-bg)뿐이다.
 */
.course-card__thumb--1 { --card-bg: url("/images/course-bg/1.webp"); }
.course-card__thumb--2 { --card-bg: url("/images/course-bg/2.webp"); }
.course-card__thumb--3 { --card-bg: url("/images/course-bg/3.webp"); }
.course-card__thumb--4 { --card-bg: url("/images/course-bg/4.webp"); }
.course-card__thumb--5 { --card-bg: url("/images/course-bg/5.webp"); }
.course-card__thumb--6 { --card-bg: url("/images/course-bg/6.webp"); }
.course-card__thumb--7 { --card-bg: url("/images/course-bg/7.webp"); }
.course-card__thumb--8 { --card-bg: url("/images/course-bg/8.webp"); }
.course-card__thumb--9 { --card-bg: url("/images/course-bg/9.webp"); }
.course-card__thumb--10 { --card-bg: url("/images/course-bg/10.webp"); }
.course-card__thumb--11 { --card-bg: url("/images/course-bg/11.webp"); }
.course-card__thumb--12 { --card-bg: url("/images/course-bg/12.webp"); }
.course-card__thumb--13 { --card-bg: url("/images/course-bg/13.webp"); }
.course-card__thumb--14 { --card-bg: url("/images/course-bg/14.webp"); }
.course-card__thumb--15 { --card-bg: url("/images/course-bg/15.webp"); }
.course-card__thumb--16 { --card-bg: url("/images/course-bg/16.webp"); }
.course-card__thumb--17 { --card-bg: url("/images/course-bg/17.webp"); }
.course-card__thumb--18 { --card-bg: url("/images/course-bg/18.webp"); }
.course-card__thumb--19 { --card-bg: url("/images/course-bg/19.webp"); }
.course-card__thumb--20 { --card-bg: url("/images/course-bg/20.webp"); }
.course-card__thumb--21 { --card-bg: url("/images/course-bg/21.webp"); }
.course-card__thumb--22 { --card-bg: url("/images/course-bg/22.webp"); }
.course-card__thumb--23 { --card-bg: url("/images/course-bg/23.webp"); }
.course-card__thumb--24 { --card-bg: url("/images/course-bg/24.webp"); }
.course-card__thumb--25 { --card-bg: url("/images/course-bg/25.webp"); }
.course-card__thumb--26 { --card-bg: url("/images/course-bg/26.webp"); }
.course-card__thumb--27 { --card-bg: url("/images/course-bg/27.webp"); }
.course-card__thumb--28 { --card-bg: url("/images/course-bg/28.webp"); }
.course-card__thumb--29 { --card-bg: url("/images/course-bg/29.webp"); }
.course-card__thumb--30 { --card-bg: url("/images/course-bg/30.webp"); }
.course-card__thumb--31 { --card-bg: url("/images/course-bg/31.webp"); }
.course-card__thumb--32 { --card-bg: url("/images/course-bg/32.webp"); }
.course-card__thumb--33 { --card-bg: url("/images/course-bg/33.webp"); }
.course-card__thumb--34 { --card-bg: url("/images/course-bg/34.webp"); }
.course-card__thumb--35 { --card-bg: url("/images/course-bg/35.webp"); }
.course-card__thumb--36 { --card-bg: url("/images/course-bg/36.webp"); }
.course-card__thumb--37 { --card-bg: url("/images/course-bg/37.webp"); }
.course-card__thumb--38 { --card-bg: url("/images/course-bg/38.webp"); }
.course-card__thumb--39 { --card-bg: url("/images/course-bg/39.webp"); }
.course-card__thumb--40 { --card-bg: url("/images/course-bg/40.webp"); }

/* 썸네일 위 뱃지 줄(좌: 분류/NEW, 우: 수강 인원). */
.course-card__flags {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.375rem;
    flex-wrap: wrap;
}

.course-card__badge {
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.25rem 0.625rem;
    border-radius: var(--mili-radius-pill);
    backdrop-filter: blur(2px);
    white-space: nowrap;
}

.course-card__badge--enrolled {
    background: var(--mili-primary-500);
    color: #fff;
}

.course-card__badge--done {
    background: var(--mili-success);
    color: #fff;
}

/* 찜 하트 — 정사각형 아이콘 버튼. */
.wish-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.375rem;
    height: 2.375rem;
    padding: 0;
    border: 1px solid var(--mili-border);
    border-radius: 0;
    background: var(--mili-surface);
    color: var(--mili-muted);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.wish-btn:hover {
    border-color: var(--mili-danger);
    color: var(--mili-danger);
}

.wish-btn--on {
    border-color: var(--mili-danger);
    color: var(--mili-danger);
}

/* 학습레벨 게이지 — 전체 레벨 개수만큼 눈금을 찍고 과정의 레벨 구간만 채운다. */
.level-gauge {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.1875rem;
}

.level-gauge__step {
    width: 0.875rem;
    height: 0.3125rem;
    border-radius: var(--mili-radius-pill);
    background: var(--mili-heat-0);
}

.level-gauge__step--on {
    background: var(--mili-primary-500);
}

/* ===== 5-1) 페이지 헤더 (브레드크럼 + 제목) ===== */

.page-header {
    margin-bottom: 1.25rem;
}

.page-header__breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    margin-bottom: 0.5rem;
    padding: 0;
    list-style: none;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

.page-header__breadcrumb a {
    color: var(--mili-muted);
    text-decoration: none;
}

.page-header__breadcrumb a:hover {
    color: var(--mili-primary-600);
}

.page-header__breadcrumb li + li::before {
    content: "/";
    margin-right: 0.375rem;
    color: var(--mili-border);
}

.page-header__title {
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mili-ink);
    margin-bottom: 0.25rem;
}

.page-header__lead {
    margin: 0;
    color: var(--mili-muted);
}

/* ===== 5-3) 필터 칩 ===== */

/* 모바일에서 가로 스크롤되는 카테고리 칩 줄. */
.filter-chips {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1rem;
    padding-bottom: 0.25rem;
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

.filter-chip {
    flex: 0 0 auto;
    padding: 0.4375rem 0.9375rem;
    border-radius: var(--mili-radius-pill);
    border: 1px solid var(--mili-border);
    background: var(--mili-surface);
    color: var(--mili-body);
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.filter-chip:hover {
    border-color: var(--mili-primary-300);
    color: var(--mili-primary-600);
}

.filter-chip--active {
    background: var(--mili-primary-500) !important;
    border-color: var(--mili-primary-500);
    color: #fff;
}

.filter-chip--active:hover {
    color: #fff;
}

/* 세부 분야 줄(대분류를 고르면 그 아래에 펼쳐진다). 상위 줄과 크기·색으로 층을 구분한다. */
.filter-chips--sub {
    margin-top: -0.5rem;
    padding-left: 1rem;
    border-left: 2px solid var(--mili-border);
}

.filter-chip--sm {
    padding: 0.3125rem 0.75rem;
    font-size: 0.8125rem;
    font-weight: 500;
    background: var(--mili-bg);
}

/* ===== 5-4) 결과 바 ===== */

.result-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.result-bar__count {
    margin: 0;
    color: var(--mili-muted);
    font-size: 0.875rem;
}

.result-bar__count strong {
    color: var(--mili-ink);
}

/* ===== 5-5) 과정 상세 ===== */

/* 본문 + 우측 액션 카드. 모바일은 1열(액션 카드가 위), lg 이상은 2열 + sticky. */
.detail-shell {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    align-items: start;
}

@media (min-width: 992px) {
    .detail-shell {
        grid-template-columns: minmax(0, 1fr) 20rem;
        gap: 1.75rem;
    }

    .detail-shell__aside {
        position: sticky;
        top: calc(var(--mili-navbar-height) + 1rem);
    }
}

.detail-shell__main {
    min-width: 0;
}

/* 과정명을 얹는 표지. 목록 카드 썸네일과 같은 배경(course-card__thumb--N)을 함께 붙여 쓴다. */
.detail-hero {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 0.75rem;
    min-height: 11rem;
    padding: 1.25rem;
    margin-bottom: 1.25rem;
    border-radius: 0;
    color: #fff;
    overflow: hidden;
    isolation: isolate;
    --card-bg: none;
    /* ⚠️ --mili-ink 를 쓰면 안 된다 — 다크 테마에서 흰색으로 뒤집혀 폴백이 "흰 판"이 된다.
       여기서 필요한 건 "테마와 무관하게 어두운 바탕"이라 원시 팔레트 값을 직접 가리킨다. */
    background: var(--mili-slate-900) var(--card-bg) no-repeat center / cover;
}

.detail-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.05) 30%, rgba(0, 0, 0, 0.45) 100%);
    z-index: -1;
}

.detail-hero__badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
}

.detail-hero__title {
    margin: 0;
    font-size: 1.375rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.35;
    text-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}

.detail-hero__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.875rem;
    font-size: 0.875rem;
    opacity: 0.95;
}

/* 레벨·이수시간·기관 등 사실 정보 표. 모바일 1열, sm 이상 2열. */
.detail-facts {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.75rem 1.5rem;
    margin: 0;
    padding: 1.125rem 1.25rem;
    border-radius: 0;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
}

@media (min-width: 576px) {
    .detail-facts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

.detail-facts__row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
}

.detail-facts__label {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    width: 7rem;
    flex: 0 0 auto;
    color: var(--mili-muted);
    font-size: 0.875rem;
}

.detail-facts__value {
    min-width: 0;
    color: var(--mili-ink);
    font-weight: 600;
    font-size: 0.9375rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 우측(모바일에서는 상단) 액션 카드 */
.detail-cta {
    padding: 1.25rem;
    border-radius: 0;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    box-shadow: var(--mili-shadow);
}

/* 모바일에서 액션 카드가 표지 위로 올라올 때만 노출되는 과정명. */
.detail-cta__course {
    margin-bottom: 0.75rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--mili-border);
    font-weight: 700;
    color: var(--mili-ink);
}

.detail-cta__headline {
    font-size: 0.875rem;
    color: var(--mili-muted);
    margin-bottom: 0.25rem;
}

.detail-cta__learners {
    font-size: 1.25rem;
    font-weight: 800;
    color: var(--mili-ink);
    margin-bottom: 1rem;
}

.detail-cta__actions {
    display: flex;
    gap: 0.5rem;
}

.detail-cta__actions .btn {
    flex: 1 1 auto;
}

.detail-cta__note {
    margin-top: 0.875rem;
    padding-top: 0.875rem;
    border-top: 1px solid var(--mili-border);
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

/* 내 진도(수강 중일 때만) */
.detail-progress {
    margin-bottom: 1rem;
}

.detail-progress__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    font-size: 0.8125rem;
    color: var(--mili-muted);
    margin-bottom: 0.375rem;
}

.detail-progress__value {
    font-weight: 700;
    color: var(--mili-primary-600);
}

/* ===== 5-6) 커리큘럼 아코디언 ===== */

.curriculum {
    border: 1px solid var(--mili-border);
    border-radius: 0;
    background: var(--mili-surface);
    overflow: hidden;
}

.curriculum__section + .curriculum__section {
    border-top: 1px solid var(--mili-border);
}

.curriculum__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    padding: 0.875rem 1.125rem;
    border: 0;
    background: transparent;
    color: var(--mili-ink);
    font-weight: 700;
    font-size: 0.9375rem;
    text-align: left;
    cursor: pointer;
}

.curriculum__head:hover {
    background: var(--mili-bg);
}

.curriculum__count {
    flex: 0 0 auto;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--mili-muted);
}

.curriculum__items {
    list-style: none;
    margin: 0;
    padding: 0 1.125rem 0.875rem;
}

.curriculum__item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4375rem 0;
    font-size: 0.875rem;
    color: var(--mili-body);
    border-top: 1px dashed var(--mili-border);
}

.curriculum__item-icon {
    flex: 0 0 auto;
    color: var(--mili-primary-500);
}

.curriculum__item-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.curriculum__item-type {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--mili-muted);
}

/* ===== 5-7) 개설 차수 목록 ===== */

.offering-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.offering-list__item {
    padding: 0.875rem 0;
    border-bottom: 1px solid var(--mili-border);
}

.offering-list__item:last-child {
    border-bottom: none;
}

.offering-list__title {
    font-weight: 600;
    color: var(--mili-ink);
    margin-bottom: 0.25rem;
}

.offering-list__period {
    display: flex;
    flex-wrap: wrap;
    gap: 0.875rem;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

/* ===== 6) 인증(로그인/회원가입) ===== */

/* 브랜드 로고 — 사이드바(_app-sidebar.jsp)·상단바(_app-topbar.jsp)와 같은 SVG(images/home/logo-mili-ai.svg). */
.auth-split__brand {
    display: inline-block;
}

/*
 * 로그인 전용 2단 레이아웃. 좌측은 비주얼, 우측은 폼.
 * 좌측 배경은 --mili-login-visual 로 갈아 끼운다(사진을 넣으려면 variables.css 의 값만 바꾼다).
 * 모바일에서는 비주얼을 감추고 폼만 세로로 채운다.
 */
.auth-split {
    display: grid;
    grid-template-columns: 1fr;
    min-height: 100vh;
}

.auth-split__visual {
    display: block;
    position: relative;
    overflow: hidden;
    min-height: 11rem;
    background: var(--mili-login-visual);
    background-size: cover;
    background-position: center;
}

/* 네온그린 방사형 글로우 — PBL 히어로 배너(pbl/project-list.css .pbl-hero::before)와 같은 조합. */
.auth-split__visual::before {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.16;
    background-image: radial-gradient(circle at 80% 20%, var(--mili-brand) 0%, transparent 60%);
}

.auth-split__visual-caption {
    position: absolute;
    left: 1.5rem;
    bottom: 1.25rem;
    right: 1.5rem;
    color: #fff;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}

.auth-split__visual-caption h2 {
    font-size: 1.125rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: 0.375rem;
}

.auth-split__panel {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding: 2rem 1.5rem 2.5rem;
    /* 입력칸(연회색)이 배경에 묻히지 않도록 폼 쪽은 흰 바탕으로 둔다. */
    background: var(--mili-surface);
}

.auth-split__form {
    width: 100%;
    max-width: 24rem;
    margin: 0 auto;
}

.auth-split__welcome {
    font-size: 1.25rem;
    font-weight: 800;
    line-height: 1.45;
    letter-spacing: -0.02em;
    color: var(--mili-ink);
    margin: 1.25rem 0 2rem;
}

@media (min-width: 768px) {
    .auth-split {
        grid-template-columns: 1fr 1fr;
    }

    .auth-split__visual {
        min-height: 0;
    }

    .auth-split__visual-caption {
        left: 2.5rem;
        bottom: 2.5rem;
        right: 2.5rem;
    }

    .auth-split__visual-caption h2 {
        font-size: 1.5rem;
    }

    .auth-split__panel {
        justify-content: center;
        padding: 3rem;
    }
}

/* 로그인 폼의 알약형 입력/버튼 */
.auth-field {
    background: var(--mili-bg);
    border-color: transparent;
    border-radius: var(--mili-radius-pill);
    padding: 0.75rem 1.25rem;
}

.auth-field:focus {
    background: var(--mili-surface);
}

.auth-submit {
    border-radius: var(--mili-radius-pill);
    padding-block: 0.75rem;
    font-size: 1rem;
}

/* 로그인 하단 보조 링크 (아이디 찾기 | 비밀번호 찾기 | 회원가입) */
.auth-links {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 1.25rem;
    font-size: 0.875rem;
}

.auth-links a {
    color: var(--mili-body);
    text-decoration: none;
    font-weight: 600;
}

.auth-links a:hover {
    color: var(--mili-primary-600);
}

.auth-links__divider {
    width: 1px;
    height: 0.875rem;
    background: var(--mili-border);
}

/* ===== 6-1) 라벨/입력 2열 폼 표 ===== */

/*
 * 회원가입·찾기 화면의 "좌 라벨 / 우 입력" 표.
 * 모바일에서는 라벨이 위로 쌓이고, md 이상에서 2열이 된다(레거시 테이블 레이아웃 대체).
 */
.form-table {
    border-top: 2px solid var(--mili-ink);
    margin-bottom: 1.5rem;
}

.form-table__row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.375rem;
    padding: 0.875rem 0;
    border-bottom: 1px solid var(--mili-border);
}

.form-table__label {
    font-weight: 700;
    color: var(--mili-ink);
    font-size: 0.9375rem;
    margin: 0;
}

.form-table__field {
    min-width: 0;
}

.form-table__hint {
    display: block;
    margin-top: 0.375rem;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

@media (min-width: 768px) {
    .form-table__row {
        grid-template-columns: 11rem minmax(0, 1fr);
        gap: 1rem;
        align-items: center;
        padding: 1rem 0.75rem;
    }

    .form-table__label {
        align-self: center;
    }
}

/* 입력 + 버튼(중복확인 등)을 한 줄에 두는 묶음 */
.field-inline {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

.field-inline > .form-control,
.field-inline > .form-select {
    flex: 1 1 8rem;
    min-width: 0;
}

.field-inline > .btn {
    flex: 0 0 auto;
}

/* 필수 표시 */
.required-mark {
    color: var(--mili-danger);
}

.form-legend {
    font-weight: 700;
    color: var(--mili-ink);
    margin-bottom: 0.5rem;
}

/* ===== 6-2) 약관 동의 ===== */

.terms-block {
    margin-bottom: 1.75rem;
}

.terms-block__title {
    font-size: 1.0625rem;
    font-weight: 800;
    color: var(--mili-ink);
    margin-bottom: 0.5rem;
}

/* 약관 본문 — 고정 높이 스크롤 박스 */
.terms-box {
    height: 11rem;
    overflow-y: auto;
    padding: 1rem 1.125rem;
    border: 1px solid var(--mili-border);
    border-radius: 0;
    background: var(--mili-surface);
    font-size: 0.8125rem;
    line-height: 1.7;
    color: var(--mili-body);
}

.terms-box h4 {
    font-size: 0.875rem;
    font-weight: 700;
    color: var(--mili-ink);
    margin: 0.875rem 0 0.25rem;
}

.terms-box h4:first-child {
    margin-top: 0;
}

.terms-box p,
.terms-box li {
    margin-bottom: 0.25rem;
}

.terms-box ul {
    padding-left: 1.125rem;
    margin-bottom: 0.5rem;
}

.terms-agree {
    margin-top: 0.625rem;
}

/* 전체 동의 줄 */
.terms-all {
    display: flex;
    justify-content: center;
    padding: 1rem;
    margin-bottom: 1.5rem;
    border-radius: 0;
    background: var(--mili-bg);
    border: 1px solid var(--mili-border);
}

.terms-all .form-check-label {
    font-weight: 700;
    color: var(--mili-ink);
}

/* 폼 하단 버튼 줄 (취소 / 저장) */
.form-actions {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 1.5rem;
}

.form-actions .btn {
    min-width: 7rem;
}

.auth-shell {
    display: flex;
    justify-content: center;
    padding-block: 2.5rem;
}

.auth-card {
    width: 100%;
    max-width: 420px;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    border-radius: 0;
    box-shadow: var(--mili-shadow);
    padding: 2rem;
}

.auth-card__title {
    font-weight: 800;
    margin-bottom: 1.5rem;
}

/* ===== 7) 빈 상태 ===== */

.empty-state {
    padding: 3rem 1rem;
    text-align: center;
    color: var(--mili-muted);
    background: var(--mili-surface);
    border: 1px dashed var(--mili-border);
    border-radius: 0;
}

/* ===================================================================
   8) 앱 셸 (좌측 사이드바 + 상단바 + 본문) — 모든 화면 공용
   -------------------------------------------------------------------
   짝이 되는 마크업 3개:
     common/_app-sidebar.jsp   좌측 IA
     common/_app-topbar.jsp    상단 유틸리티
     .app-frame / .app-frame__body / .app-page   (호출 화면이 직접 씀)

   한때 같은 얼개가 다섯 벌로 복제돼 있었다 — .home-layout / .mypage-dash-layout /
   .catalog-layout / .pbl-layout / .assess-layout. 전부 이 클래스로 수렴시켰고
   화면 전용 CSS 에는 세로 여백 같은 화면 고유값만 남겼다. **새 화면은 이 클래스를 쓴다.**

   본문 폭:
     .app-page          --mili-content-narrow (960px)  읽기·폼형 기본값
     .app-page--wide    --mili-content-max    (1180px) 목록형 기본값

   ⚠️ **폭을 억지로 통일하지 말 것.** 한 번 전 화면을 1180/960 두 벌로 수렴시켰다가 되돌렸다.
   화면마다 시안 실측값이 다르고(VOD 목록 1360 / VOD 상세 1120 / PBL 1200 / 역량진단 900),
   카드 그리드 열 수와 읽기 줄 길이가 그 폭에 맞춰져 있어 좁히면 배치가 어긋난다.
   실측값이 있는 화면은 자기 CSS 에서 max-width 를 덮어쓴다.

   사이드바 폭(--mili-sidebar-width, 271px)은 더 민감하다 — 홈이
   1920 = 271 + 1649 산술로 고정 캔버스를 얹기 때문에 이 값을 바꾸면 홈이 깨진다.
   자세한 것은 theme/variables.css 의 해당 토큰 주석 참고.
   =================================================================== */

.app-frame {
    display: flex;
    min-height: 100vh;
}

.app-frame__body {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    background: var(--mili-canvas);
}

/* ---------- 상단바 ----------
   마크업은 WEB-INF/views/common/_app-topbar.jsp 하나다.

   실측(참고 앱 계산값): 배경은 블러 유리가 아니라 **불투명 단색**(--mili-app-bg 상당,
   우리 토큰으론 --mili-canvas)이고 backdrop-filter 는 아예 쓰지 않는다. 패딩도
   12px 20px 이고 1280px 이상에서 좌우만 32px 로 넓어진다(위아래는 그대로 12px —
   전에 "16px 24px 8px" 로 위아래가 다른 비대칭값을 썼던 건 실측이 아니라 어림값이었다).
   다크 테마는 테두리도 없다(라이트에서만 하단 경계선). */
/*
 * 높이(72px, 767px 이하 56px) · 좌우 패딩(--mili-page-padding) · 배경 · 아래 테두리는
 * theme/reference.css 의 .mili-page-header 가 준다(마크업이 두 클래스를 함께 단다).
 * 여기서는 겹침 순서(sticky)만 맡는다 — 값을 다시 쓰면 참고 규칙을 덮어 버린다.
 */
.app-topbar {
    position: sticky;
    top: 0;
    z-index: 30;
}

/* 본문과 같은 열에 가운데 정렬된 내용 줄. 폭·높이는 reference.css 의 .mili-page-header > div 가 준다. */
.app-topbar__inner {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* 아래 테두리 유무는 reference.css 의 .mili-page-header / .mili-light .mili-page-header 가 정한다
   (다크는 없고 라이트만 1px). 좌우 패딩도 --mili-page-padding 이 32/24/20/16px 로 알아서 바뀐다. */

.app-topbar__spacer {
    flex: 1 1 auto;
}

/* 드로어일 때만 보이는 좌측 로고(데스크톱은 사이드바에 로고가 있다). */
.app-topbar__brand {
    display: inline-flex;
    margin-left: 4px;
    text-decoration: none;
}

.app-topbar__brand img {
    display: block;
    width: 90px;
    height: auto;
}

/* 화면 제목. 시안 규약상 전역 헤더에 브레드크럼을 두지 않고 제목으로 위치를 알린다. */
.app-topbar__title {
    margin: 0 0 0 4px;
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--mili-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* 드로어 열기 버튼. 아이콘 버튼과 달리 테두리 없는 고스트다. */
.app-topbar__menu {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px;
    border: 0;
    background: transparent;
    color: var(--mili-fg-muted);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
}

.app-topbar__menu:hover,
.app-topbar__menu:focus {
    color: var(--mili-ink);
}

/* 우상단 아이콘 버튼(검색·알림·테마 토글). 시안: size-9 rounded-[14px] bg-white/5 border-white/10 */
/* 실측: size-10(40px) · border-current/15 · 배경 없음 · opacity 75%→100% (hover 는 배경이
   아니라 불투명도로 표시한다). 이전에는 36px + 배경 채움이었는데 참고는 그 방식을 쓰지 않는다. */
.app-icon-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.15);
    background: transparent;
    color: var(--mili-fg);
    font-size: 1.125rem;
    line-height: 1;
    text-decoration: none;
    opacity: 0.75;
    transition: opacity 160ms ease;
    cursor: pointer;
}

.app-icon-btn:hover,
.app-icon-btn:focus {
    opacity: 1;
    color: var(--mili-ink);
}

/* 읽지 않은 알림 표시 점. */
.app-icon-btn__dot {
    position: absolute;
    top: 7px;
    right: 7px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--mili-brand);
    box-shadow: 0 0 4px rgba(var(--mili-brand-rgb), 0.8);
}

/* 프로필 칩(로그인 시 이름, 비로그인 시 "로그인"). */
.app-chip-user {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 12px 7px 9px;
    border: 1px solid var(--mili-line);
    background: rgba(var(--mili-fg-rgb), 0.05);
    color: var(--mili-ink);
    font-size: 12px;
    font-weight: 700;
    text-decoration: none;
}

.app-chip-user:hover,
.app-chip-user:focus {
    background: rgba(var(--mili-fg-rgb), 0.1);
    color: var(--mili-ink);
}

.app-chip-user__avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    object-position: top;
    background: rgba(0, 0, 0, 0.6);
}

.app-chip-user > .bi {
    font-size: 0.6875rem;
    color: var(--mili-fg-faint);
}

/* 좁은 폭에서는 이름을 접고 아바타만 남긴다(칩이 제목을 밀어내지 않게). */
@media (max-width: 575.98px) {
    .app-chip-user__name,
    .app-chip-user > .bi {
        display: none;
    }

    .app-chip-user {
        padding: 7px;
    }
}

/* 사이드바가 상시 노출되는 폭에서는 드로어 버튼·헤더 로고가 필요 없다.
   ⚠️ 배경·position 은 폭이 아니라 **화면 종류(홈이냐 아니냐)** 로 갈린다 — 참고 앱 실측 결과
   .mili-page-header(비-홈)는 모든 폭에서 sticky + 불투명 배경이고, .mili-home-header(홈)만
   absolute + 그라디언트다. 예전엔 이 갈림을 폭 기준으로 잘못 재현해서 1280px 이상에서
   상단바 배경이 통째로 사라졌었다. 홈 전용 오버라이드는 home.css 의
   `.home-canvas > .app-topbar` 가 별도로 맡는다(그쪽은 1280 을 그대로 쓴다 — 위 주석 참고). */
@media (min-width: 1024px) {
    .app-topbar__menu,
    .app-topbar__brand {
        display: none;
    }

    .app-icon-btn--menu {
        display: none;
    }
}

/* 본문 열. 기본은 읽기·폼형 폭이고, 카탈로그·목록형은 --wide 모디파이어를 붙인다.
   폭을 토큰으로 뽑기 전에는 화면마다 960/900/1200/1360px 로 제각각이었다. */
.app-page {
    max-width: var(--mili-content-narrow);
    margin: 0 auto;
    padding: 8px 32px 56px;
}

.app-page--wide {
    max-width: var(--mili-content-max);
}

@media (max-width: 575.98px) {
    .app-page {
        padding: 8px 16px 40px;
    }
}

/* ---------- 3 depth 탭바 ----------
   1·2 depth 는 사이드바가 그린다(§18의 .app-sidebar__link / __sublink). 여기 항목은
   **같은 2 depth 아래의 형제 화면들**뿐이다 — 예: 마이페이지 > 내학습 아래의
   강의(?tab=vod) · 프로젝트(?tab=pbl) · 로드맵(/mypage/roadmap).
   마크업은 mypage/_learning-tabs.jsp.

   ⚠️ 여기에 2 depth 항목을 다시 넣지 말 것. 예전 이름이 .mypage-tabs 였고 실제로
   마이페이지 하위 화면 전체를 그렸는데, 사이드바와 같은 depth 를 두 곳에서 그린 탓에
   갈라졌다(/mypage 에서는 탭이 아예 안 나왔고, /mypage/assessment-result 에서는
   대응 탭이 없어 아무 것도 켜지지 않았다). 그래서 3 depth 전용으로 좁히고 개명했다.

   ⚠️ .filter-chips 와 섞지 말 것. 그쪽은 같은 화면을 좁히는 **필터**(관심목록 유형,
   로드맵 후보 검색)이고 이쪽은 다른 화면으로 가는 **내비게이션**이다.

   좁은 화면에서는 가로 스크롤한다(.filter-chips 와 같은 방식). */
.app-subtabs {
    display: flex;
    gap: 0.375rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.25rem;
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

.app-subtabs__link {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 0.375rem;
    padding: 0.4375rem 0.875rem;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.06);
    border-radius: var(--mili-radius-pill);
    background: rgba(var(--mili-fg-rgb), 0.03);
    color: var(--mili-fg-muted);
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.app-subtabs__link:hover,
.app-subtabs__link:focus {
    background: rgba(var(--mili-fg-rgb), 0.07);
    color: var(--mili-ink);
}

.app-subtabs__link--active,
.app-subtabs__link--active:hover,
.app-subtabs__link--active:focus {
    border-color: rgba(var(--mili-brand-rgb), 0.25);
    background: rgba(var(--mili-brand-rgb), 0.1);
    color: var(--mili-brand);
}

/* ===================================================================
   8-1) 마이페이지 하위 화면 공용 (머리글 · 패널)
   -------------------------------------------------------------------
   mypage/_page-top.jsp 와 짝이다. 6개 화면(내 학습·내 로드맵·관심 목록·작성한 글·
   역량진단 결과·프로필)이 같은 조각을 쓰므로 화면별 CSS 가 아니라 여기 둔다.
   탭바는 이 절에 있었으나 3 depth 전용 .app-subtabs 로 §8 위쪽에 옮겼다.
   =================================================================== */

.mypage-head {
    margin-bottom: 1.25rem;
}

.mypage-head__eyebrow {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.375rem;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    color: var(--mili-fg-faint);
}

.mypage-head__dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--mili-brand);
    box-shadow: 0 0 8px rgba(var(--mili-brand-rgb), 0.6);
}

.mypage-head__row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.625rem;
}

.mypage-head__title {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--mili-ink);
}

/* 제목 옆 보조 표기(진단일·아이디 등). */
.mypage-head__meta {
    padding: 0.1875rem 0.625rem;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.1);
    border-radius: var(--mili-radius-pill);
    background: rgba(var(--mili-fg-rgb), 0.04);
    color: var(--mili-fg-muted);
    font-size: 0.6875rem;
    font-weight: 600;
}

.mypage-head__lead {
    margin: 0.375rem 0 0;
    font-size: 0.8125rem;
    color: var(--mili-fg-muted);
}

/*
 * 하위 화면의 기본 판. 개요(.mypage-dash-card)와 같은 수치를 쓴다 —
 * 라운드나 표면 농도가 어긋나면 두 화면이 다른 재질로 보인다.
 */
.mypage-panel {
    padding: 20px;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.06);
    border-radius: 0;
    background: rgba(var(--mili-fg-rgb), 0.02);
}

.mypage-panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.mypage-panel__title {
    margin: 0;
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--mili-ink);
}

.mypage-panel__action {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mili-brand);
    text-decoration: none;
}

.mypage-panel__action:hover,
.mypage-panel__action:focus {
    color: var(--mili-brand-strong);
}

/* 2단 배치(프로필·역량진단 결과). 좁으면 1단으로 쌓인다. */
.mypage-2col {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    margin-bottom: 1rem;
}

@media (min-width: 900px) {
    .mypage-2col {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .mypage-2col--aside {
        grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
        align-items: start;
    }
}

/* ===== 9) 대시보드 위젯 카드 ===== */

.widget-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    border-radius: 0;
    box-shadow: var(--mili-shadow-sm);
}

.widget-card__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 1rem 1.25rem 0.25rem;
}

.widget-card__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    color: var(--mili-ink);
}

.widget-card__action {
    font-size: 0.8125rem;
    color: var(--mili-muted);
    text-decoration: none;
    white-space: nowrap;
}

.widget-card__action:hover {
    color: var(--mili-primary-600);
}

.widget-card__body {
    flex: 1 1 auto;
    padding: 0.75rem 1.25rem 1.25rem;
}

/* 위젯 안의 빈 상태는 카드 테두리와 중복되지 않도록 여백을 줄인다. */
.widget-card__body > .empty-state {
    padding: 1.75rem 1rem;
    border: none;
    background: transparent;
}

/* ===== 10) KPI 타일 ===== */

.kpi-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.kpi-tile {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 1rem 1.125rem;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    border-radius: 0;
    box-shadow: var(--mili-shadow-sm);
}

.kpi-tile__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    flex: 0 0 auto;
    border-radius: 0;
    background: var(--mili-primary-100);
    color: var(--mili-primary-600);
    font-size: 1.125rem;
}

.kpi-tile__label {
    display: block;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

.kpi-tile__value {
    display: block;
    font-size: 1.375rem;
    font-weight: 800;
    line-height: 1.2;
    color: var(--mili-ink);
}

.kpi-tile__unit {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--mili-muted);
    margin-left: 0.125rem;
}

/* ===== 11) 알림 스트립 ===== */

.alert-strip {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.alert-strip__icon {
    flex: 0 0 auto;
    font-size: 1.125rem;
}

.alert-strip__body {
    flex: 1 1 auto;
    min-width: 0;
}

/* ===== 12) 주간 학습 스트립 ===== */

.week-strip {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 0.375rem;
    text-align: center;
}

.week-strip__day {
    padding: 0.5rem 0.125rem;
    border-radius: 0;
}

.week-strip__day--today {
    background: var(--mili-brand-soft);
}

.week-strip__label {
    display: block;
    font-size: 0.75rem;
    color: var(--mili-muted);
    margin-bottom: 0.375rem;
}

.week-strip__dot {
    display: inline-block;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--mili-radius-pill);
    background: var(--mili-heat-0);
    line-height: 1.75rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mili-muted);
}

.week-strip__dot--active {
    background: var(--mili-brand);
    color: var(--mili-brand-on);
}

.week-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin-top: 1rem;
    padding-top: 0.875rem;
    border-top: 1px solid var(--mili-border);
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

.week-summary__value {
    font-weight: 700;
    color: var(--mili-ink);
}

/* ===== 13) 연간 잔디 히트맵 ===== */

.heatmap {
    overflow-x: auto;
    padding-bottom: 0.25rem;
}

.heatmap__grid {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(7, 0.75rem);
    gap: 3px;
    min-width: max-content;
}

.heatmap__cell {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0;
    background: var(--mili-heat-0);
}

.heatmap__cell--0 { background: var(--mili-heat-0); }
.heatmap__cell--1 { background: var(--mili-heat-1); }
.heatmap__cell--2 { background: var(--mili-heat-2); }
.heatmap__cell--3 { background: var(--mili-heat-3); }
.heatmap__cell--4 { background: var(--mili-heat-4); }

/* 집계 구간 밖(오늘 이후)의 자리 채움 칸. */
.heatmap__cell--out {
    background: transparent;
}

.heatmap__legend {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    margin-top: 0.625rem;
    font-size: 0.75rem;
    color: var(--mili-muted);
}

.heatmap__legend-item {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0;
}

/* ===== 14) 타임라인 / 수료증 / 로드맵 ===== */

.timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.timeline__item {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    padding: 0.5rem 0;
}

.timeline__marker {
    width: 0.5rem;
    height: 0.5rem;
    margin-top: 0.4375rem;
    flex: 0 0 auto;
    border-radius: var(--mili-radius-pill);
    background: var(--mili-muted);
}

.timeline__marker--earn {
    background: var(--mili-primary-500);
}

.timeline__marker--spend {
    background: var(--mili-border);
}

.timeline__body {
    flex: 1 1 auto;
    min-width: 0;
}

.timeline__summary {
    display: block;
    font-size: 0.875rem;
    color: var(--mili-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.timeline__date {
    font-size: 0.75rem;
    color: var(--mili-muted);
}

.timeline__amount {
    flex: 0 0 auto;
    font-weight: 700;
    font-size: 0.875rem;
    color: var(--mili-muted);
}

.timeline__amount--earn {
    color: var(--mili-primary-600);
}

.cert-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.cert-list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.625rem 0;
    border-bottom: 1px solid var(--mili-border);
}

.cert-list__item:last-child {
    border-bottom: none;
}

.cert-list__title {
    font-size: 0.875rem;
    color: var(--mili-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cert-list__date {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--mili-muted);
}

.roadmap-track {
    padding: 0.625rem 0;
    border-bottom: 1px solid var(--mili-border);
}

.roadmap-track:last-child {
    border-bottom: none;
}

.roadmap-track__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.roadmap-track__title {
    font-weight: 600;
    color: var(--mili-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.roadmap-track__count {
    flex: 0 0 auto;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

/* ===== 15) 프로필 카드 / PBL 팀 현황 / 리더보드 ===== */

.profile-card__meta {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.875rem;
}

.profile-card__meta-item {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.3125rem 0;
    color: var(--mili-body);
}

.profile-card__meta-item > span:first-child {
    color: var(--mili-muted);
}

.team-status__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--mili-border);
}

.team-status__row:last-child {
    border-bottom: none;
}

.team-status__title {
    font-size: 0.875rem;
    color: var(--mili-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.team-status__badge {
    flex: 0 0 auto;
}

.leaderboard {
    list-style: none;
    margin: 0;
    padding: 0;
}

.leaderboard__row {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.375rem;
    border-radius: 0;
}

.leaderboard__row--mine {
    background: var(--mili-primary-100);
}

.leaderboard__rank {
    width: 1.5rem;
    flex: 0 0 auto;
    text-align: center;
    font-weight: 700;
    font-size: 0.875rem;
    color: var(--mili-muted);
}

.leaderboard__rank--top {
    color: var(--mili-primary-600);
}

.leaderboard__name {
    width: 6.5rem;
    flex: 0 0 auto;
    font-size: 0.875rem;
    color: var(--mili-body);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.leaderboard__bar {
    flex: 1 1 auto;
    height: 0.5rem;
    border-radius: var(--mili-radius-pill);
    background: var(--mili-heat-0);
    overflow: hidden;
}

.leaderboard__bar-fill {
    height: 100%;
    border-radius: var(--mili-radius-pill);
    background: var(--mili-primary-500);
}

.leaderboard__value {
    flex: 0 0 auto;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--mili-ink);
}

/* ===== 16) 차트 래퍼 ===== */

/* Chart.js 는 maintainAspectRatio:false 로 쓰므로 래퍼가 높이를 확정해야 한다. */
.chart-box {
    position: relative;
    height: 260px;
}

/* ===== 17) 소속·계급 2단 선택기 ===== */

/*
 * 레거시 miliai(mbrEdit.jsp)의 소속/계급 선택 UI 를 이 디자인 시스템으로 옮긴 것이다.
 * 좌측은 구분 select, 우측은 검색형 드롭다운(소속) 또는 일반 select(계급)다.
 * 부대가 456건이라 통째로 렌더하지 않고 구분을 고른 뒤 AJAX 로 받아 검색한다.
 */

.mili-selector {
    display: flex;
    gap: 0.5rem;
    align-items: flex-start;
    flex-wrap: wrap;
}

/* .form-select 는 width:100% 라 그대로 두면 줄을 독차지해 좌우 배치가 무너진다. */
.mili-selector__category {
    flex: 0 0 auto;
    width: auto;
    min-width: 9rem;
}

/* 검색형 드롭다운 (소속 전용) */
.mili-selector__search {
    position: relative;
    flex: 1 1 16rem;
    min-width: 14rem;
}

.mili-selector__input-wrap {
    display: flex;
    align-items: center;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    border-radius: 0;
}

.mili-selector__input-wrap:focus-within {
    border-color: var(--mili-primary-500);
    box-shadow: 0 0 0 0.2rem var(--mili-primary-100);
}

.mili-selector__input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    padding: 0.5rem 0.75rem;
    font-size: 0.9375rem;
    color: var(--mili-ink);
}

.mili-selector__input:focus {
    outline: none;
    box-shadow: none;
}

.mili-selector__arrow {
    flex: 0 0 auto;
    border: 0;
    background: transparent;
    color: var(--mili-muted);
    padding: 0.5rem 0.75rem;
    line-height: 1;
    cursor: pointer;
}

.mili-selector__arrow:hover {
    color: var(--mili-ink);
}

.mili-selector__list {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    right: 0;
    z-index: 1050;
    max-height: 15rem;
    overflow-y: auto;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    border-radius: 0;
    box-shadow: var(--mili-shadow);
}

.mili-selector__list[hidden] {
    display: none;
}

.mili-selector__items {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
}

.mili-selector__item {
    padding: 0.4375rem 0.75rem;
    font-size: 0.9375rem;
    color: var(--mili-ink);
    cursor: pointer;
}

.mili-selector__item:hover,
.mili-selector__item:focus {
    background: var(--mili-primary-100);
    color: var(--mili-primary-700);
    outline: none;
}

.mili-selector__empty {
    padding: 0.625rem 0.75rem;
    font-size: 0.875rem;
    color: var(--mili-muted);
}

/* 선택 결과 표시: "육군 > 육군본부" */
.mili-selector__selected {
    margin: 0.5rem 0 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--mili-primary-700);
}

.mili-selector__selected[hidden] {
    display: none;
}

/* "수료증에 나타나는 정보이므로..." 안내 문구 */
.mili-selector__hint {
    margin: 0.25rem 0 0;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

@media (max-width: 575.98px) {
    .mili-selector__category,
    .mili-selector__search {
        flex: 1 1 100%;
    }
}


/* ===================================================================
   18) 앱 사이드바 — mili-home-v3 시안의 전역 IA
   -------------------------------------------------------------------
   시안 원본(App.tsx 의 VodDesktopSidebar / SidebarNav / AiCoachCard / MobileSidebar)의
   수치를 그대로 옮겼다. 폭 271px, 우측 라운드 32px, 항목 라운드 14px,
   활성 배경 rgba(180,255,57,.1) + 1px 아웃라인 rgba(180,255,57,.2).
   마크업은 WEB-INF/views/common/_app-sidebar.jsp.

   현재는 홈(/)에서만 쓰지만 상단 navbar(main-layout.jsp) 를 대체할 후보라
   화면 전용 CSS(css/home.css)가 아니라 재사용 컴포넌트로 여기에 둔다.
   =================================================================== */
/* 시안 실측(참고 앱 계산값): width 208 · bg #090A0C · radius 0 · 테두리 없음.
   예전에는 271px 에 우측 모서리를 32px 둥글렸는데 그건 이전 세대(mili-home-v3) 시안이다. */
.app-sidebar {
    /* 시안 LNB 는 fixed inset-y-0 라 본문을 아무리 내려도 늘 같은 자리에 있다.
       sticky + 100dvh 로 같은 결과를 만든다(fixed 와 달리 flex 자리를 그대로 차지해
       본문에 margin-left 를 따로 줄 필요가 없다).
       이걸 안 걸면 긴 화면에서 사이드바가 본문과 함께 밀려 올라가, 하단의
       AI 교관 카드와 계정 메뉴가 문서 맨 아래로 가 버린다(실제로 그렇게 안 보였다). */
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100dvh;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    width: var(--mili-sidebar-width);
    background: var(--mili-sidebar-bg);
    /* 위쪽 모서리만 아주 옅게 띄우는 하이라이트(실측: inset 0 1px 0 rgba(255,255,255,.04)). */
    box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.04);
}

/* 참고 소스 재확인(PortalApp.tsx, 커밋 58ce9b0): px-6 pb-7 pt-10 = 24px 28px 40px. */
.app-sidebar__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 2.5rem 1.5rem 1.75rem;
}

.app-sidebar__brand {
    display: inline-flex;
    text-decoration: none;
}

/* 참고 소스 재확인: 로고 124x46(예전 100x37 실측은 구버전 기준이었다). */
.app-sidebar__brand img {
    display: block;
    width: 124px;
    height: auto;
}

/* 닫기 버튼은 드로어일 때만 쓴다(넓은 화면에서는 사이드바가 상시 노출된다). */
.app-sidebar__close {
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0.25rem;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--mili-fg-faint);
    cursor: pointer;
}

.app-sidebar__close:hover,
.app-sidebar__close:focus {
    color: var(--mili-ink);
}

/* 참고 PortalApp.tsx:308 — nav 는 gap-2(8px) / px-4 py-2(8px 16px). */
.app-sidebar__nav {
    flex: 1 1 auto;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 8px 16px;
}

/*
 * 항목의 **형태와 치수만** 여기서 정한다.
 * 색·테두리·활성/호버(라임 채움 + 13px 코너 브라켓)는 theme/reference.css 의
 * .mili-nav-item 이 맡는다 — 마크업이 두 클래스를 함께 달고 있다.
 * 여기에 상태 색을 다시 쓰면 components.css 가 나중에 로드돼 참고 규칙을 덮어 버린다.
 *
 * 참고 실측: min-h-12(48px) · px-4 py-3(12px 16px) · gap-3.5(14px) · text-[15px] · 아이콘 19px.
 * ⚠️ 비활성 항목에 상시 테두리를 두지 않는다(참고 커밋 bcf66fd "Remove inactive sidebar borders").
 */
.app-sidebar__link {
    position: relative;
    display: flex;
    min-height: 48px;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    border: 0;
    border-radius: 14px;
    background: transparent;
    color: var(--mili-text);
    font-size: 15px;
    line-height: 1.4;
    font-weight: 400;
    text-decoration: none;
    transition: color 180ms ease, background-color 180ms ease, outline-color 180ms ease;
}

.app-sidebar__link:hover,
.app-sidebar__link:focus-visible {
    background: rgba(255, 255, 255, 0.04);
    color: var(--mili-text);
}

/*
 * 그룹 부모는 <button> 이라 UA 기본 배경이 있다. :where() 로 감싸 **명시도 0** 으로 지운다 —
 * 그냥 .app-sidebar__link--group { background: transparent } 로 쓰면 components.css 가
 * reference.css 보다 나중에 로드되므로 .mili-nav-item-active 의 라임 채움을 덮어 버린다.
 */
:where(button.app-sidebar__link) {
    background: transparent;
}

.app-sidebar__icon {
    flex: 0 0 auto;
    width: 19px;
    height: 19px;
}

/* 활성 항목은 참고와 같이 굵기까지 한 단 올린다(font-semibold). 색은 reference.css 가 준다. */
.app-sidebar__link--active {
    font-weight: 600;
}

.app-sidebar__link--active,
.app-sidebar__link--active:hover,
.app-sidebar__link--active:focus-visible,
.app-sidebar__link--current,
.app-sidebar__link--current:hover,
.app-sidebar__link--current:focus-visible {
    background: rgba(180, 255, 57, 0.1);
    outline: 1px solid rgba(180, 255, 57, 0.2);
    color: var(--mili-text);
}

/* ---------- 2 depth 아코디언 ----------
   IA 가 3 depth 라 커뮤니티·역량진단·마이페이지는 하위 항목을 갖는다.
   마크업은 _app-sidebar.jsp — 부모는 <button>(이동하지 않는다), 열림 상태는
   aria-expanded + <ul hidden> 두 속성뿐이고 CSS 도 그 둘만 본다. */
/* 링크와 같은 형태를 물려받고 버튼 UA 기본값(시스템 폰트·가운데 정렬·width:auto)만 지운다
   — 계정 메뉴(.app-sidebar__account-link)에서 이미 쓰는 처리다. */
.app-sidebar__link--group {
    width: 100%;
    border: 0;
    -webkit-appearance: none;
    appearance: none;
    font: inherit;
    font-size: 15px;
    line-height: 1.4;
    text-align: left;
    cursor: pointer;
}

.app-sidebar__caret {
    flex: 0 0 auto;
    margin-left: auto;
    width: 14px;
    height: 14px;
    color: var(--mili-fg-faint);
    transition: transform 160ms ease;
}

.app-sidebar__link--group[aria-expanded="true"] .app-sidebar__caret {
    transform: rotate(180deg);
}

/* 아이콘 폭(16.4)+gap(12) 만큼 들여쓰고 좌측 레일로 종속을 보인다
   (분야 필터의 세부 줄 .filter-chips--sub 이 이미 쓰는 언어). */
.app-sidebar__sub {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0 0 0 12px;
    padding: 2px 0 2px 16px;
    list-style: none;
    border-left: 1px solid rgba(var(--mili-line-rgb), 0.8);
}

/* ⚠️ 필수. 위 display:flex(작성자 스타일)가 UA 의 [hidden]{display:none} 을 이겨서
   접어도 그대로 보인다. 같은 함정을 드로어 스크림에서 이미 겪었다(이 절 맨 아래). */
.app-sidebar__sub[hidden] {
    display: none;
}

/* 하위 항목에는 아이콘을 넣지 않는다 — 들여쓰기와 레일이 이미 위계를 말한다.
   테두리는 투명으로 미리 자리를 잡아 둔다(활성될 때 글자가 1px 밀리지 않게). */
.app-sidebar__sublink {
    display: block;
    padding: 7px 10px;
    border: 0;
    border-radius: 12px;
    outline: 1px solid transparent;
    color: var(--mili-fg-muted);
    font-size: 14px;
    line-height: 20px;
    text-decoration: none;
    transition: background-color 160ms ease, color 160ms ease, outline-color 160ms ease;
}

.app-sidebar__sublink:hover,
.app-sidebar__sublink:focus-visible {
    background: rgba(255, 255, 255, 0.04);
    color: var(--mili-text);
}

/* 1 depth 활성과 같은 세 요소(테두리·채움·글자)를 한 단 낮은 크기로 되풀이한다. */
.app-sidebar__sublink--active,
.app-sidebar__sublink--active:hover,
.app-sidebar__sublink--active:focus-visible {
    outline-color: rgba(180, 255, 57, 0.2);
    background: rgba(180, 255, 57, 0.1);
    color: var(--mili-text);
    font-weight: 600;
}

/* 사용자가 손으로 접었을 때도 "이 안이 현재 위치"라는 표식은 남아야 한다
   (펼쳐져 있으면 활성 하위 항목이 이미 말해 주므로 그때는 조용히 둔다). */
.app-sidebar__link--group[aria-expanded="false"].app-sidebar__link--current {
    background: rgba(180, 255, 57, 0.1);
    outline: 1px solid rgba(180, 255, 57, 0.2);
    color: var(--mili-text);
}

/* ---------- AI 교관 카드 ---------- */
/* 시안 원본에는 없는 우리 추가분이지만, 형태 언어는 각진 프레임으로 맞춘다. */
.app-sidebar__coach {
    margin: 0 17px 1rem;
    padding: 1rem;
    border: 1px solid var(--mili-line);
    background: var(--mili-surface);
}

.app-sidebar__coach-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.5rem;
    font-size: 12px;
    font-weight: 600;
    color: var(--mili-ink);
}

.app-sidebar__coach-title .app-sidebar__icon {
    color: var(--mili-brand-strong);
}

.app-sidebar__coach-desc {
    margin: 0 0 0.5rem;
    font-size: 11px;
    line-height: 1.65;
    color: var(--mili-fg-muted);
}

.app-sidebar__coach-cta {
    display: block;
    width: 100%;
    padding: 0.5rem;
    background: var(--mili-fg-muted);
    color: var(--mili-slate-600);
    font-size: 12px;
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    transition: background-color 160ms ease;
}

.app-sidebar__coach-cta:hover,
.app-sidebar__coach-cta:focus {
    background: var(--mili-ink);
    color: var(--mili-slate-600);
}

/* ---------- 계정 메뉴 (사이드바 최하단) ----------
   로그인 상태에 따라 프로필·로그아웃 / 로그인·회원가입·찾기 로 갈린다.
   주 메뉴보다 한 단 낮은 위계라 아이콘을 키우지 않고 글자도 작게 둔다. */
/* 바닥으로 미는 건 .app-sidebar__nav 의 flex:1 1 auto 가 이미 한다 — 여기 margin-top:auto 를
   덧붙이면 nav 가 줄어들어야 할 때 서로 밀어내므로 두지 않는다. */
.app-sidebar__account {
    padding: 0.75rem 1rem 1rem;
    border-top: 1px solid var(--mili-line);
}

/* 로그아웃은 CSRF 토큰이 필요해 <a> 가 아니라 <form><button> 이다.
   폼이 레이아웃에 끼어들지 않게 껍데기를 지운다. */
.app-sidebar__logout {
    margin: 0;
}

.app-sidebar__account-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.4375rem 0.5rem;
    border: 0;
    background: transparent;
    color: var(--mili-fg-muted);
    font-size: 12px;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 160ms ease, color 160ms ease;
}

.app-sidebar__account-link:hover,
.app-sidebar__account-link:focus {
    background: rgba(var(--mili-fg-rgb), 0.06);
    color: var(--mili-ink);
}

.app-sidebar__scrim {
    display: none;
}

/*
 * 아이콘 레일(1024–1279px) — 우리 쪽 추가분.
 *
 * 폭 축소와 메뉴 항목 처리는 theme/reference.css 가 이미 한다
 * (`:root { --mili-sidebar-width: 80px }` + `.mili-lnb .mili-nav-item span` 시각적 숨김).
 * 여기서는 **참고에 없는 우리 조각**만 접는다 — 2 depth 서브메뉴 · 캐럿 · AI 교관 카드 · 계정 메뉴.
 *
 * 계정 링크는 텍스트가 <span> 으로 감싸여 있지 않아(아이콘 + 맨 텍스트 노드) 선택자로 못 잡는다.
 * font-size: 0 으로 접고 아이콘만 되살린다 — DOM 에 남으므로 스크린리더는 계속 읽는다.
 *
 * ⚠️ 이 블록은 §18 의 **맨 끝**에 있어야 한다. 위쪽에 두면 뒤따르는 .app-sidebar__account-link
 *    (같은 명시도)가 나중에 로드돼 font-size 를 되돌린다 — 실제로 그렇게 한 번 놓쳤다.
 */
@media (min-width: 1024px) and (max-width: 1279px) {
    .app-sidebar__caret,
    .app-sidebar__coach {
        display: none;
    }

    .app-sidebar__account-link {
        justify-content: center;
        padding-inline: 0;
        font-size: 0;
    }

    .app-sidebar__account-link > i {
        font-size: 1rem;
    }

    /*
     * 2 depth 서브메뉴는 이 폭에서 문서 흐름 안 자리(들여쓴 목록)를 접는 대신
     * 사이드바 오른쪽으로 튀어나오는 플라이아웃으로 바꾼다 — 그렇지 않으면 이 폭에서
     * 커뮤니티·마이페이지 하위 화면에 사이드바로 갈 방법이 아예 없어진다.
     *
     * aria-expanded/hidden(4번 아코디언의 진실)은 건드리지 않는다. 표시 여부는
     * 별도 클래스 .app-sidebar__sub--flyout 하나로만 다룬다 — js/common/app-shell.js 의
     * initSidebarFlyouts() 가 그룹 버튼 호버·포커스에 맞춰 붙였다 뗀다.
     * top 은 버튼마다 문서상 위치가 달라 CSS만으로 정렬할 수 없어 그 함수가 실측 좌표로 찍는다.
     */
    .app-sidebar__sub {
        display: flex;
        position: absolute;
        left: 100%;
        margin: 0 0 0 8px;
        padding: 8px;
        min-width: 190px;
        border: 1px solid var(--mili-line);
        border-radius: 14px;
        background: var(--mili-sidebar-bg);
        box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transition: opacity 140ms ease, visibility 140ms ease;
        z-index: 30;
    }

    /* 접힌 아코디언(hidden)도 플라이아웃 후보여야 하므로 §18 기본 규칙의 display:none 을 무효화한다. */
    .app-sidebar__sub[hidden] {
        display: flex;
    }

    .app-sidebar__sub--flyout {
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
    }
}

/*
 * IA 가 2 depth 로 늘면서 메뉴가 최대 8행 + 하위 6행까지 나온다. 노트북 높이(≈900px)에서는
 * AI 교관 카드와 계정 메뉴가 nav 를 밀어 메뉴 자체를 스크롤해야 볼 수 있게 된다.
 * 메뉴가 먼저다 — 낮은 화면에서는 카드를 접는다(계정 메뉴는 로그아웃 경로라 남긴다).
 */
@media (max-height: 860px) {
    .app-sidebar__coach {
        display: none;
    }
}

/*
 * 좁은 화면에서는 드로어로 바뀐다(원본 MobileSidebar: 폭 230px, bg-black/60 스크림).
 * 열고 닫기는 js/common/app-shell.js 가 .app-sidebar--open 으로 한다.
 *
 * 실측: Tailwind lg(1024px) 미만이 드로어다(참고 앱은 이 breakpoint 를 재정의하지 않는다).
 * ⚠️ 홈(/)은 예외다 — home.css 의 고정 캔버스(1920=271+1649)가 자기 기준폭 1280 에
 * 맞춰져 있어, 이 값을 1024 로 낮추면 1024~1279px 구간에서 사이드바는 상시 노출로 바뀌는데
 * 홈 캔버스는 아직 모바일 흐름 모드인 과도기가 생긴다. 사용자 확인 후 감수하기로 한 트레이드오프다
 * — 다른 화면(강의·프로젝트·마이페이지 등) 전부를 정확히 맞추는 쪽을 택했다.
 */
@media (max-width: 1023.98px) {
    .app-sidebar {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        z-index: 1045;
        width: 230px;
        transform: translateX(-100%);
        transition: transform 300ms ease-out;
    }

    .app-sidebar--open {
        transform: translateX(0);
    }

    .app-sidebar__close {
        display: inline-flex;
    }

    .app-sidebar__head {
        padding: 1.75rem 1.5rem 0.5rem;
    }

    .app-sidebar__scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 1040;
        background: rgba(0, 0, 0, 0.6);
    }

    .app-sidebar__scrim[hidden] {
        display: none;
    }
}

/* ===================================================================
   19) 등장 연출
   -------------------------------------------------------------------
   mili-home-v3 시안의 진입 애니메이션을 옮긴 것(타이밍·이징 그대로).
   시안은 클래스명이 home-* 라 홈 전용처럼 보였지만 어느 화면에서나 쓸 수 있어
   .reveal 로 이름을 바꿔 재사용 컴포넌트로 둔다.

   스태거는 JSP 인라인 style 대신 --d1~--d6 모디파이어로 준다
   (색·간격 하드코딩 금지 규칙과 같은 이유로 마크업에 수치를 넣지 않는다).
   =================================================================== */
@keyframes reveal-rise {
    from {
        opacity: 0;
        transform: translateY(22px) scale(0.985);
        filter: blur(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
        filter: blur(0);
    }
}

@keyframes progress-grow {
    0% {
        transform: scaleX(0);
        filter: brightness(0.7);
    }
    70% {
        filter: brightness(1.35);
    }
    100% {
        transform: scaleX(1);
        filter: brightness(1);
    }
}

.reveal {
    opacity: 0;
    animation: reveal-rise 620ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
    will-change: transform, opacity, filter;
}

.reveal--d1 { animation-delay: 80ms; }
.reveal--d2 { animation-delay: 160ms; }
.reveal--d3 { animation-delay: 240ms; }
.reveal--d4 { animation-delay: 320ms; }
.reveal--d5 { animation-delay: 400ms; }
.reveal--d6 { animation-delay: 480ms; }

/* 진행바 채우기. transform 이라 width 를 건드리지 않으므로 리플로우가 없다. */
.progress-grow {
    transform: scaleX(0);
    transform-origin: left center;
    animation: progress-grow 1350ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
    will-change: transform, filter;
}

/*
 * 모션을 줄이도록 설정한 사용자 대응.
 * 미디어쿼리를 여러 곳에 나눠 두면 나중에 한쪽만 고치는 사고가 나므로 여기 한 블록에 모은다.
 */
@media (prefers-reduced-motion: reduce) {
    .app-sidebar {
        transition: none;
    }

    /* 등장 연출은 "결과 상태"만 남기고 끈다. opacity 를 되돌리지 않으면 화면이 비어 버린다. */
    .reveal,
    .progress-grow {
        opacity: 1;
        animation: none;
        transform: none;
        filter: none;
    }
}

/* ===================================================================
   20) 커스텀 커서 (.target-cursor-*)
   -------------------------------------------------------------------
   mili-home-v3 시안 TargetCursor.css 를 값 그대로 옮긴 것이다.
   마크업은 js/common/target-cursor.js 가 만들고, gsap 이 x/y/rotation/scale 만 건드린다.
   (원본도 CSS 는 정지 상태만 정의하고 움직임은 전부 gsap 이 맡는다)

   mix-blend-mode: difference 때문에 배경색에 따라 커서 색이 반전된다 — 원본 의도다.
   모바일·터치 단말과 prefers-reduced-motion 사용자에게는 JS 가 아예 마운트하지 않는다.
   =================================================================== */
.target-cursor-wrapper {
    position: fixed;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    pointer-events: none;
    z-index: 9999;
    mix-blend-mode: difference;
    transform: translate(-50%, -50%);
}

.target-cursor-dot {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 4px;
    height: 4px;
    background: #fff;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    will-change: transform;
}

.target-cursor-corner {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 12px;
    height: 12px;
    border: 3px solid #fff;
    will-change: transform;
}

.corner-tl {
    transform: translate(-150%, -150%);
    border-right: none;
    border-bottom: none;
}

.corner-tr {
    transform: translate(50%, -150%);
    border-left: none;
    border-bottom: none;
}

.corner-br {
    transform: translate(50%, 50%);
    border-left: none;
    border-top: none;
}

.corner-bl {
    transform: translate(-150%, 50%);
    border-right: none;
    border-top: none;
}

/* ===================================================================
   21) 다크 셸 공용 오버라이드 (.mili-theme-dark)
   -------------------------------------------------------------------
   VOD/PBL 목록(mili-ai-4-screens-share 참조 시안)부터 Bootstrap 컴포넌트를
   다크 셸 안에서 그대로 쓴다. 라이트 화면(대부분)은 .mili-theme-dark 가
   body 에 없어 이 블록의 영향을 받지 않는다 — 화면별 재작성 없이 이 스코프
   안에서만 브랜드를 라임으로, 표면을 반투명 화이트로 바꾼다.
   =================================================================== */
.mili-theme-dark .filter-chip:hover {
    border-color: rgba(var(--mili-brand-rgb), 0.3);
    color: var(--mili-brand);
}

.mili-theme-dark .filter-chip--active,
.mili-theme-dark .filter-chip--active:hover {
    background: rgba(var(--mili-brand-rgb), 0.1) !important;
    border-color: rgba(var(--mili-brand-rgb), 0.25);
    color: var(--mili-brand);
}

.mili-theme-dark .form-select,
.mili-theme-dark .form-control {
    background-color: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.1);
    color: var(--mili-fg);
}

.mili-theme-dark .form-select {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff80' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
}

.mili-theme-dark .form-select:focus,
.mili-theme-dark .form-control:focus {
    background-color: rgba(255, 255, 255, 0.06);
    border-color: rgba(var(--mili-brand-rgb), 0.4);
    color: var(--mili-fg);
    box-shadow: 0 0 0 0.25rem rgba(var(--mili-brand-rgb), 0.15);
}

.mili-theme-dark .form-select option {
    color: #14161a;
}

.mili-theme-dark .btn-primary {
    --bs-btn-color: var(--mili-brand-on);
    --bs-btn-bg: var(--mili-brand);
    --bs-btn-border-color: var(--mili-brand);
    --bs-btn-hover-color: var(--mili-brand-on);
    --bs-btn-hover-bg: var(--mili-brand-strong);
    --bs-btn-hover-border-color: var(--mili-brand-strong);
    --bs-btn-active-color: var(--mili-brand-on);
    --bs-btn-active-bg: var(--mili-brand-strong);
    --bs-btn-active-border-color: var(--mili-brand-strong);
    --bs-btn-focus-shadow-rgb: var(--mili-brand-rgb);
}

.mili-theme-dark .btn-outline-primary {
    --bs-btn-color: var(--mili-brand);
    --bs-btn-border-color: rgba(var(--mili-brand-rgb), 0.4);
    --bs-btn-hover-color: var(--mili-brand-on);
    --bs-btn-hover-bg: var(--mili-brand);
    --bs-btn-hover-border-color: var(--mili-brand);
    --bs-btn-active-color: var(--mili-brand-on);
    --bs-btn-active-bg: var(--mili-brand);
    --bs-btn-active-border-color: var(--mili-brand);
    --bs-btn-focus-shadow-rgb: var(--mili-brand-rgb);
}

.mili-theme-dark .btn-outline-secondary {
    --bs-btn-color: var(--mili-fg-muted);
    --bs-btn-border-color: rgba(255, 255, 255, 0.14);
    --bs-btn-hover-color: var(--mili-fg);
    --bs-btn-hover-bg: rgba(255, 255, 255, 0.08);
    --bs-btn-hover-border-color: rgba(255, 255, 255, 0.2);
}

.mili-theme-dark .alert-success {
    background: rgba(var(--mili-brand-rgb), 0.1);
    border-color: rgba(var(--mili-brand-rgb), 0.25);
    color: var(--mili-fg);
}

.mili-theme-dark .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

/* Bootstrap pagination — size-8 원형 고스트 버튼(시안 실측)으로 재구성한다. */
.mili-theme-dark .pagination {
    --bs-pagination-color: rgba(255, 255, 255, 0.4);
    --bs-pagination-bg: rgba(255, 255, 255, 0.04);
    --bs-pagination-border-color: transparent;
    --bs-pagination-hover-color: var(--mili-fg);
    --bs-pagination-hover-bg: rgba(255, 255, 255, 0.08);
    --bs-pagination-hover-border-color: transparent;
    --bs-pagination-focus-bg: rgba(255, 255, 255, 0.08);
    --bs-pagination-focus-box-shadow: 0 0 0 0.2rem rgba(var(--mili-brand-rgb), 0.2);
    --bs-pagination-active-color: var(--mili-brand);
    --bs-pagination-active-bg: rgba(var(--mili-brand-rgb), 0.1);
    --bs-pagination-active-border-color: transparent;
    --bs-pagination-disabled-color: rgba(255, 255, 255, 0.2);
    --bs-pagination-disabled-bg: rgba(255, 255, 255, 0.04);
    --bs-pagination-disabled-border-color: transparent;
    gap: 0.25rem;
}

.mili-theme-dark .page-link {
    border-radius: 0;
    font-weight: 600;
    margin: 0;
}

/* ---------- 아래는 학습 플레이어(/mypage/learning/{n})가 추가로 쓰는 컴포넌트 ---------- */

/*
 * 체크박스·라디오. Bootstrap 은 테두리를 rgba(0,0,0,.25) 로 박아 두어 검정 판 위에서 사실상
 * 보이지 않는다. 체크 표시 SVG 도 흰색이 박혀 있어 라임 배경 위에서 대비가 나오지 않으므로
 * --bs-form-check-bg-image 를 브랜드 위 글자색(--mili-brand-on)으로 다시 그린다.
 */
.mili-theme-dark .form-check-input {
    background-color: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.25);
}

.mili-theme-dark .form-check-input:checked {
    background-color: var(--mili-brand);
    border-color: var(--mili-brand);
}

.mili-theme-dark .form-check-input:checked[type="checkbox"] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%2311170d' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

.mili-theme-dark .form-check-input:checked[type="radio"] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%2311170d'/%3e%3c/svg%3e");
}

.mili-theme-dark .form-check-input:focus {
    border-color: rgba(var(--mili-brand-rgb), 0.5);
    box-shadow: 0 0 0 0.25rem rgba(var(--mili-brand-rgb), 0.15);
}

/* 코드 "실행" 버튼. Bootstrap 기본 초록(#198754)은 다크 판 위에서 탁해 한 단계 밝힌다.
   채점(.btn-primary=라임)과 색으로 구분돼야 하므로 브랜드로 흡수하지 않는다. */
.mili-theme-dark .btn-success {
    --bs-btn-bg: rgba(72, 199, 142, 0.16);
    --bs-btn-border-color: rgba(72, 199, 142, 0.35);
    --bs-btn-color: var(--mili-code-success);
    --bs-btn-hover-bg: rgba(72, 199, 142, 0.26);
    --bs-btn-hover-border-color: rgba(72, 199, 142, 0.5);
    --bs-btn-hover-color: var(--mili-code-success);
    --bs-btn-active-bg: rgba(72, 199, 142, 0.3);
    --bs-btn-active-border-color: rgba(72, 199, 142, 0.5);
    --bs-btn-active-color: var(--mili-code-success);
    --bs-btn-disabled-bg: rgba(255, 255, 255, 0.04);
    --bs-btn-disabled-border-color: rgba(255, 255, 255, 0.08);
    --bs-btn-disabled-color: var(--mili-fg-faint);
}

/* 비활성 버튼(이전/다음 끝, 학습 완료됨)이 채도만 낮아지면 다크에서 여전히 눌러 보인다. */
.mili-theme-dark .btn-primary:disabled,
.mili-theme-dark .btn-outline-primary:disabled,
.mili-theme-dark .btn-outline-secondary:disabled {
    --bs-btn-disabled-bg: rgba(255, 255, 255, 0.04);
    --bs-btn-disabled-border-color: rgba(255, 255, 255, 0.08);
    --bs-btn-disabled-color: var(--mili-fg-faint);
    opacity: 1;
}

/* .text-success(수료 체크) 는 Bootstrap 기본값이 어두워 다크에서 거의 검게 보인다. */
.mili-theme-dark .text-success {
    color: var(--mili-code-success) !important;
}

/*
 * .card 와 .text-muted 는 PBL 프로젝트 상세가 다크 셸로 옮겨오며 처음 쓰인다.
 * 부트스트랩 기본값이 라이트(흰 카드·짙은 회색 글자)라 그대로 두면 어두운 배경 위에
 * 흰 박스가 뜨고 보조 문구는 읽히지 않는다. 표면만 list-group 과 같은 결로 맞춘다 —
 * 화면별로 카드를 새로 만들면 라이트 화면과 마크업이 갈라진다.
 */
.mili-theme-dark .card {
    --bs-card-bg: rgba(255, 255, 255, 0.03);
    --bs-card-color: var(--mili-fg-body);
    --bs-card-border-color: rgba(255, 255, 255, 0.08);
    --bs-card-cap-bg: rgba(255, 255, 255, 0.04);
}

/* 커뮤니티 FAQ(community/faq-list.jsp)가 다크 셸로 옮겨오며 처음 쓰인다.
   Bootstrap 기본값이 흰 판 + 남색 활성이라 검은 캔버스에서 통째로 튄다.
   화살표 아이콘은 색이 data-URI 안에 박혀 있어(#212529 / 파랑) 다시 그려야 한다
   — 위 .form-select 와 같은 형식에서 stroke 색만 바꾼 것이다.
   collapse 동작 자체는 base-layout.jsp 가 싣는 bootstrap.bundle 이 그대로 처리한다. */
.mili-theme-dark .accordion {
    --bs-accordion-color: var(--mili-fg-body);
    --bs-accordion-bg: rgba(255, 255, 255, 0.03);
    --bs-accordion-border-color: rgba(255, 255, 255, 0.08);
    --bs-accordion-btn-color: var(--mili-fg);
    --bs-accordion-btn-bg: transparent;
    --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff80' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
    --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23aaff19' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
    --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--mili-brand-rgb), 0.15);
    --bs-accordion-active-color: var(--mili-brand);
    --bs-accordion-active-bg: rgba(var(--mili-brand-rgb), 0.1);
}

.mili-theme-dark .text-muted {
    color: var(--mili-fg-muted) !important;
}

/* ---------- 아래는 마이페이지 하위 화면이 추가로 쓰는 컴포넌트 ---------- */

/*
 * 진행바. .progress 는 --mili-primary-500(파랑) 을 박아 두어 라임 셸에서 혼자 파랗게 남는다.
 * 트랙도 --bs-secondary-bg(밝은 회색)라 검정 판 위에서 흰 띠로 튄다.
 * (개요의 PBL 미션·로드맵 진행바도 같은 컴포넌트라 함께 라임으로 맞춰진다)
 */
.mili-theme-dark .progress {
    --bs-progress-bg: rgba(255, 255, 255, 0.08);
    --bs-progress-bar-bg: var(--mili-brand);
}

/* 회원탈퇴 같은 위험 동작. Bootstrap 기본 테두리(#dc3545)는 검정 판에서 탁하다. */
.mili-theme-dark .btn-outline-danger {
    --bs-btn-color: #ff8b8f;
    --bs-btn-border-color: rgba(229, 72, 77, 0.4);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--mili-danger);
    --bs-btn-hover-border-color: var(--mili-danger);
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: var(--mili-danger);
    --bs-btn-active-border-color: var(--mili-danger);
}

/* 안내/오류 알림. 파스텔 배경을 그대로 두면 검정 판 위에 흰 카드가 얹힌 것처럼 보인다. */
.mili-theme-dark .alert-info,
.mili-theme-dark .alert-warning,
.mili-theme-dark .alert-danger {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.1);
    color: var(--mili-fg-body);
}

.mili-theme-dark .alert-danger {
    border-color: rgba(229, 72, 77, 0.35);
    color: #ff8b8f;
}

/*
 * 라벨/입력 2열 폼 표(프로필). border-top 2px 가 --mili-ink 라 다크에서 흰 굵은 선이 되어
 * 표 위에 자를 얹은 것처럼 보인다. 다크에서는 경계를 그리는 대신 옅게 얹는다(시안 방식).
 */
.mili-theme-dark .form-table {
    border-top-color: var(--mili-line-strong);
}

.mili-theme-dark .form-table__row {
    border-bottom-color: var(--mili-line);
}

/*
 * 소속·계급 2단 선택기. 표면·글자는 시맨틱 토큰이라 다크를 따라오지만
 * 포커스 링·선택 결과 색만 --mili-primary-*(파랑) 하드코딩이라 여기서 브랜드로 돌린다.
 */
.mili-theme-dark .mili-selector__input-wrap {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.1);
}

.mili-theme-dark .mili-selector__input-wrap:focus-within {
    border-color: rgba(var(--mili-brand-rgb), 0.4);
    box-shadow: 0 0 0 0.2rem rgba(var(--mili-brand-rgb), 0.15);
}

.mili-theme-dark .mili-selector__list {
    background: var(--mili-panel-raised);
    border-color: var(--mili-line-strong);
}

.mili-theme-dark .mili-selector__item:hover,
.mili-theme-dark .mili-selector__item:focus {
    background: rgba(var(--mili-brand-rgb), 0.12);
    color: var(--mili-brand);
}

.mili-theme-dark .mili-selector__selected {
    color: var(--mili-brand);
}


/* ===================================================================
   22) "내 로드맵에 담기" 드롭다운 (.roadmap-add*)
   -------------------------------------------------------------------
   마크업은 WEB-INF/views/common/_roadmap-add.jsp 이고, 과정 상세(/courses/{no}, 라이트 셸)와
   PBL 상세(/pbl/{offeringNo}, 라이트 셸)가 함께 쓴다. 화면 전용 CSS 가 아니라 재사용
   컴포넌트라 여기에 둔다(로드맵 진행률 트랙 .roadmap-track* 은 위 14) 절에 있다).

   드롭다운 한 줄이 곧 하나의 POST 폼이라 Bootstrap 의 .dropdown-item 대신 버튼을
   항목처럼 보이게 맞춘다.
   =================================================================== */
.roadmap-add__menu {
    min-width: 15rem;
    max-height: 18rem;
    overflow-y: auto;
}

.roadmap-add__empty {
    padding: 0.5rem 1rem;
    font-size: 0.8125rem;
    color: var(--mili-muted);
}

.roadmap-add__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    padding: 0.375rem 1rem;
    background: none;
    border: 0;
    font-size: 0.875rem;
    color: var(--mili-ink);
    text-align: left;
    cursor: pointer;
}

.roadmap-add__item:hover:not(:disabled),
.roadmap-add__item:focus-visible:not(:disabled) {
    background: var(--mili-bg);
}

/* 이미 담긴 로드맵 · 번호가 충돌하는 로드맵은 눌러도 담기지 않으므로 잠근다. */
.roadmap-add__item:disabled {
    color: var(--mili-muted);
    cursor: default;
}

.roadmap-add__flag {
    flex: 0 0 auto;
    font-size: 0.75rem;
    color: var(--mili-muted);
}

/* ===================================================================
   23) 장문 텍스트 에디터 (.mili-editor*)
   -------------------------------------------------------------------
   마크업은 화면 JSP 가 만들고 동작은 js/common/mili-editor.js + Tiptap 벤더 번들이 맡는다.
   현재 쓰는 곳은 PBL 문제 풀이의 장문형 답안·수행 설명이고, 다른 장문 입력란이 생기면
   같은 마크업으로 붙일 수 있게 화면 전용이 아니라 여기에 둔다.

   Tiptap 은 헤드리스라 스타일이 하나도 딸려 오지 않는다. 아래가 그 전부다 —
   .form-control 을 그대로 입히지 않는 이유는 편집면이 <textarea> 가 아니라
   contenteditable 이라 높이·패딩·스크롤을 따로 잡아야 하기 때문이다.
   =================================================================== */

.mili-editor {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--mili-surface);
    border: 1px solid var(--mili-border);
    /* 참고의 --mili-radius-control(10px) 은 원본에서도 소비처가 0건인 사문 토큰이다.
       실제 렌더는 각진 형태이므로 --mili-radius-none 을 쓴다. */
    border-radius: var(--mili-radius-none);
}

.mili-editor:focus-within {
    border-color: var(--mili-brand);
}

/* ── 툴바 ── */
.mili-editor__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.125rem;
    flex: 0 0 auto;
    padding: 0.25rem 0.375rem;
    background: var(--mili-panel-sunken);
    border-bottom: 1px solid var(--mili-border);
}

.mili-editor__sep {
    width: 1px;
    height: 1.125rem;
    margin: 0 0.25rem;
    background: var(--mili-border);
}

.mili-editor__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--mili-radius-sm);
    background: transparent;
    color: var(--mili-muted);
    font-size: 0.875rem;
    line-height: 1;
}

.mili-editor__btn:hover {
    color: var(--mili-ink);
    background: var(--mili-surface);
    border-color: var(--mili-border);
}

.mili-editor__btn.is-active {
    color: var(--mili-brand-strong);
    background: var(--mili-brand-soft);
    border-color: var(--mili-brand);
}

/* ── 편집면 ── */
.mili-editor__body {
    flex: 1 1 auto;
    min-height: 10rem;
    min-width: 0;
    overflow-y: auto;
}

/* ProseMirror 가 contenteditable 을 얹는 요소. 클릭 영역이 글자 줄에만 잡히지 않도록 꽉 채운다. */
.mili-editor__surface {
    min-height: 100%;
    padding: 0.75rem 0.875rem;
    color: var(--mili-body);
    line-height: 1.7;
    outline: none;
}

.mili-editor__surface > * {
    margin-top: 0;
    margin-bottom: 0.625rem;
}

.mili-editor__surface > *:last-child {
    margin-bottom: 0;
}

/*
 * 빈 문서의 안내 문구. Tiptap 의 Placeholder 확장이 첫 문단에 data-placeholder 를 심고
 * 실제 글자는 CSS 가 그린다(문서에 텍스트로 들어가면 값에 섞인다).
 */
.mili-editor__surface p.is-editor-empty:first-child::before {
    content: attr(data-placeholder);
    float: left;
    height: 0;
    pointer-events: none;
    color: var(--mili-fg-faint);
}

.mili-editor__surface h1,
.mili-editor__surface h2,
.mili-editor__surface h3 {
    color: var(--mili-ink);
    font-weight: 700;
    line-height: 1.35;
}

.mili-editor__surface h1 { font-size: 1.25rem; }
.mili-editor__surface h2 { font-size: 1.0625rem; }
.mili-editor__surface h3 { font-size: 0.9375rem; }

.mili-editor__surface ul,
.mili-editor__surface ol {
    padding-left: 1.375rem;
}

.mili-editor__surface li > p {
    margin: 0;
}

.mili-editor__surface blockquote {
    margin-left: 0;
    padding: 0.125rem 0 0.125rem 0.75rem;
    border-left: 3px solid var(--mili-border);
    color: var(--mili-muted);
}

.mili-editor__surface code {
    padding: 0.0625rem 0.25rem;
    border-radius: var(--mili-radius-sm);
    background: var(--mili-panel-sunken);
    font-size: 0.875em;
}

.mili-editor__surface pre {
    padding: 0.75rem;
    border-radius: var(--mili-radius-sm);
    background: var(--mili-code-bg);
    color: var(--mili-code-text);
    overflow-x: auto;
}

/* pre 안의 code 는 인라인 코드 배경을 물려받으면 이중 배경이 된다. */
.mili-editor__surface pre code {
    padding: 0;
    background: none;
    color: inherit;
    font-size: 0.8125rem;
}

.mili-editor__surface hr {
    border: 0;
    border-top: 1px solid var(--mili-border);
}

.mili-editor__surface a {
    color: var(--mili-brand-strong);
}

/*
 * 원본 <textarea>. 에디터가 붙으면 감추지만 DOM 에는 남는다 — 폼 제출이 계속 이 필드로 간다.
 * display:none 이 아니라 화면에서만 지우는 방식이라야 폼 검증이 "숨은 필드" 로 보고 막지 않는다.
 */
.mili-editor__source.is-editor-managed {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* 초안 보관 안내. 서버 제출로 오해하지 않도록 입력란 바로 아래에 붙인다. */
.mili-editor-note {
    display: flex;
    align-items: flex-start;
    gap: 0.375rem;
    margin: 0.375rem 0 0;
    font-size: 0.75rem;
    color: var(--mili-muted);
}

/* ===================================================================
   24) 커뮤니티 (공지/Q&A/FAQ/뉴스)
   -------------------------------------------------------------------
   커뮤니티 8개 화면(목록·상세·작성)이 main-layout(라이트 navbar)에서 다크 앱셸로
   옮겨오며 새로 생긴 공용 컴포넌트. 게시판이 4종이라도 화면 얼개는 하나(탭 + 목록/
   아코디언 + 상세 기사 + 답변 스레드)라 재사용 컴포넌트로 여기 둔다.
   =================================================================== */

/* ----- 23-1) 게시판 탭. .mypage-tabs 와 같은 얼개(마크업은 common/_community-tabs.jsp). ----- */

.community-tabs {
    display: flex;
    gap: 0.375rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.25rem;
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

.community-tabs__link {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 0.375rem;
    padding: 0.4375rem 0.875rem;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.06);
    border-radius: var(--mili-radius-pill);
    background: rgba(var(--mili-fg-rgb), 0.03);
    color: var(--mili-fg-muted);
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.community-tabs__link:hover,
.community-tabs__link:focus {
    background: rgba(var(--mili-fg-rgb), 0.07);
    color: var(--mili-ink);
}

.community-tabs__link--active,
.community-tabs__link--active:hover,
.community-tabs__link--active:focus {
    border-color: rgba(var(--mili-brand-rgb), 0.25);
    background: rgba(var(--mili-brand-rgb), 0.1);
    color: var(--mili-brand);
}

/*
 * ----- 23-2) 게시글 목록 줄 (.post-list / .post-row*) -----
 * 원래 마이페이지 "작성한 글"(mypage/posts.css) 전용이었지만, 커뮤니티 목록 4종도
 * 같은 "제목 + 게시판 칩 + 날짜" 줄을 쓰게 되어 재사용 컴포넌트로 승격했다.
 * 게시판 유형별 색(--accent)은 그대로 공지=파랑/QNA=브랜드/FAQ=보라/뉴스=노랑이다.
 */

.post-list {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.post-row {
    position: relative;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.06);
    border-radius: 0;
    background: rgba(var(--mili-fg-rgb), 0.025);
    overflow: hidden;
    transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.post-row:hover,
.post-row:focus-within {
    background: rgba(var(--mili-fg-rgb), 0.05);
    border-color: rgba(var(--mili-fg-rgb), 0.1);
    transform: translateX(2px);
}

.post-row--notice { --accent: var(--mili-stat-time); }
.post-row--qna { --accent: var(--mili-brand); }
.post-row--faq { --accent: #a78bfa; }
.post-row--news { --accent: var(--mili-stat-badge); }
.post-row--etc { --accent: var(--mili-fg-faint); }

.post-row__link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.875rem 1rem;
    color: inherit;
    text-decoration: none;
}

.post-row__board {
    flex: 0 0 auto;
    width: 3.75rem;
    padding: 0.25rem 0;
    border: 1px solid;
    border-color: var(--accent);
    border-radius: 999px;
    color: var(--accent);
    font-size: 0.625rem;
    font-weight: 700;
    text-align: center;
    opacity: 0.9;
}

.post-row__main {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    min-width: 0;
}

.post-row__title {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    min-width: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--mili-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.post-row__reply {
    flex: 0 0 auto;
    padding: 0.0625rem 0.375rem;
    border-radius: 0;
    background: rgba(var(--mili-fg-rgb), 0.08);
    color: var(--mili-fg-muted);
    font-size: 0.625rem;
    font-weight: 700;
}

.post-row__date {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.6875rem;
    color: var(--mili-fg-faint);
}

.post-row__chevron {
    flex: 0 0 auto;
    color: var(--mili-fg-faint);
    font-size: 0.75rem;
    transition: transform 0.2s ease, color 0.2s ease;
}

.post-row:hover .post-row__chevron,
.post-row:focus-within .post-row__chevron {
    color: var(--mili-brand);
    transform: translateX(2px);
}

@media (max-width: 575.98px) {
    .post-row__main {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.25rem;
    }

    .post-row__title {
        max-width: 100%;
    }
}

/* ----- 23-3) FAQ 아코디언. 과정 상세 커리큘럼(.curriculum, 5-6절)과 같은 얼개. ----- */

.faq-accordion {
    border: 1px solid var(--mili-border);
    border-radius: 0;
    background: var(--mili-surface);
    overflow: hidden;
}

.faq-accordion__item + .faq-accordion__item {
    border-top: 1px solid var(--mili-border);
}

.faq-accordion__q {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.875rem 1.125rem;
    border: 0;
    background: transparent;
    color: var(--mili-ink);
    font-weight: 700;
    font-size: 0.9375rem;
    text-align: left;
    cursor: pointer;
}

.faq-accordion__q:hover {
    background: var(--mili-bg);
}

.faq-accordion__q-icon {
    flex: 0 0 auto;
    color: var(--mili-brand);
    transition: transform 0.2s ease;
}

.faq-accordion__q[aria-expanded="true"] .faq-accordion__q-icon {
    transform: rotate(180deg);
}

.faq-accordion__q-text {
    flex: 1 1 auto;
    min-width: 0;
}

.faq-accordion__a {
    padding: 0 1.125rem 1.125rem 2.875rem;
    color: var(--mili-body);
    font-size: 0.875rem;
    line-height: var(--mili-line-body, 1.6);
    white-space: pre-wrap;
}

/* ----- 23-4) 게시글 상세 기사(공지·뉴스·QNA 본문 공용) ----- */

.post-detail {
    padding: 1.5rem;
    margin-bottom: 1.5rem;
    border: 1px solid var(--mili-border);
    border-radius: 0;
    background: var(--mili-surface);
}

.post-detail__title {
    margin: 0 0 0.5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--mili-ink);
}

.post-detail__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    margin: 0 0 1.25rem;
    color: var(--mili-fg-faint);
    font-size: 0.8125rem;
}

.post-detail__body {
    color: var(--mili-body);
    font-size: 0.9375rem;
    line-height: var(--mili-line-body, 1.6);
    white-space: pre-wrap;
}

.post-detail__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--mili-border);
}

/* ----- 23-5) Q&A 답변 스레드 ----- */

.qna-replies {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.qna-reply {
    padding: 0.875rem 1rem;
    border: 1px solid rgba(var(--mili-fg-rgb), 0.06);
    border-radius: 0;
    background: rgba(var(--mili-fg-rgb), 0.02);
}

.qna-reply__meta {
    margin: 0 0 0.375rem;
    color: var(--mili-fg-faint);
    font-size: 0.75rem;
}

.qna-reply__body {
    color: var(--mili-body);
    font-size: 0.875rem;
    line-height: var(--mili-line-body, 1.6);
    white-space: pre-wrap;
}
