/*
Theme Name: esangedu
Theme URI: https://esangedu.com
Author: 글로벌창업연구소
Author URI: https://esangedu.com
Description: 글로벌창업연구소 AI교육센터 전용 테마
Version: 1.0.68.108
License: GNU General Public License v2 or later
Text Domain: esangedu

Changelog:
  v77.90 (2026-05-18) - 🔐 NICE 본인인증 — 관리자 입력 UI 추가:

                        【사용자 보고】
                        "API를 받으면 어디다가 넣어야 하는지 위치 모르겠다"
                        v77.88에서 NICE 시스템은 구축했지만 키 입력 UI를
                        만들지 않아서 DB 직접 편집 외엔 방법 없었음. 누락 보완.

                        【추가 위치】
                        관리자 → 사이트 설정 → SNS 로그인 카드 바로 아래
                        새 카드 "🔐 NICE 본인인증 설정"

                        【카드 내용】
                        - 상태 표시: ✅ 활성 / ⚠ 미설정 (Mock 모드)
                        - NICE 가입 방법 안내 (4단계)
                        - 사이트 코드 입력칸 (text)
                        - 사이트 패스워드 입력칸 (password, 가려짐)
                        - Mock/실제 모드 상태 안내 박스
                        - NICE 측에 등록할 콜백 URL 자동 표시

                        【동작】
                        - 두 필드 모두 입력 + 저장 → 자동으로 실제 NICE 모드 전환
                        - 한쪽이라도 비어있으면 Mock 모드 유지
                        - esg_nice_is_configured() 함수가 자동 감지

                        【4역할 협업】
                        🏗️ Architect: SNS 카드 패턴 그대로 일관성 유지
                        🎨 Designer: 노란 안내 박스(주의), 파란 상태 박스(현재 모드)
                        👨‍💻 Developer: 저장 처리 1줄 + UI 카드 1개 추가
                        🔍 Reviewer: 기존 SNS 카드 패턴 충실 재현, 보안 (password type)

  v77.89 (2026-05-18) - 🔧 약관 저장 — 백슬래시(\) 무한 누적 버그 외과 수정:

                        【사용자 보고】
                        관리자에서 약관 본문의 \ 를 지우고 저장한 뒤 다시 봐도
                        \" 형태로 슬래시가 그대로 남아있음.
                        실제로는 지운 게 아니라 저장 때마다 슬래시가 추가되는 상황.

                        【원인】
                        WordPress는 보안상 모든 $_POST 데이터에 자동으로 magic
                        slashes를 추가함. 정상적으로는 저장 직전 wp_unslash()로
                        제거해야 함.
                        functions.php 약관 저장 두 곳(L12175, L23459)에서만
                        wp_unslash()가 누락되어 있었음:
                          1회차: "회사"     → DB에 \"회사\" 저장
                          2회차: \"회사\"   → DB에 \\"회사\\" 저장
                          3회차: \\"회사\\" → DB에 \\\\"회사\\\\" 저장
                        매 저장마다 백슬래시가 기하급수적으로 누적됨.
                        같은 파일의 다른 저장 로직들은 wp_unslash() 정상 사용 중.

                        【해결】
                        ① esg_terms_versions 저장 (L12175):
                           - $_POST['terms_company_name'] → wp_unslash() 추가
                           - $_POST['terms_ver'] 배열 전체에 wp_unslash() 일괄 적용
                        ② esg_terms 저장 (L23459):
                           - $_POST['title_*'], $_POST['content_*']에 wp_unslash() 추가
                        ③ 기존 DB 정리 마이그레이션 (1회 실행):
                           - 옵션 esg_v77_89_terms_unslash_done 플래그로 중복 방지
                           - stripslashes_deep()로 기존 옵션값 정리:
                             • esg_terms (탭형식 약관 4종)
                             • esg_terms_versions (버전별 약관)
                             • esg_terms_company (회사명)

                        【영향】
                        - 앞으로 저장: 슬래시 누적 없음
                        - 기존 데이터: 사이트 로드 시 1회 자동 정리됨
                        - 다른 옵션/기능: 영향 0건 (약관 3개 옵션에만 적용)

                        【검증】
                        - "회사" 같은 일반 텍스트: 백슬래시 완전 제거 ✓
                        - "회사" 안에 의도적 \ 가 있는 경우: 1회만 정리되므로 의도 보존 ✓
                        - 정상 데이터 (백슬래시 없음): stripslashes_deep 적용해도 변화 없음 ✓

                        【4역할 협업】
                        🏗️ Architect: magic slashes 누적 메커니즘 식별
                        🎨 Designer: 시각적 변화 없음 (단지 정상 표시로 복원)
                        👨‍💻 Developer: 외과 수정 — 약관 저장 2곳 + 마이그레이션 1개
                        🔍 Reviewer: 다른 옵션 영향 0, 한 번만 실행되는 플래그 확인

  v77.88 (2026-05-18) - 🔐 NICE 본인인증 시스템 — UI 골격 + API 자리 구축:

                        【배경】
                        파트너님 요청: SNS 로그인 흐름에 본인인증 단계 추가.
                        (구글 로그인 → 구글 동의 → 본인인증 알림 모달 → NICE PASS)
                        멀티캠퍼스 등 한국 교육 사이트 표준 패턴.
                        NICE 단가 협상 중이라 API 키는 아직 없음 → Mock 모드로 골격만.

                        【신규 파일】
                        - inc/nice-auth.php (신규, ~280 lines)
                          NICE 본인인증 시스템 — Mock 모드 + 실제 API 호출 자리
                          + 콜백 처리 + 세션 데이터 관리 + 관리자 안내

                        【핵심 컴포넌트】
                        1. 글로벌 본인인증 모달 (footer.php)
                           - 모든 페이지에서 esgNiceVerify({...}) 한 줄로 호출 가능
                           - 디자인: 1번 스크린샷 그대로 (멀티캠퍼스 스타일)
                           - 흰 박스 + navy 텍스트 + blue 확인 버튼
                           - ESC 키 / backdrop 클릭 / X 버튼으로 닫기
                           - 모바일 반응형

                        2. NICE 인증 시스템 (inc/nice-auth.php)
                           - esg_nice_is_configured(): 키 등록 여부 자동 감지
                           - esg_nice_render_request_form(): hidden form 출력
                           - esg_nice_handle_callback(): NICE → 우리 사이트 콜백
                           - esg_nice_handle_mock(): Mock 모드 진행 화면
                           - esg_nice_get_verified_data(): 세션 인증 결과 조회
                           - 세션 30분 만료, 가입 후 즉시 정리

                        3. SNS 신규 가입자 흐름 변경 (functions.php)
                           - 기존: SNS 로그인 후 신규면 wp_create_user()로 즉시 자동가입
                           - 변경: 신규면 SNS 정보 세션 임시 저장 → /register 페이지로
                                   redirect (esg_sns_pending=1)
                                   → 본인인증 안내 배너 표시 → NICE 인증 후 가입 완료
                           - 기존 회원은 영향 없음 (바로 로그인)

                        4. 회원가입 페이지 통합 (page-register.php)
                           - 기존 alert("준비중") 제거
                           - 새 글로벌 모달 호출로 교체
                           - 본인인증 미완료 시 가입 제출 차단 + 친절한 안내
                           - 본인인증 완료 시 버튼 "✅ 본인인증 완료"로 변경
                           - 실제 NICE 연동 후엔 이름/생년월일 등 자동 입력

                        【Mock 모드 동작 (현재 상태)】
                        1. 사용자가 본인인증 버튼 클릭
                        2. 우리 모달 표시 ("최초 1회 본인인증이 필요합니다")
                        3. 확인 클릭 → hidden form submit
                        4. /?esg_nice_mock=1 페이지로 이동
                        5. "개발 모드 안내" 화면 표시
                           - "본인인증 없이 진행" 버튼 클릭 시
                             세션에 mock 플래그 저장 후 회원가입 페이지로 복귀
                        6. 회원가입 페이지에서 자동으로 본인인증 완료 상태
                           - 버튼이 "✅ 본인인증 완료 (개발모드)"로 변경

                        【NICE 계약 완료 후 작업】
                        1. NICE에서 사이트 코드 / 사이트 패스워드 발급받음
                        2. WordPress 관리자 옵션:
                           - esg_nice_site_code: 발급받은 사이트 코드
                           - esg_nice_site_pw:   발급받은 사이트 패스워드
                        3. inc/nice-auth.php 두 곳의 //TODO 부분에
                           NICE 샘플 코드 한 줄씩 채워넣기
                           - esg_nice_build_request_data(): 암호화
                           - esg_nice_handle_callback(): 복호화
                        4. 키 입력만으로 자동 활성화 (esg_nice_is_configured()가 감지)
                        5. Mock 화면 → 실제 NICE PASS 팝업으로 자동 전환

                        【보안】
                        - 인증 결과는 PHP session에만 저장 (DB X)
                        - 30분 자동 만료
                        - 가입 완료 후 즉시 세션 제거 (esg_nice_clear_verified)
                        - state nonce로 CSRF 방어 (NICE 연동 후 활성화)

                        【4역할 협업】
                        🏗️ Architect: Mock↔Production 분리, 세션 기반 안전한 데이터 흐름
                        🎨 Designer: 1번 스크린샷 모달 정확 재현, 토큰 사용
                        👨‍💻 Developer: 별도 파일 분리, NICE SDK 자리 명확히 표시
                        🔍 Reviewer: 기존 회원 영향 0, Mock→Production 전환 시나리오 검증

  v77.87 (2026-05-18) - 🛡️ URL Migration 안전망 — 도메인 변경 후 DB 잔존 URL 자동 교정:

                        【배경】
                        파트너님이 도메인을 esangedunet.mycafe24.com → esangedu.ai 로 변경.
                        WordPress 관리자 → 설정 → 일반에서 사이트 주소는 새 도메인으로
                        바꿨지만, DB의 wp_options(esg_nav_menus)·wp_posts(본문)에 박힌
                        옛 URL은 그대로 남아있어 메뉴 클릭 시 옛 도메인으로 가서 Forbidden.

                        【해결】
                        정공법은 Better Search Replace 플러그인으로 DB를 일괄 변환하는 것.
                        본 패치는 그 작업 전후로도 사이트가 깨지지 않도록 안전망을 친다.

                        【새 파일】
                        - inc/url-migration.php (신규)

                        【동작 방식】
                        1. option_esg_nav_menus 필터:
                           전체메뉴·내비게이션이 메뉴 옵션을 읽을 때 자동 교정
                        2. the_content/the_excerpt 필터:
                           본문 안의 이미지 src·a href에 박힌 옛 도메인 출력 시 교정
                        3. path 교정 룰:
                           /about/location → /about#location 자동 변환
                           (관리자가 메뉴 URL 잘못 입력한 케이스 대비)
                        4. 관리자 배너:
                           DB 잔존 카운트 안내 + Better Search Replace 사용 권장
                           (1회 dismiss 가능)

                        【비침습성】
                        - DB는 한 줄도 안 건드림 (read-time correction only)
                        - 기존 URL이 정상이면 어떤 동작도 안 함 (early return)
                        - 비활성화 옵션 esg_url_migration_enabled (기본 1)
                        - 새 도메인은 home_url() 옵션에서 자동 감지 → 하드코딩 회피
                        - 무한 루프 회피: raw DB query로 home 옵션 직접 읽기

                        【확장성】
                        - 추가 옛 도메인: esg_url_migration_get_old_domains() 배열에 추가
                        - 추가 path 룰: esg_url_migration_fix_path() $rules 배열에 추가

                        【영향 범위】
                        - functions.php L840 require_once 1줄 추가
                        - inc/url-migration.php 신규
                        - 기존 코드 동작 변경 0 (필터로만 개입)
                        - 새 도메인 사용자에게는 어떤 영향도 없음

                        【파트너님 후속 작업 안내】
                        1. (즉시 효과) — 이 ZIP 적용 즉시 옛 URL 자동 교정 시작
                        2. (영구 해결) — Better Search Replace 플러그인으로
                           https://esangedunet.mycafe24.com → https://esangedu.ai
                           DB 일괄 변환 (Dry run 먼저 → 실제 실행)
                        3. (검증 후) — 관리자 옵션에서 esg_url_migration_enabled=0
                           로 안전망 비활성화 가능 (선택적)

                        【4역할 협업】
                        🏗️ Architect: read-time correction 설계 + 필터 후크 선정
                        🎨 Designer: 관리자 배너 (notice-warning + dismissible)
                        👨‍💻 Developer: 별도 파일 분리 + 무한 루프 회피 raw query
                        🔍 Reviewer: 성능(early return)/안전성(DB 무수정)/확장성 검증

  v77.86 (2026-05-18) - 🗂️ 모바일 전체메뉴 — "고객지원" 카테고리 2개 중복 해결:

                        【사용자 보고】
                        모바일 전체메뉴 좌측 카테고리 목록에 "고객지원"이 2번 표시됨.
                          ① 펼치면 공지사항/자주묻는질문/문의게시판/자료실 (관리자 메뉴)
                          ② 라벨만 있는 "고객지원" (하드코딩 — 기업교육 문의 들어있음)
                        요청: ② 블록을 없애고, "기업교육 문의"를 ① 메뉴의
                              "자료실" 다음에 합쳐서 표시.

                        【원인 분석】
                        header.php 두 곳에서 "고객지원" 카테고리가 각각 렌더됨:
                        - L224~236: 관리자 설정(get_option('esg_nav_menus')) 메뉴 루프
                          → 사용자가 관리자 페이지에서 "고객지원" 카테고리를 만들고
                             공지사항/자주묻는질문/문의게시판/자료실 4개 항목 추가했음
                        - L260~270 (v68.78): /inquiry 페이지가 다른 메뉴 어디에도 없을 때
                          하드코딩으로 "고객지원" fm-col 추가 — 기업교육 문의 + 로그인/회원가입
                        → 두 블록 라벨이 같아서 모바일에서 "고객지원" 두 번 표시.

                        【해결】
                        ① 관리자 설정 메뉴 렌더링 루프(L224~)에 자동 보충 로직 추가
                           - 라벨이 정확히 "고객지원"인 메뉴 발견 시
                           - subs에 /inquiry 링크가 없으면 (중복 방지)
                           - 기존 subs 렌더링 끝에 "기업교육 문의" 자동 추가
                           - 비로그인 사용자에게는 로그인/회원가입 링크도 함께 자동 추가
                        ② 기존 v68.78 하드코딩 "고객지원" fm-col 블록 제거
                        ③ fallback 추가: 관리자가 "고객지원" 카테고리를 아예 만들지
                           않은 사이트를 위해 — $_has['inquiry']도 false이고
                           라벨 "고객지원" 메뉴도 없을 때만 별도 fm-col로 표시

                        【동작 시나리오】
                        - 시나리오 A (현재 사이트): "고객지원" 카테고리 있음
                          → ① 자동 보충 발동 → 한 카테고리 안에 모든 항목 표시
                        - 시나리오 B: 관리자가 직접 "고객지원"에 "기업교육 문의"
                          링크 수동 추가
                          → /inquiry 중복 체크로 자동 보충 skip → 중복 없음
                        - 시나리오 C: 관리자가 "고객지원" 라벨을 "도움말" 등으로 변경
                          → 자동 보충 발동 안 함 → fallback이 별도 fm-col로 표시
                          (관리자 의도 존중)

                        【영향 범위】
                        - header.php 1개 파일만 수정
                        - 데스크탑 전체메뉴도 같은 컴포넌트를 공유하므로 동일하게 적용됨
                          (데스크탑은 가로 그리드라 중복도 보였지만 UX 영향은 작았음)

                        【4역할 협업】
                        🏗️ Architect: 두 "고객지원"의 정체(DB vs 하드코딩) 식별, 합치기 설계
                        🎨 Designer: 자료실 다음 위치 + 기존 텍스트 링크 스타일 유지
                        👨‍💻 Developer: 외과 수정 (header.php 2곳 변경)
                        🔍 Reviewer: 라벨 매칭/URL 중복 체크/fallback 동작 검증

  v77.85 (2026-05-18) - 🔐 로그인 페이지 + 회원가입 모바일 — 3가지 PPT 이슈 외과 수정:

                        【사용자 보고 — PPT 슬라이드 3장】
                        ① 로그인 페이지 비밀번호 작성란
                           - 보이게/안 보이게 토글 시 아이콘 모양이 안 바뀜
                           - 1) 비밀번호 안 보이게 → 사선 있는 눈(👁‍🗨)
                             2) 비밀번호 보이게 → 일반 눈(👁)
                        ② SNS 계정으로 로그인 버튼
                           - 모바일에서 동그란 G 버튼이 가로로 늘어난 타원으로 찌그러짐
                        ③ 모바일 회원가입 이메일 부분
                           - @ 기호가 사라져서 도메인 어디에 넣을지 헷갈림
                           - 빨간 박스 표시 부분(도메인 input 앞)에 @ 추가 필요

                        【원인 분석】
                        ① page-login.php L59~64
                           - 자체 esgTogglePw() 인라인 함수가 type만 토글하고
                             눈 SVG는 1개로 고정되어 있었음
                           - 같은 테마 main.js(v68.77~v75.5)에 이미 EYE_ON/EYE_OFF
                             두 아이콘 토글 글로벌 시스템이 존재 (data-pw-toggle 속성)
                           - 회원가입 페이지는 이 시스템을 정상 사용 중이었으나
                             로그인 페이지만 별도 구현 → 일관성 깨짐

                        ② page-login.php L229~230
                           - @media(max-width:560px) 안에 .esg-sns-btns(오타: 's' 추가)
                             + .esg-sns-btn { width:100% } 적용
                           - 실제 클래스명은 .esg-sns-btn-list / .esg-sns-btn(s 없음)
                           - 결과: 52×52 원형 버튼의 width가 100%로 늘어나
                             타원으로 찌그러짐 (height는 그대로 52)

                        ③ page-register.php L563
                           - @media(max-width:560px)에서 .esg-reg-at { display:none }
                           - 모바일 1열 레이아웃에서 @ 기호가 완전히 사라짐
                           - "이메일 / 직접입력" 두 input만 보여 사용자 혼동

                        【해결】
                        ① page-login.php
                           - 자체 토글 버튼 DOM + esgTogglePw() 인라인 함수 제거
                           - input에 data-pw-toggle 속성만 추가
                           - main.js 전역 핸들러가 자동으로 wrapper + EYE_ON/EYE_OFF
                             토글 + CAPS LOCK 안내까지 부착
                           - 회원가입 페이지와 동일한 동작 보장 (일관성 회복)

                        ② page-login.php @media(max-width:560px) 블록
                           - .esg-sns-btns(오타) 룰 제거
                           - .esg-sns-btn 모바일 전용: 56×56 원형 유지
                             (탭 영역 확보 위해 데스크탑 52 → 모바일 56)
                           - aspect-ratio:1/1, flex:0 0 56px로 비율 절대 보장
                           - .esg-auth-sns-list gap 1.25rem (간격 약간 확보)

                        ③ page-register.php @media(max-width:560px) 블록
                           - .esg-reg-at { display:none } 제거
                           - grid-template-columns: auto 1fr 로 변경
                           - 1행: 이메일 input (전체 폭)
                             2행: [@] [도메인 input]
                             3행: 도메인 select (전체 폭)
                           - @ 기호가 도메인 input 바로 앞에 명확히 보임

                        【영향 범위】
                        - page-login.php (HTML 1곳, JS 1곳, CSS 1곳)
                        - page-register.php (CSS 1곳, 모바일 @media만)
                        - main.js, functions.php, header.php, footer.php 변경 없음
                        - 데스크탑 동작 완전 동일 (모바일 @media에서만 수정)

                        【4역할 협업】
                        🏗️ Architect: 3개 이슈 모두 격리, 다른 파일 영향 없음 확인
                        🎨 Designer: 데스크탑 52→모바일 56 키움, @ 2행 자연스러운 배치
                        👨‍💻 Developer: 미니멀 외과 수정 (총 변경 4곳)
                        🔍 Reviewer: PHP syntax/CSS 매칭/main.js 통합 동작 확인

  v77.84 (2026-05-15) - 🎨 폼 입력 요소 — 모바일 글씨 크기/외형 영구 통일:

                        【사용자 보고】
                        모바일에서 1:1 상담문의 모달의 입력 필드들이 글씨 크기가
                        제각각으로 보임. 이름/이메일/문의종류/제목/내용 5개 필드가
                        같은 클래스 .fi 인데도 시각적으로 다르게 렌더링됨.
                        "전에도 수정 해 달라고 했었는데 반영이 잘 안되긴 하더라고"
                        — 반복 보고된 이슈.

                        【근본 원인 (4가지 복합)】
                        ① .fi/.fs/.fta 가 input/select/textarea를 동시에 잡지만,
                           브라우저는 native select와 input/textarea를 다른 박스로
                           렌더링 (특히 모바일 iOS/Android).
                        ② select에 -webkit-appearance:none 미적용 →
                           OS 기본 UI (회색 아이콘, 두꺼운 보더 등) 노출.
                        ③ font-size, line-height, padding이 rem 단위로 정의되어
                           브라우저별 폰트 크기 설정에 따라 시각 차이 발생.
                        ④ font-size .88rem (≈14px) < 16px → iOS Safari가
                           포커스 시 자동 줌인 발동 → 화면 흔들림 + 더 큰 시각 차이.

                        【해결】
                        .fi / .fs / .fta 시스템 전면 강화:
                        ① 모든 input/select/textarea 픽셀 단위로 통일:
                             height 44px (input/select), min-height 110px (textarea)
                             font-size 14px, line-height 1.5
                             padding 0 14px, border 1.5px solid var(--g200)
                        ② select에 appearance:none + 인라인 SVG 화살표:
                             input과 동일한 박스 외형 + 우측 12px에 화살표
                             padding-right 36px로 화살표 자리 확보
                        ③ placeholder도 명시적 font-size 14px (Firefox/Safari 통일):
                             color #94a3b8, font-weight 400, opacity 1
                        ④ 모바일 (640px 이하) 전용 블록:
                             font-size 16px (iOS 줌 방지 + 시각 일관성)
                             height 46px, padding 유지
                             textarea min-height 120px, padding 14px
                             label(.fl) font-size 13px, margin-bottom 6px
                        ⑤ disabled/readonly 상태 통일 (배경 #f8fafc, 비활성 회색).
                        ⑥ !important 사용:
                             기존 인라인 style="font-size:..." 등이 덮어쓰지 못하게
                             강제. (영향 범위가 .fi/.fs/.fta 클래스 한정이라 안전.)

                        【적용 범위】
                        .fi/.fs/.fta 클래스를 사용하는 모든 폼이 자동 통일됨:
                        - header.php #m-inquiry (1:1 상담문의 모달) — 5개 필드
                        - page-mypage.php — 13곳
                        - functions.php — 2곳
                        다른 .inq-input 폼(기업교육 문의)은 별도 시스템이므로 영향 없음.

                        【테스트 권장】
                        - 모바일 (iOS Safari, Android Chrome)에서 1:1 상담문의 열기
                        - input/select/textarea 모두 같은 박스 크기·글씨 크기인지
                        - placeholder도 입력값과 동일 크기인지
                        - select 화살표가 깔끔하게 우측에 떠 있는지
                        - 입력 필드 탭 시 화면이 줌인되지 않는지 (iOS)

  v77.83 (2026-05-15) - 🔒 수강신청 차단 완성 + 🎓 수강 취소 정책 + 📨 관리자 안내 편집:

                        【사용자 보고】
                        v77.82 적용 후에도 "준비중" 과정의 수강신청 모달이
                        떴음. 카드 클릭은 차단됐지만, 홈/추천과정에서 [수강신청]
                        버튼을 누르면 그대로 모달 진입.

                        【근본 원인】
                        v77.82는 page-courses.php의 카드만 차단했음.
                        ① 공용 카드 렌더 함수 esangedu_course_card() 미처리
                           → 홈/추천과정/관련과정 등 어디서든 미차단 버튼 노출.
                        ② footer.php의 window.esangeduEnroll 글로벌 함수에
                           신청기간 검증 없음 → 어디서든 호출하면 모달 열림.
                        ③ 결과적으로 v77.82의 서버 검증(esg_enroll_submit_handler)이
                           최종 방어를 하긴 하지만, UX적으로 모달이 한 번 뜨고
                           폼 제출 단계에서 차단되는 어색한 흐름.

                        【해결】

                        ─ ① 공용 카드 함수 차단 (functions.php) ─
                        esangedu_course_card() L14200 부근 — 버튼 출력 분기에
                        esg_course_can_enroll($post_id) 호출 추가:
                          - 신청가능 → 기존 [수강신청] 버튼
                          - 준비중   → [준비중] disabled 버튼 (회색)
                          - 마감     → [마감]   disabled 버튼 (회색)
                        홈/추천과정/관련과정 모든 곳에 자동 적용 (단일 함수이므로).

                        ─ ② 글로벌 함수 사전 검증 (footer.php) ─
                        window.esangeduEnroll에 신청기간 사전 검증 추가:
                          window.esgCheckEnrollPeriod(courseId) 신규 함수
                            → 신규 AJAX 'esg_check_enroll_period' 호출
                            → 서버 esg_course_can_enroll() 결과 반환.
                          esangeduEnroll() 흐름:
                            1. 입력값 검증
                            2. 로그인 체크
                            3. 신청기간 서버 검증 (await)
                               - 실패 → alert(서버 메시지) 표시 후 중단
                               - 성공 → _esgOpenEnrollAfterCheck()로 모달 진행
                          네트워크 오류 시: soft_pass=true로 통과시키되,
                          서버측 esg_enroll_submit_handler에서 어차피 한 번 더 차단.

                        ─ ③ 신청기간 사전 체크 AJAX (functions.php) ─
                        esg_check_enroll_period_handler() 신규:
                          - wp_ajax + wp_ajax_nopriv 양쪽 등록 (비로그인도 체크).
                          - course_id를 받아 esg_course_can_enroll() 결과 반환.

                        ─ ④ 수강 취소 정책 (사용자 측) ─
                        page-mypage.php L1511 부근 — 상태별 버튼 분기:
                          - 입금대기/신청완료 → [❌ 수강 취소] (직접 취소 OK, 기존)
                          - 입금확인/수강확정/승인완료 → [📞 취소 문의] (안내 모달)
                          - 취소/수료 → 버튼 자체 미노출 (기존)
                        새 모달 #esg-cancel-notice-modal:
                          - 과정/주문번호/현재상태 표시
                          - 관리자 편집 메시지 표시 (white-space: pre-wrap)
                          - [확인] 단일 버튼 (정보 전달용).

                        ─ ⑤ 서버 측 직접취소 차단 (functions.php) ─
                        esg_cancel_enrollment_handler — esg_can_user_self_cancel($status)
                        검사 추가:
                          - 직접취소 불가 상태에서 AJAX 요청 시 차단,
                            needs_contact=true와 함께 에러 반환.
                          - 클라이언트 우회 시도도 서버에서 차단됨.

                        ─ ⑥ 관리자 — 안내 메시지 편집 (functions.php) ─
                        esangedu_enrollments_page() 상단에 접을 수 있는
                        <details> 박스 추가:
                          - 9행 textarea (최대 3000자)
                          - [💾 저장] / [🔄 기본값으로 복원] 버튼
                          - AJAX 저장: wp_ajax_esg_save_cancel_notice
                          - option('esg_cancel_notice_message')에 저장.
                          - HTML 태그는 wp_strip_all_tags()로 제거 (XSS 방지).

                        【헬퍼 함수 신설】
                        - esg_can_user_self_cancel($status) — 직접취소 가능 여부
                        - esg_needs_cancel_notice($status)   — 안내 필요 여부
                        - esg_get_default_cancel_notice()    — 기본 메시지 (PHP+JS 공유)
                        - esg_get_cancel_notice_message()    — 저장된 or 기본 메시지

                        【영향 범위】
                        - functions.php: 4곳 수정/추가
                            ① esg_check_enroll_period_handler 신규 (L944~)
                            ② esangedu_enrollments_page 상단 관리자 UI (L11163~)
                            ③ esangedu_course_card 버튼 분기 (L14200~)
                            ④ 기존 helpers/cancel handler는 v77.82에서 이미 들어감
                        - footer.php: window.esangeduEnroll에 비동기 검증
                        - page-mypage.php: 버튼 분기 + 안내 모달 + JS 핸들러
                        - style.css: 본 changelog

  v77.82 (2026-05-15) - 🔒 수강신청 — 신청기간 외 차단 강화 (3-Layer 방어):

                        【사용자 보고】
                        수강신청(/courses) 목록에서 "준비중" 상태인데도
                        카드를 클릭하면 상세페이지로 들어가서 "준비중" 표시만 뜸.
                        사용자가 신청기간 아닌데 들어갈 수 있는 게 부자연스러움.
                        + 서버 측 검증이 없어서 개발자 도구로 disabled 우회하면
                        신청기간 외에도 신청 완료 가능 (보안 문제).

                        【근본 원인】
                        ① page-courses.php: 카드 전체가 <a href> 로 감싸져 있음
                           → 버튼은 disabled여도 카드 다른 영역 클릭 시 이동됨.
                        ② <a> 안에 <button> 중첩 (HTML 표준 위반).
                        ③ functions.php esg_enroll_submit_handler:
                           신청기간 검증 로직 전혀 없음. 클라이언트 disabled만
                           우회하면 신청 가능 (보안 취약).

                        【해결: 3-Layer 방어 시스템】

                        ─ Layer 1 (UX 차단): page-courses.php ─
                        ① 카드를 <a> → <div role="link" tabindex="0"> 로 변경.
                           HTML 표준 준수 + 접근성 유지.
                        ② data-can-enroll, data-status-txt 속성 추가.
                        ③ JS 통합 클릭 핸들러:
                           - 신청가능 → 상세페이지 이동 (location.href)
                           - 준비중   → "신청 준비중입니다 · MM.DD부터" 토스트
                           - 마감     → "신청이 마감되었습니다" 토스트
                        ④ 토스트 디자인: 우측 상단 슬라이드인 (데스크톱),
                           하단 슬라이드업 (모바일), 3초 후 자동 사라짐.
                        ⑤ 키보드 접근성: Enter/Space 키 핸들러 추가.
                        ⑥ 🛒 장바구니 버튼: 신청기간 무관 항상 활성화
                           (미리 담기 허용 — 사용자 결정).

                        ─ Layer 2 (UX 차단): single-course.php ─
                        ① 수강신청/바로결제 버튼: disabled 유지 (이미 OK).
                        ② 🛒 장바구니 버튼: disabled 조건 제거 → 항상 가능.
                           ($apply_can_enroll 의존성 제거, $_already_status만 검사).

                        ─ Layer 3 (보안 차단 — 핵심!): functions.php ─
                        ① esg_course_can_enroll($course_id) 헬퍼 함수 신규 추가:
                           - 신청기간(_course_apply_start/_course_apply_end) 검증
                           - 반환: ['can'=>bool, 'status'=>'before|open|closed|none',
                                    'message'=>string]
                        ② esg_enroll_submit_handler에 신청기간 서버 검증 추가:
                           - 신청 시작 전: "아직 신청기간이 아닙니다" 에러 반환
                           - 신청 마감 후: "신청이 마감되었습니다" 에러 반환
                           - 클라이언트 우회해도 서버에서 차단됨.
                        ③ esg_cart_add_handler는 의도적으로 검증 X
                           (장바구니는 미리 담기 허용 정책).

                        【설계 의사결정】
                        - 카드 클릭: 상세이동 차단 + 토스트 (UX 명확성)
                        - 장바구니: 신청기간 무관 (마케팅 & 사용자 편의)
                        - 신청완료 버튼: 클라이언트+서버 이중 차단 (보안)

                        【영향 범위】
                        - page-courses.php: 카드 마크업 + JS (Layer 1)
                        - single-course.php: 장바구니 버튼 1줄 수정 (Layer 2)
                        - functions.php: 헬퍼 함수 신규 + 핸들러 검증 추가 (Layer 3)
                        - style.css: 토스트 스타일 + 비활성 카드 스타일 추가

  v77.81 (2026-05-15) - 🤖 자동 캐시 삭제 — 페이지 자동 감지 시스템 (하이브리드):

                        【사용자 보고】
                        v77.79 적용 후에도 문의게시판(/community)에서 본인 글이
                        새로고침해도 안 보임. 슬러그 'community'가 헬퍼 함수에
                        하드코딩 안 되어 있어서 캐시 안 비워지는 문제.

                        【근본 원인】
                        v77.79 헬퍼 함수가 슬러그를 하드코딩으로 관리:
                          ['mypage', 'support', 'learnq', 'inquiry']
                        → 새 페이지 추가될 때마다 코드 수정 필요 (유지보수 부담).

                        【해결: 하이브리드 자동 감지 시스템】

                        ① esg_collect_user_facing_pages() 신규 함수:
                           세 가지 방법을 합쳐서 사용자 페이지를 자동 수집.

                           방법 A — page-*.php 템플릿 자동 감지:
                             SELECT post_id WHERE meta_key='_wp_page_template'
                             AND meta_value LIKE 'page-%.php' (page-courses.php 제외)
                             → esangedu 페이지 템플릿을 쓰는 모든 페이지를 자동 발견

                           방법 B — 알려진 슬러그 매칭 (안전망):
                             mypage, support, learnq, inquiry, community, about,
                             instructors, schedule, location, login, register,
                             lostpassword, privacy, terms (14개)
                             → 페이지 템플릿이 수동 지정 안 되어도 슬러그로 매칭

                           방법 C — esg_*_page 옵션 ID 매칭:
                             esangedu가 직접 관리하는 페이지 ID 옵션들
                             → esg_inquiry_page, esg_mypage 등에 등록된 모든 페이지

                        ② 성능 최적화 — 트랜션트 캐싱:
                           수집 결과를 1시간 동안 esg_user_pages_list 트랜션트에 저장.
                           매 캐시 삭제 호출마다 DB 쿼리 3개 안 돌고 메모리에서 가져옴.

                        ③ 자동 무효화 — 페이지 변경 즉시 재스캔:
                           esg_invalidate_cached_pages_list() 신규 함수.
                           다음 훅에 자동 등록:
                           • save_post_page  → 페이지 생성/수정 시
                           • delete_post     → 페이지 삭제 시
                           • trashed_post    → 휴지통 이동 시
                           • untrashed_post  → 휴지통 복원 시
                           • updated_option  → esg_*_page 옵션 변경 시
                           → 본인은 신경 안 써도 항상 최신 페이지 목록 유지

                        ④ 안전망 — 전체 캐시 비우기 (3단계):
                           WP 슈퍼 캐시가 정적 HTML로 미리 빌드한 파일은
                           페이지별 삭제만으로 안 비워지는 경우가 많음.
                           사용자 콘텐츠 등록은 빈도가 낮으므로
                           "전체 캐시 비우기 비용" < "본인 글 안 보이는 불편"
                           → 안전망으로 전체 비우기 추가.

                        【효과】
                        • 새 페이지 추가 → 자동으로 캐시 삭제 대상 포함 ✅
                        • 새 메뉴 슬러그 → page-*.php 템플릿 쓰면 자동 감지 ✅
                        • 슬러그 변경 → 페이지 ID로 처리하므로 영향 없음 ✅
                        • 영구적으로 코드 수정 불필요 ✅

                        【성능】
                        • 트랜션트 캐시로 DB 쿼리 1시간에 1번만
                        • 페이지 변경 시 즉시 무효화 → 항상 최신 데이터
                        • 캐시 삭제 호출 자체 비용은 동일

                        【파일 변경】
                        • style.css: 버전 v77.81
                        • functions.php:
                          - esg_clear_user_action_cache 자동 감지 방식으로 교체
                          - esg_collect_user_facing_pages 신규 함수 (+45줄)
                          - esg_invalidate_cached_pages_list 신규 함수 (+5줄)
                          - 5개 훅 자동 등록 (페이지 변경 감지)

  v77.80 (2026-05-15) - 🐛 캐시 삭제 버튼 "Unexpected token '<'" 오류 수정:

                        【사용자 보고】
                        사이트 설정 페이지의 "전체 캐시 즉시 삭제" 버튼 클릭 시
                        빨간 네트워크 오류:
                        "Unexpected token '<', '<!DOCTYPE'... is not valid JSON"

                        【원인】
                        v77.77에서 캐시 삭제를 <form method="post"> 폼으로 만들었는데,
                        사이트 설정 페이지 자체가 이미 큰 폼 안에 있음.
                        → HTML 폼 중첩 (nested form) 발생.
                        → 사이트 설정의 AJAX JS가 안쪽 폼 제출을 가로채서
                          JSON을 기대하지만 HTML (리다이렉트) 응답을 받음.
                        → 파싱 실패 → 오류 표시.

                        【해결】
                        폼 방식을 완전히 제거하고 순수 JavaScript AJAX 호출로 변경.
                        • PHP: esg_manual_clear_cache_handler를 wp_ajax_* 액션으로 등록
                          - admin_init 훅 제거 → wp_ajax_esg_clear_cache_manual 사용
                          - 응답: wp_send_json_success / wp_send_json_error
                          - nonce: esg_clear_cache_ajax
                        • HTML: <form>을 제거하고 <button type="button">만 남김
                        • JS: esgClearCacheManual() 함수 추가
                          - fetch() 로 ajaxurl 호출
                          - 응답 결과에 따라 버튼 라벨 동적 변경 ("⏳ 캐시 삭제 중..." → "✓ 삭제 완료!")
                          - 2.5초 후 원래 라벨로 복원
                          - 에러 시 alert 표시 + 버튼 활성화

                        【효과】
                        • 페이지 새로고침 없이 캐시 삭제 → 더 빠른 UX
                        • 폼 중첩 문제 영구 해결
                        • 다른 사이트 설정 폼과 충돌 없음

                        【파일 변경】
                        • style.css: 버전 v77.80
                        • functions.php:
                          - esg_manual_clear_cache_handler 핸들러 수정 (~10줄)
                          - 캐시 카드 UI: form → button + script (~50줄)

  v77.79 (2026-05-15) - 🗄 사용자 액션도 자동 캐시 삭제 (12개 핸들러 확장):

                        【사용자 보고】
                        v77.77 자동 캐시 삭제는 관리자 콘텐츠 저장에만 작동.
                        사용자가 문의/후기/학습질문 등록 시 본인 글이 바로 안 보임.
                        매분마다 캐시 삭제 버튼 누르기 부담.

                        【신규 헬퍼 함수】
                        esg_clear_user_action_cache($post_id = 0):
                        • $post_id > 0이면 그 글 캐시 삭제 (esg_clear_post_cache 활용)
                        • 마이페이지·고객지원·학습질문·문의 페이지도 자동 감지하여 삭제
                        • 캐시 함수 없어도 안전 (function_exists 가드)

                        【적용된 12개 핸들러】
                        🔴 1순위 (사용자 콘텐츠 등록, 즉시 표시 필수):
                        1. esg_review_submit_handler — 수강 후기 등록/수정
                        2. esg_inquiry_submit_handler — 1:1 상담 문의
                        3. esg_learnq_submit_handler — 학습 질문
                        4. esg_enroll_submit_handler — 수강 신청
                        5. esg_inquiry_submit — 기업 문의

                        🟡 2순위 (회원 정보 변경):
                        6. esg_register_submit_handler — 회원가입
                        7. esg_update_profile_handler — 개인정보 수정
                        8. esg_change_password_handler — 비밀번호 변경
                        9. esg_withdraw_member_handler — 회원 탈퇴
                        10. esg_cancel_enrollment_handler — 수강 취소

                        🟢 3순위 (찜/장바구니):
                        11. esg_wish_toggle_handler — 찜 토글
                        12. esg_cart_add_handler / esg_cart_remove_handler — 장바구니

                        【효과】
                        • 이용자: 본인이 등록한 글이 즉시 표시됨 ✅
                        • 관리자: 캐시 삭제 버튼 누를 일 거의 없음 ✅
                        • 사이트 속도: 캐시 활용 그대로 → 빠름 유지 ✅

                        【호환성】
                        • function_exists 가드: 캐시 헬퍼 없어도 에러 없이 동작
                        • 기존 동작에 추가만 함 (기존 코드 변경 없음)
                        • 모든 캐시 플러그인 호환 (WP Super/W3TC/Rocket/LiteSpeed)

                        【파일 변경】
                        • style.css: 버전 v77.79
                        • functions.php:
                          - 신규 헬퍼 esg_clear_user_action_cache (+25줄)
                          - 12개 핸들러에 캐시 삭제 호출 추가 (+24줄)

  v77.78 (2026-05-15) - 📱 모바일 전체메뉴 — SNS를 "웹사이트" 메뉴에 통합:

                        【사용자 피드백】
                        모바일 전체메뉴 하단에 SNS 동그라미 아이콘(인스타·블로그 등)이
                        덩그러니 있어서 UI가 어색함. 웹사이트 메뉴 안에 텍스트 링크로
                        넣어달라는 요청.

                        【변경 전 구조】
                        ┌─ fm-grid (메뉴 그리드)
                        │  ├─ 홈, 기관소개, AI 활용 과정, 교육일정...
                        │  ├─ "웹 사이트" 컬럼 (원격평생교육원, 국민내일배움카드 등 4개)
                        │  └─ "나의 강의실" 컬럼
                        └─ fm-sns (별도 영역, 동그란 SNS 4개 버튼) ← 이게 어색

                        【변경 후 구조】
                        └─ fm-grid (메뉴 그리드)
                           ├─ 홈, 기관소개, AI 활용 과정, 교육일정...
                           ├─ "웹 사이트" 컬럼:
                           │  ├─ 원격평생교육원
                           │  ├─ 국민내일배움카드
                           │  ├─ MEDYON
                           │  ├─ 나야넷 안전보건교육
                           │  ├─ 네이버 블로그   ← SNS도 같은 자리에
                           │  ├─ 유튜브
                           │  ├─ 인스타그램
                           │  └─ 카카오톡 채널
                           └─ "나의 강의실" 컬럼
                        (하단 동그라미 영역은 완전 제거)

                        【구현】
                        • header.php "fm-col (웹 사이트)" 영역 내부에 SNS 인라인 출력:
                          - $__fm_sns_inline 배열: blog/youtube/instagram/kakao + 한글 라벨
                          - 기존 SNS 옵션 시스템(esg_sns_*) 그대로 활용 → 사이트 설정에서
                            URL 입력하면 자동으로 메뉴에 표시됨
                          - URL이 빈 값 또는 '#' 이면 자동으로 표시 안 함
                          - 텍스트 링크 스타일은 기존 웹사이트 링크와 동일 (자동 통일)
                        • 기존 fm-grid 밖의 .fm-sns 영역 완전 제거 (HTML)
                          - .fm-sns / .fm-sns-btn CSS 정의는 유지 (안전망, 미사용)

                        【효과】
                        • 일관된 텍스트 링크 디자인으로 UI 통일감
                        • 메뉴 항목이 한 영역에 모여 사용자가 찾기 쉬움
                        • 미설정 SNS는 자동 숨김 → 깔끔
                        • 데스크톱 영향 없음 (전체메뉴는 모바일 우선 UI)

                        【파일 변경】
                        • style.css: 버전 v77.78
                        • header.php:
                          - 웹사이트 메뉴 컬럼에 SNS 인라인 추가 (+15줄)
                          - 하단 동그라미 SNS 영역 제거 (-32줄)

  v77.77 (2026-05-15) - 🗄 자동 캐시 삭제 시스템 + 차시 시간 표시 제거:

                        【배경 1: 캐시 문제】
                        파트너 보고: 강의목차 챕터/차시 입력하고 저장했는데
                        상세페이지에 안 나오는 현상. 시크릿 창에서는 정상 표시됨.
                        → 진단: WP 슈퍼 캐시가 옛 페이지를 그대로 캐싱 중.
                        → 해결 방향: "콘텐츠 저장 시 자동으로 그 페이지 캐시 삭제"

                        【배경 2: 차시 시간】
                        파트너 요청: 커리큘럼에 차시별 시간(10:00 등) 표시 제거.
                        관리자 편집 화면에서도 시간 입력칸 제거.
                        단, DB 데이터는 보존 (나중에 부활 가능).

                        【1. 자동 캐시 삭제 시스템】
                        신규 함수 (functions.php 끝부분):
                        • esg_clear_post_cache($post_id): 특정 글 캐시 삭제
                          - WP Super Cache, W3 Total Cache, WP Rocket, LiteSpeed 동시 지원
                          - 각 함수는 function_exists 가드로 안전 처리
                        • esg_clear_all_cache(): 전체 캐시 삭제
                        • esg_detect_cache_plugins(): 활성 캐시 플러그인 감지
                        • esg_get_cache_status(): 카드 표시용 상태 도우미
                        • esg_auto_clear_cache_on_save() — save_post 훅 priority 99
                          - course/instructor/post/page 저장 시 해당 글만 삭제
                          - 자동 저장/리비전은 무시
                        • esg_auto_clear_all_cache_on_settings() — admin_init 훅
                          - 사이트 설정 저장 시 전체 캐시 삭제
                        • esg_manual_clear_cache_handler() — 관리자 버튼 처리

                        【2. 사이트 설정 → 캐시 관리 카드】
                        보안 카드와 동일 패턴(esg-st-card)으로 추가.
                        • 카드 헤더 배지:
                          - "✓ 자동 삭제 작동 중" / "⚠ 캐시 플러그인 없음"
                          - 감지된 캐시 플러그인명 표시
                        • 펼치면 2개 그라데이션 박스:
                          1. 🤖 자동 캐시 삭제 (그린): 작동 상태 + 감지 목록
                          2. 🧹 전체 캐시 즉시 삭제 (네이비): 클릭 시 폼 제출
                        • 최근 삭제 시각 표시 (방금 전/N분 전/N시간 전/날짜)
                        • 삭제 완료 후 ?cache_cleared=1로 리다이렉트 → 성공 메시지

                        【3. 차시 시간 표시 제거】
                        • single-course.php: 차시 목록의 시간 라벨 제거
                          - $l_time 변수 자체는 보존 (구조 유지)
                          - <span class="esg-lesson-time">...</span> 출력만 제거
                        • functions.php: 관리자 챕터/차시 편집 영역
                          - 차시 시간 입력칸 (text input) → hidden input으로 변경
                          - 기존 DB 데이터는 그대로 유지 (저장 핸들러 변경 없음)
                          - JS 템플릿 2곳 (새 챕터 추가, 새 차시 추가)도 hidden으로 변경
                          - 사용자가 변경할 수 없으므로 항상 빈 값으로 저장
                        • 부활 방법: 이 3곳의 hidden을 text로 되돌리면 즉시 복원

                        【호환성·안정성】
                        • 모든 캐시 플러그인 호출은 function_exists/class_exists 가드
                          → 캐시 플러그인 업데이트·삭제·교체에 영향 없음
                        • save_post 훅 priority 99 → 다른 모든 저장 핸들러 끝난 후 실행
                          → 다른 플러그인과 충돌 없음
                        • DOING_AUTOSAVE / wp_is_post_revision 가드 → 자동 저장 무시
                        • current_user_can('manage_options') → 권한 없는 사용자 차단
                        • wp_nonce_field → CSRF 공격 방지

                        【파일 변경】
                        • style.css: 버전 v77.77
                        • functions.php:
                          - 자동 캐시 시스템 추가 (+150줄)
                          - 사이트 설정 캐시 카드 추가 (+95줄)
                          - 관리자 차시 시간 입력 hidden 변경 (-2줄)
                          - JS 템플릿 2곳 hidden 변경 (-2줄)
                        • single-course.php: 차시 시간 표시 제거 (-3줄)

  v77.76 (2026-05-15) - 📅 과정 카드 "교육일정" — 수업일 목록 표시 + 연속 구간 자동 압축:

                        【사용자 요청 3가지】
                        1) "교육기간 = 시작일~종료일 범위" 대신 실제 수업일 목록을 표시
                           예: 6/17, 6/24, 7/1, 7/8, 7/15 (요일은 제외, 날짜만)
                        2) 연속된 수업일은 압축 표시
                           예: 6/4, 6/5, 6/6, 6/7 → "6/4~6/7"
                        3) 라벨을 "교육기간"에서 "교육일정"으로 변경

                        【신규 공통 함수 (functions.php 끝부분)】
                        esg_format_session_dates_compact($session_dates):
                          - 입력: ['YYYY-MM-DD', ...] 배열
                          - 자동 정렬 + 중복 제거
                          - 연속된 날짜는 시작~끝 으로 묶음
                          - 단독 날짜는 그대로 콤마 구분
                          - 빈 배열/잘못된 형식은 빈 문자열 반환

                        검증된 케이스:
                          [6/17, 6/24, 7/1, 7/8, 7/15] → "6/17, 6/24, 7/1, 7/8, 7/15"
                          [6/4, 6/5, 6/6, 6/7]        → "6/4~6/7"
                          [6/4, 6/5, 6/7, 6/10~6/12]   → "6/4~6/5, 6/7, 6/10~6/12"
                          [6/29, 6/30, 7/1, 7/2]      → "6/29~7/2"  (월 경계 자연스럽게)
                          [7/8, 6/17, 6/24]           → "6/17, 6/24, 7/8"  (자동 정렬)

                        【적용 위치】
                        • esangedu_course_card() (functions.php):
                          - 변수 $session_dates 추가 (_course_session_dates 메타)
                          - $edu_period 생성 로직: 수업일 목록 우선, 없으면 시작~종료 폴백
                          - 라벨 "교육기간" → "교육일정"
                        • page-courses.php (수강신청 페이지):
                          - 동일하게 수업일 목록 우선, 라벨 "교육일정"

                        【영향 범위】
                        • 홈페이지: 신규/인기/추천 과정 카드 모두 적용
                        • 수강신청 페이지: 모든 과정 카드 적용
                        • 모바일 동일 적용 (반응형 CSS .cc-meta-row 그대로 사용)

                        【파일 변경】
                        • style.css: 버전 v77.76
                        • functions.php:
                          - 신규 함수 esg_format_session_dates_compact (+50줄)
                          - esangedu_course_card 메타 영역 라벨 + 로직 변경
                        • page-courses.php: edu_period 생성 로직 + 라벨 변경

  v77.75 (2026-05-15) - 🎨 과정 카드 메타 정보 변경: 강사 라인 제거, 신청기간+교육기간 표시:

                        【사용자 요청】
                        과정 카드에서 "강사" 이름 대신 "신청기간"을 위에 표시하고,
                        그 아래에 "교육기간"을 표시하도록 변경 요청.
                        모바일도 동일하게 적용.

                        【변경 사항】
                        • esangedu_course_card() (functions.php, 홈페이지 카드):
                          - 변수 추가: $e_start, $e_end (_course_start_date, _course_end_date)
                          - 변수 추가: $edu_period (교육기간 문자열 m.d ~ m.d 형식)
                          - 메타 영역: 강사 라인 제거 → 신청기간 + 교육기간 2줄로 변경
                          - 빈 줄 처리 동일 (카드 높이 통일 유지)
                        • page-courses.php (수강신청 페이지):
                          - 강사 라인 (dt: 강사) 제거
                          - 신청기간 + 교육기간 2줄로 단순화
                        • 가로형 카드 (horizontal layout)는 이미 v68.64에서 강사 제거됨
                          → 신청기간 + 개폐강 확정일 유지 (변경 없음)
                        • 모바일 동일 적용 (반응형 CSS는 .cc-meta-row 구조 그대로 사용)

                        【영향 범위】
                        • 홈페이지: 신규 과정, 인기 과정, 추천 과정 카드 모두 적용
                        • 수강신청 페이지: 모든 과정 카드 적용
                        • 단일 과정 페이지(single-course.php): 변경 없음 (카드가 아닌 상세 페이지)

                        【파일 변경】
                        • style.css: 버전 v77.75
                        • functions.php: +12줄 (변수 + edu_period 생성), 메타 영역 강사 → 교육기간 치환
                        • page-courses.php: -3줄 (강사 라인 제거)

  v77.74 (2026-05-14) - 🔒 휴대폰·이메일 중복 가입 차단 (1번호 1계정 정책):

                        【배경】
                        파트너 보고: A 회원이 자신의 휴대폰·이메일로 가입한 상태에서
                        B가 똑같은 휴대폰·이메일로 또 가입할 수 있는 문제 발견.
                        → 본인 사칭, 개인정보보호 위반, 법적 분쟁 위험.

                        【원인 분석】
                        • 이메일 중복: email_exists() 호출은 있으나, 대소문자 차이
                          (Abc@... vs abc@...)는 WordPress 내부적으로 처리되지만
                          명시적 정규화가 없어 안전망이 약함
                        • 휴대폰 중복: 검증 코드 자체가 없었음 (가장 심각)

                        【신규 함수 (functions.php 끝부분)】
                        • esg_normalize_phone($phone): 하이픈/공백 제거하여 비교용 정규화
                        • esg_phone_exists($phone, $exclude_user_id=0): phone, esg_phone
                          두 메타키 모두 검사. 하이픈 포함/미포함 변형 모두 매칭.
                          본인 제외용 exclude_user_id 파라미터 지원 (마이페이지 수정 시).
                        • esg_normalize_email($email): 소문자 + 공백 제거 정규화

                        【적용 위치 4곳】
                        • 회원가입 핸들러 (esg_register_submit_handler):
                          - 휴대폰 중복 검증 추가
                          - 이메일 정규화 후 저장
                          - 에러 메시지에 "아이디/비밀번호 찾기" 안내 추가
                        • 마이페이지 프로필 수정 (esg_update_profile_handler):
                          - 휴대폰 변경 시 중복 검증 (본인 제외)
                        • 관리자 회원 직접 생성:
                          - 휴대폰·이메일 중복 검증
                          - 이메일 정규화
                          - 휴대폰 입력 시 esg_phone 메타도 동기화 (SMS 시스템용)
                        • 비번 찾기 (page-lostpassword.php):
                          - get_user_by('email')은 WP 내부적으로 대소문자 무시이므로 변경 불필요

                        【실시간 중복 체크 (UX 개선)】
                        • 신규 AJAX 핸들러 2개:
                          - esg_check_phone: 휴대폰 번호 입력 후 포커스 이탈 시 즉시 체크
                          - esg_check_email: 이메일 입력 후 포커스 이탈 시 즉시 체크
                        • 회원가입 페이지(page-register.php):
                          - 휴대폰/이메일 필드 아래에 결과 메시지 영역 추가
                          - blur 이벤트로 자동 체크 → 사용자가 폼 제출 전에 확인 가능
                          - 결과: "✅ 사용 가능" / "⚠️ 이미 등록된 휴대폰 번호입니다"

                        【파일 변경】
                        • style.css: 버전 v77.74
                        • functions.php: +120줄
                          - 정규화/검증 함수 3개 (esg_normalize_phone, esg_phone_exists, esg_normalize_email)
                          - 실시간 체크 AJAX 핸들러 2개
                          - 회원가입·마이페이지·관리자 생성 핸들러에 검증 적용
                        • page-register.php: +75줄
                          - 휴대폰/이메일 필드에 메시지 영역
                          - blur 이벤트 기반 실시간 체크 JS

  v77.73 (2026-05-14) - 🔒 보안 카드 UX 개선 (사용자 피드백 반영):

                        【피드백 1】
                        • 약관 페이지 헤더에는 "← 사이트 설정으로" 버튼이 있는데
                          보안 설정 페이지에는 없어서 뒤로가기가 불편하다는 보고

                        【피드백 2】
                        • 무차별 대입 방어 페이지 안에 이미 "차단된 IP 목록" 섹션이 있으므로
                          사이트 설정 카드에 별도의 "차단 IP 목록" 박스를 두는 것은 중복이라는 보고

                        【수정 사항】
                        • esg_security_settings_page(): h1에 "← 사이트 설정으로" 버튼 추가
                          - 약관 관리 페이지(esangedu_terms_admin_page)와 동일한 패턴 사용
                          - admin.php?page=esangedu-group-site로 이동
                        • 사이트 설정 보안 카드:
                          - "🚫 차단된 IP 목록" 박스 제거 (총 3개 → 2개 박스로 단순화)
                          - "🛡 무차별 대입 방어" 박스에 차단 IP 정보를 통합:
                            기존: "✓ 활성화 · 5회/15분 차단"
                            변경: "✓ 활성화 · 5회/15분 차단 + 🚫 N개 IP 차단 중" (차단 있을 때만)
                          - 박스 하단 라벨: "⚙️ 설정 페이지로 이동" → "⚙️ 설정 & 차단 IP 관리"
                        • 보안 페이지 버전 표시 업데이트: v77.71 → v77.73

                        【파일 변경】
                        • style.css: 버전 v77.73
                        • functions.php:
                          - 사이트 설정 보안 카드 박스 3개 → 2개 (-50줄)
                          - 보안 설정 페이지 h1에 뒤로가기 버튼 (+1줄)
                          - 합계 -49줄 (단순화)

  v77.72 (2026-05-14) - 🔒 보안 설정 카드를 사이트 설정에 통합 (사용성 개선):

                        【배경】
                        v77.71에서 보안 설정을 별도 탭(esg_admin_groups_filter)으로 추가했으나,
                        실제 화면에서는 탭으로 노출되지 않는 문제 발견.
                        파트너가 "보안 메뉴 어디에 있는지 못 찾겠다" 보고.
                        → 직접 URL 접속 시(/wp-admin/admin.php?page=esangedu-security)는
                          페이지 정상 작동 확인됨. 단지 진입점이 안 보이는 문제.

                        【원인 분석】
                        esangedu 사이트 설정은 약관 관리처럼 큰 카드(esg-st-card) 안에
                        하위 항목 그라데이션 박스를 두는 패턴이 표준임.
                        v77.71의 별도 탭 방식은 esangedu UI 패턴과 불일치 → 사용자 혼란.

                        【해결】
                        보안 카드를 약관 관리 카드와 동일한 패턴(esg-st-card)으로
                        사이트 설정 페이지 안에 추가. 약관 카드 바로 다음 위치.

                        【보안 카드 구성】
                        • 카드 헤더: 🔒 보안 설정 (무차별 대입 방어 + 비밀번호 정책)
                        • 요약 배지:
                          - ✓ 방어 활성 / ⚠ 방어 비활성 (실시간 상태)
                          - 차단 IP 없음 / 🚫 N개 IP 차단 중 (실시간 카운트)
                        • 펼치면 3개 그라데이션 박스:
                          1. 🛡 무차별 대입 방어 (네이비-블루 그라데이션) → 설정 페이지 링크
                          2. 🚫 차단된 IP 목록 (차단 있으면 빨강, 없으면 회색) → 목록 페이지
                          3. 🔑 비밀번호 정책 (그린 그라데이션, 정보 표시만)
                        • 하단 운영 팁: 화이트리스트 IP 등록 안내

                        【v77.71 기능은 그대로 유지】
                        • esangedu-security 페이지 자체는 변경 없음 (코드 유지)
                        • 무차별 대입 방어 시스템 정상 작동
                        • 비밀번호 정책 정상 적용 중
                        • 단지 사이트 설정 페이지에 접근 카드만 추가

                        【파일 변경】
                        • style.css: 버전 v77.72
                        • functions.php:
                          - 사이트 설정 렌더 함수에 보안 카드 통계 변수 추가 (+13줄)
                          - 약관 카드 다음에 보안 카드 HTML 추가 (+85줄)
                          - 합계 +98줄

  v77.71 (2026-05-14) - 🔒 호스팅 전 보안 강화 + 비밀번호 정책 강화:

                        【1️⃣ 디버그 정보 노출 차단 (🔴 1순위)】
                        • page-lostpassword.php: ?debug=1 URL 파라미터 제거
                          기존: 누구나 ?debug=1 붙이면 회원정보 일부 노출 가능
                          수정: current_user_can('manage_options') 관리자만 디버그 확인

                        【2️⃣ PHP 파일 직접 접근 차단 (🟡 2순위)】
                        • 18개 페이지 파일 최상단에 ABSPATH 가드 추가
                          if (!defined('ABSPATH')) exit;
                        • 대상: page-about, page-community, page-courses, page-custom,
                          page-inquiry, page-instructors, page-location, page-login,
                          page-lostpassword, page-mypage, page-privacy, page-register,
                          page-schedule-list, page-schedule, page-support, page-terms,
                          single-course, single-instructor, inc/sms-system

                        【3️⃣ 무차별 대입 방어 시스템 (🟡 3순위)】
                        • 신규: 관리자 설정 → 보안 탭 추가 (esg_security_*)
                          - 활성화 토글 (사용함/사용 안 함)
                          - 허용 실패 횟수 (기본 5회, 1~20회 조절 가능)
                          - 차단 시간 (기본 15분, 1~1440분 조절 가능)
                          - 카운트 유지 시간 (기본 30분)
                          - 관리자 차단 예외 (실수로 본인 잠금 방지)
                          - 사용자 알림 방식 (남은 횟수 표시/단순 에러)
                          - 화이트리스트 IP (사무실 IP 등록)
                        • 작동 방식:
                          - wp_login_failed 훅 사용, IP+사용자명 기준 카운트
                          - WordPress transient에 저장 (자동 만료)
                          - 실패 시 친절한 안내 메시지 ("3/5회 실패 — 2회 남음")
                          - 차단 IP 목록 + 수동 해제 버튼 (관리자 화면)
                          - 기본값 복원 버튼 포함

                        【4️⃣ 비밀번호 정책 강화 (🔑)】
                        • 기존: 영문 + 숫자 필수
                        • 변경: 영문 + 숫자 + 특수문자 모두 필수
                        • 허용 특수문자: !@#$%^&*()-_=+[]{};:,.<>?/~`|\
                        • 적용 위치:
                          - 회원가입 (서버: functions.php / 클라이언트: page-register.php)
                          - 마이페이지 비번 변경 (functions.php)
                          - 비번 찾기 재설정 (page-lostpassword.php)
                        • 실시간 강도 표시 추가 (회원가입 페이지):
                          - 영문 ✓ / 숫자 ✓ / 특수문자 ✓ / 8자 이상 ✓
                          - 4가지 조건 충족 시 "🟢 안전한 비밀번호" 표시
                        • 기존 회원 영향 없음 (비번 변경 시점부터 적용)

                        【파일 변경 요약】
                        • style.css: 버전 v77.71
                        • functions.php: 보안 시스템 + 비번 검증 함수 (+약 280줄)
                        • page-register.php: 비번 안내 + 실시간 강도 표시 JS (+30줄)
                        • page-lostpassword.php: 디버그 차단 + 비번 정책 (+5줄)
                        • 18개 페이지/inc 파일: ABSPATH 가드 (+1줄씩)

                        【호스팅 후 권장 플러그인】
                        • UpdraftPlus (자동 백업)
                        • WPS Hide Login (관리자 URL 숨김)
                        • Advanced Google reCAPTCHA (봇 차단)
                        ※ Wordfence는 3순위 무차별 대입 방어와 겹치므로 제외 권장

  v77.70 (2026-05-13) - v77.67 SNS 위치 픽스: fm-grid 안 → 밖으로 이동:

                        【사용자 보고 + 캡처】
                        v77.67에서 추가한 SNS 버튼 4개가 전체메뉴 하단이 아니라
                        "홈 전체" 메뉴 카드 옆(각 메뉴 카테고리 옆)에 표시됨.

                        【원인】
                        v77.67에서 SNS 영역(.fm-sns)을 .fm-grid 닫기 직전(라인 300 부근)
                        에 넣었는데, .fm-grid가 CSS Grid 컨테이너라 .fm-sns가
                        grid의 자식 cell로 잡힘 → 다른 .fm-col 카테고리 옆에 표시됨.
                        + v77.67에서 추가한 .fm-body 클래스는 코드에 존재하지 않는
                        클래스여서 CSS가 작동 안 함.

                        【해결】
                        SNS를 .fm-grid 밖, .fm-inner 직속 자식으로 이동.
                        구조:
                          <div class="fm-inner">
                            <div class="fm-grid">...메뉴...</div>  ← grid 닫힘
                            <div class="fm-sns">...4개 버튼...</div>  ← grid 밖
                          </div>
                        CSS: margin-top:1.5rem + border-top:1px + width:100% 풀폭.
                        존재하지 않던 .fm-body 룰 2줄 제거.

                        SNS 옵션이 모두 비어있으면 .fm-sns div 자체를 출력 안 함
                        (PHP 사전 체크 + if (!empty($__fm_sns_visible)): 감싸기).

                        【영향】
                        header.php (SNS 영역 위치만 이동 + 빈 값 체크 강화)
                        SNS 표시 동작 변화: 메뉴 옆 X → 메뉴 전체 아래 한 곳 ✓
                        다른 영역 영향 없음

  v77.69 (2026-05-13) - v77.65 임시 디버그 도구 제거 (원인 파악 완료):

                        【배경】
                        v77.66의 form nested 픽스로 사이트 설정 저장 정상 작동 확인됨.
                        사용자 캡처: esg_footer_address "서울특별시 강남구 테헤란로
                        123_1234" → "123456" 변경됨 ✅
                        POST 정상 + DB 저장 정상 + 캐시 자동 비움 정상.

                        【정리 — v77.69에서 제거】
                        1. functions.php esangedu_settings_page() 안의 v77.65 디버그
                           코드 두 영역 제거:
                           ▸ 영역 1: 저장 전 데이터 수집 ($__debug_before, $__debug_post)
                                     약 25줄
                           ▸ 영역 2: 저장 후 디버그 테이블 출력 ($__debug_after,
                                     foreach 결과 테이블) 약 45줄
                        2. v77.65 마커 0건 잔존 (완전 제거 확인)

                        【유지 — 유용한 도구】
                        - v77.64 진단 박스 (사이트 설정 페이지 상단 노란 박스)
                          → 사용자가 언제든 DB 실제 값 확인 가능. 유용하므로 보존.
                        - v77.64 🧹 캐시 즉시 비우기 버튼 → 유지.
                        - v77.63 저장 후 자동 캐시 무효화 → 유지.
                        - v77.66 form nested 픽스 → 유지 (핵심 픽스).

                        【영향】
                        functions.php (-70줄, 디버그 코드 제거)
                        사이트 설정 페이지 — 저장 후 노란 디버그 테이블 표시 안 됨
                        다른 영역 영향 없음

  v77.68 (2026-05-13) - PPT9 9장 슬라이드 통합 변경 (폰트 통일 + 디자인 정밀 정리):

                        【사용자 요청 — 9장 슬라이드 한 번에 처리 (옵션 A)】

                        ▸ 슬라이드 1: 기업교육 문의 폼 9개 필드 폰트 사이즈 통일
                                       (.inq-input의 input/select/textarea 모두 .92rem
                                        !important로 강제)
                        ▸ 슬라이드 2: 기업교육 폼 동의 영역 border-top 직선 제거
                                       (textarea 아래 회색 가로선 원인)
                        ▸ 슬라이드 3: 마이페이지 조회기간 날짜 input 폰트 .92rem 통일
                        ▸ 슬라이드 4: 1:1 문의 모달 input/textarea/file/label 폰트 .92rem 통일
                        ▸ 슬라이드 5: 비공개 체크박스 좌측 정렬 강화 (v77.67 부족했던 부분)
                                       .fg label 자체에 flex + justify-content:flex-start
                        ▸ 슬라이드 6: '문의 내용' / '관리자 답변' 헤더 좌측 정렬 PC도 적용
                                       기본 CSS .esg-myinq-section-hd { space-between }
                                       → { flex-start }. 답변 헤더의 날짜만 margin-left:auto
                        ▸ 슬라이드 7: 수강신청 모달 4개 input 폰트 .92rem 통일
                                       (signup-modal / signup-form 광범위 셀렉터)
                        ▸ 슬라이드 8: 수강신청 모달 × 닫기 버튼 타원 → 원형
                                       (v77.50 패턴: button { min-height:40px } 무력화)
                        ▸ 슬라이드 9: 나의 1:1 문의 헤더 ‹ 화살표 + 글자 세로 정렬
                                       .my-mobile-back-btn flex align-items:center,
                                       .back-arrow line-height:1 + transform 미세 조정

                        【변경 파일】
                        - page-mypage.php: 기본 .esg-myinq-section-hd 좌측 정렬 (PC도)
                                            + 모바일 미디어쿼리에 v77.68 통합 블록 ~90줄
                        - page-inquiry.php: 동의 border-top 제거 + .inq-input 폰트 강제 통일
                        - style.css: changelog

                        【호환성】
                        - PC 영향: 슬라이드 1, 2, 6은 PC도 적용 (의도)
                        - 모바일 미디어쿼리: 슬라이드 3, 4, 5, 7, 8, 9
                        - v77.x 누적 작업 모두 유지

  v77.67 (2026-05-13) - PPT8 7장 슬라이드 통합 변경 (모바일 1:1 문의 모달 + 전체메뉴 SNS + 자료실):

                        【사용자 요청 — 7장 슬라이드 한 번에 처리 (옵션 A)】

                        ▸ 슬라이드 1-①: 1:1 문의 상세 모달 메타+본문 레이아웃 정렬
                        ▸ 슬라이드 1-②: "아직 답변이 등록되지 않았습니다" 안내 영역 삭제
                                          (HTML 제거 + JS null 안전 처리)
                        ▸ 슬라이드 2  : 전체메뉴 하단에 SNS 버튼 4개 추가
                                          (YouTube/Instagram/Blog/Kakao)
                                          기존 esg_sns_{key} 옵션 시스템 활용,
                                          URL 비어있으면 자동 숨김
                        ▸ 슬라이드 3-①: 1:1 문의 작성 모달 "비공개 문의" 체크박스
                                          체크박스 좌 + 텍스트 좌측 정렬 + 풀폭
                        ▸ 슬라이드 3-②: 등록하기 바 하단 고정 (v77.59 sticky 이미 적용 — 확인)
                        ▸ 슬라이드 4  : 1:1 문의 상세 모달 "닫기" 버튼 영역(.fg-action) 삭제
                                          (× 버튼으로 충분)
                        ▸ 슬라이드 5-①: "📝 문의 내용" 헤더 좌측 정렬
                                          (기본 .esg-myinq-section-hd { justify-content:
                                          space-between } 이 우측으로 밀어내던 문제)
                        ▸ 슬라이드 5-②: "💬 관리자 답변" 헤더 좌측 정렬 + 날짜만 우측
                                          (.esg-myinq-reply-date { margin-left:auto })
                        ▸ 슬라이드 6  : 모달 헤더와 본문 사이 공백 축소
                                          (.esg-modal-body padding-top 축소 +
                                          .esg-myinq-section margin-bottom 축소)
                        ▸ 슬라이드 7  : 자료실 다운로드 버튼 사이즈 = 비밀번호 입력 버튼과 동일
                                          (min-width:110px + justify-content:center)

                        【변경 파일】
                        - page-mypage.php : HTML 영역 (모달 fg-action+noreply 제거) +
                                            JS null 안전 처리 + 모바일 CSS v77.67 블록 ~80줄
                        - header.php      : 전체메뉴 fm-body 끝에 SNS 버튼 영역 + CSS
                        - page-community.php: 자료실 두 버튼 inline style (min-width)
                        - style.css       : changelog

                        【호환성 / 안전성】
                        - SNS 버튼: URL 옵션이 '#' 또는 비어있으면 표시 안 함
                        - JS null 안전 처리: noReply 제거에 따른 TypeError 방지
                        - v77.x 누적 작업 모두 그대로 유지

                        【PC 영향】
                        - 자료실 버튼 min-width:110px → PC에도 적용됨 (시각 개선)
                        - 1:1 문의 모달 변경은 모바일 미디어쿼리 안에서만
                        - 전체메뉴 SNS: PC에서도 모바일 햄버거로 전체메뉴 열면 표시됨

  v77.66 (2026-05-13) - 🎯 진짜 원인 발견: form nested 구조로 사이트 설정 저장 자체가 동작 안 했음:

                        【배경 — 사용자가 정확히 보고한 증상】
                        v77.65 디버그 테이블이 저장 눌러도 안 나타남
                        = isset($_POST['esg_settings_save']) 가 false
                        = 메인 form의 submit 자체가 작동 안 함

                        【진짜 원인】
                        functions.php esangedu_settings_page() 안에서:
                          라인 12531: <form method="post"> ← 메인 form 시작
                            ...사이트 타이틀 카드, 회사 정보 카드...
                          라인 12788: <form method="post" action=""> ← SMTP form (nested!)
                          라인 12860: </form> ← SMTP form 닫기 (그러나 브라우저는
                                                메인 form 닫기로 해석함!)
                          라인 12863: <form method="post" action="..."> ← 테스트 메일 form
                          라인 12874: </form>
                          라인 12968: <input type="submit" name="esg_settings_save">
                                       ← 이 submit 버튼은 어떤 form에도 안 속함!

                        HTML 표준은 form nested 금지. 브라우저는 첫 </form> 만나면
                        가장 가까운 <form> 자동 닫음. 결과:
                          - 메인 form은 라인 12788의 SMTP form 시작 시점에 끊김
                          - submit 버튼이 form 밖이라 클릭해도 데이터 전송 X
                          - 회사 정보 카드의 주소/전화/이메일 등 모든 옵션 저장 실패

                        【해결】
                        nested form 2개 제거:
                          1. SMTP form 태그(<form>, </form>) 제거
                             - input 필드는 메인 form 안에 그대로 남김
                             - SMTP 옵션 저장 로직을 메인 esg_settings_save 핸들러에 통합
                             - "💾 SMTP 설정 저장" 버튼 제거 + 안내 메시지로 대체
                          2. 테스트 메일 form 제거
                             - admin-post.php로 가는 별도 form이라 통합 불가
                             - 테스트 메일 발송 기능은 일단 제거
                             - 필요 시 사이트 설정 페이지 외부에 별도 도구로 추후 추가 가능

                        SMTP 저장은 이제 메인 form의 "📁 모든 변경사항 저장" 클릭 시
                        다른 옵션과 함께 처리됨.

                        【영향】
                        functions.php (form 태그 제거 + SMTP 저장 로직 메인 핸들러에 통합)
                        사이트 설정 페이지 모든 옵션 저장이 정상 작동
                          (회사 정보, 사이트 타이틀, SMTP, 약관, SNS 등)
                        테스트 메일 발송 UI 일시 제거 (필요시 v77.67에서 별도 페이지로 추가)

  v77.65 (2026-05-13) - 저장이 안 되는 진짜 원인 추적용 디버그 출력 추가:

                        【배경】
                        사용자 캡처 확정:
                          - 진단 박스 DB 값: 서울특별시 강남구 테헤란로 123_1234
                          - 사이트 푸터 표시: 같은 값
                          - form input 필드: 123456789 (방금 입력)
                        → 사이트는 DB 값을 정확히 표시 중. 캐시 문제 아님.
                        → 진짜 문제: 사용자가 입력한 새 값이 DB에 저장이 안 됨.

                        【추가 진단 도구】
                        저장 분기에 디버그 테이블 추가:
                          - 저장 전 DB 값
                          - POST로 받은 값 (실제로 form에서 PHP로 넘어왔는지)
                          - 저장 후 DB 값
                          - 결과 (✅ 변경됨 / ❌ 변경 안 됨 / ➖ 동일)

                        이걸로 정확히 어느 단계에서 끊기는지 확인 가능:
                          - POST 값이 "(POST에 없음!)" → form에서 PHP로 안 옴
                            (form action / nonce / submit 버튼 문제)
                          - POST 값은 있는데 저장 후 DB가 안 바뀜 → update_option 실패
                            (옵션 보호 플러그인 / DB 쓰기 권한 / 필터 등)
                          - 저장 후 DB가 바뀌었는데 다음 페이지 로드 시 안 바뀜
                            → 다른 곳에서 옵션을 덮어쓰는 코드 존재

                        【사용 가이드】
                        1. 사이트 설정 → 회사 정보 카드 펼치기
                        2. 주소 필드 등 수정
                        3. "📁 모든 변경사항 저장" 클릭
                        4. 상단에 노란 디버그 테이블이 표시됨
                        5. 그 테이블 결과를 캡처해서 알려주면 정확한 원인 추적 가능

                        【영향】
                        functions.php (esangedu_settings_page 저장 분기에 디버그 +1)
                        실제 저장 로직 변경 없음
                        디버그는 v77.66 이후 제거 예정

  v77.64 (2026-05-13) - 푸터 정보 진단 패널 + 캐시 비우기 단독 버튼 추가:

                        【배경 — 사용자 보고 지속】
                        v77.63에서 저장 후 자동 캐시 무효화 추가했지만 사용자가
                        주소 필드에 "123456" 저장해도 사이트엔 옛 값
                        "서울특별시 강남구 테헤란로 123_1234" 그대로 표시.

                        【코드 재검증】
                        - 저장 input name="footer_address" → update_option('esg_footer_address')
                        - footer.php $f_address = get_option('esg_footer_address')
                        - 모두 일관됨. 코드 자체 문제 없음.

                        【추정 원인】
                        v77.63 zip 미적용 또는 외부 캐시(Cloudflare 등 CDN, 외부
                        리버스 프록시, 호스팅 자체 풀페이지 캐시)로 인한 옛 HTML 서빙.

                        【해결 - 진단 + 자가 해결 도구 추가】
                        사이트 설정 페이지 상단에 노란 박스로 "🔍 푸터 정보 진단"
                        패널 추가 (접힘 상태로 시작, 클릭 펼침):
                          - DB에 실제 저장된 모든 푸터 옵션 값을 표로 표시
                            (esg_footer_company, esg_footer_ceo, esg_footer_brn,
                             esg_footer_phone, esangedu_email, esg_footer_privacy_mgr,
                             esg_footer_address, esg_footer_edu_reg, esg_footer_copyright)
                          - "이 값이 사이트와 다르면 = 캐시 문제" 명확 안내
                          - 🧹 모든 캐시 즉시 비우기 버튼 (저장 없이 단독 실행)
                          - Cloudflare 사용 시 별도 처리 필요 안내

                        캐시 비우기 핸들러는 v77.63과 동일한 함수 체크 호출.
                        (wp_cache_flush, w3tc_flush_all, wp_cache_clear_cache,
                         rocket_clean_domain, litespeed_purge_all, opcache_reset)

                        【사용 가이드】
                        1. 관리자 → 사이트 설정 → 노란 박스 열기
                        2. DB 실제 저장값 확인
                        3. 사이트와 일치하지 않으면 🧹 캐시 비우기
                        4. Ctrl+Shift+R로 강제 새로고침해서 다시 확인
                        5. 그래도 안 되면 Cloudflare 대시보드에서 별도 비우기

                        【영향】
                        functions.php (esangedu_settings_page에 진단 패널 + 핸들러)
                        화면: 사이트 설정 페이지 상단에 노란 박스 추가만
                        사이트 푸터 자체 변화 없음 (진단 도구임)

  v77.63 (2026-05-13) - 관리자 푸터 정보 변경 후 사이트에 반영 안 되는 문제 (캐시 픽스):

                        【배경 — 사용자 보고】
                        관리자 페이지에서 푸터 회사정보(전화/이메일/주소/사업자번호 등)
                        를 변경하고 저장해도 PC/모바일 사이트에 옛 값 그대로 표시.

                        【코드 진단 결과】
                        functions.php esangedu_settings_page() 의 저장 로직 정상
                        (update_option으로 esg_footer_* 옵션에 저장).
                        footer.php의 읽기 로직 정상 (같은 옵션 키 사용).
                        → 코드 자체는 문제 없음.

                        【진짜 원인 가능성 (최빈)】
                        외부 페이지 캐시 플러그인이 푸터 HTML을 정적으로 캐시해서
                        update_option 후에도 옛 HTML을 그대로 서빙:
                          - WP Super Cache
                          - W3 Total Cache
                          - WP Rocket
                          - LiteSpeed Cache
                          - Cloudflare 등 CDN
                        update_option은 WP 코어의 옵션 캐시는 무효화하지만, 정적 HTML
                        캐시는 무효화 안 함.

                        【해결】
                        사이트 설정 저장 직후 표준 캐시 무효화 함수들을 일괄 호출:
                          - wp_cache_flush()          ← WP 오브젝트 캐시
                          - wp_cache_clear_cache()    ← WP Super Cache
                          - w3tc_flush_all()          ← W3 Total Cache
                          - rocket_clean_domain()     ← WP Rocket
                          - litespeed_purge_all 액션  ← LiteSpeed
                          - opcache_reset()           ← PHP opcache
                          - do_action('esg_footer_settings_updated') ← 커스텀 훅
                        function_exists / class_exists 체크로 해당 캐시 시스템이
                        설치된 경우만 호출 (미설치 시 에러 없이 건너뜀).

                        성공 메시지에도 "페이지 캐시도 함께 비웠습니다" 안내 추가.

                        【사용자 진단 가이드 함께 제공】
                        - 관리자 페이지가 wp-admin/admin.php?page=esangedu-settings 인지
                        - 저장 후 "✅ 설정이 저장되었습니다" 메시지 확인
                        - 시크릿 모드에서 사이트 열어 새 값 보이는지 확인
                        - Cloudflare 사용 시 별도 캐시 비우기 필요

                        【영향】
                        functions.php (esangedu_settings_page 함수 안 ~12줄 추가)
                        화면 변경 없음 (백엔드 로직만)
                        모든 캐시 플러그인과 호환 (함수 존재 체크)

  v77.62 (2026-05-13) - 모바일 검색 결과 카드에 강좌 포스터 이미지 표시:

                        【배경】
                        모바일에서 검색창에 "AI" 등 검색하면 결과 카드의 썸네일
                        영역이 어두운 박스 + 작은 책 모양만 보임. 강좌 포스터
                        이미지가 안 나옴.

                        【원인】
                        functions.php esg_live_search_handler() 가 'thumb' 값을
                            get_the_post_thumbnail_url($id, 'medium')
                        로만 가져옴. 그러나 사이트의 강좌는 대표이미지 대신
                        _course_poster 메타에 포스터를 저장하는 컨벤션 →
                        대부분 강좌의 thumb가 빈 문자열 → JS가 📚 이모지 fallback
                        → 어두운 박스 + 작은 글자.

                        【해결】
                        thumb 값을 다음 순서로 fallback:
                          _course_poster 메타 → 대표이미지 → 빈 문자열
                        학습 카드(v77.56) 패턴과 동일.

                        【영향】
                        functions.php (1줄 변경, esg_live_search_handler 안)
                        검색 결과 드롭다운의 모든 카드 (PC + 모바일)
                        검색 JS / CSS 변경 없음
                        다른 페이지 영향 없음

  v77.61 (2026-05-13) - v77.60 후속: 액션 영역(등록 버튼) 우측 빈 공간 픽스:

                        【배경】
                        v77.60 적용 후 헤더는 풀폭 정상, 하지만 액션 영역(취소/등록)
                        만 좁음.
                        DevTools: div.fg-action 폭 422.94 (박스 풀폭 ~480 미달).

                        【원인】
                        v77.60에서 헤더/본문/액션을 통합 셀렉터로 묶어
                            { width:100% !important; max-width:100% !important }
                        를 박았는데, 액션 영역은 v77.54에서
                            { margin: 0 -1.25rem !important }
                        로 부모 padding을 음수 마진으로 풀어내 박스 풀폭으로 만드는
                        구조였음.
                        v77.60의 width:100% + max-width:100% 가 음수 마진의 width
                        확장을 무력화 → 액션이 body 콘텐츠 폭에 갇힘.

                        【해결】
                        통합 셀렉터에서 액션 영역 제거 (헤더/본문만 width:100%):
                          .esg-modal .esg-modal-hd,
                          .esg-modal .esg-modal-body { width:100%; max-width:100% }

                        액션 영역은 별도 룰로 명시적 width 계산:
                          .esg-modal .fg-action,
                          .esg-modal .esg-modal-actions,
                          .esg-modal .esg-modal-footer {
                            width: calc(100% + 2.5rem) !important;
                            max-width: none !important;
                            margin-left: -1.25rem !important;
                            margin-right: -1.25rem !important;
                          }
                        2.5rem = body 좌우 padding(1.25rem) × 2.

                        【영향】
                        page-mypage.php (모바일 미디어쿼리)
                        v77.54의 액션 sticky 룰(아래쪽)은 그대로 유지
                        PC 영향 없음

  v77.60 (2026-05-13) - v77.59 후속: 모달 박스 우측/하단 빈 공간 추가 픽스:

                        【배경】
                        v77.58/59 적용 후에도 사용자 캡처에서 우측/하단 빈 공간 존재.
                        DevTools 정보:
                          - .esg-modal-hd 폭: 436.14 (viewport ~480)
                          - .fg-action 폭: 383.22
                        둘이 다른 폭 = 박스의 width가 viewport 100% 못 채우는 케이스
                        가능성.

                        【해결】
                        (1) Specificity 강화: .esg-modal → .esg-modal.esg-modal
                            (같은 클래스 두 번으로 specificity 인위 증가, 어떤 기본
                            룰도 못 이기게)
                        (2) width/height 다중 단위 명시:
                            100vw + 100dvw + min-width 100vw + max-width 100vw
                            100vh + 100dvh + max-height 100vh + max-height 100dvh
                        (3) box-sizing: border-box 명시 (혹시 모를 box model 차이)
                        (4) overflow-x: hidden (가로 스크롤바 잠재 원인 차단)
                        (5) 헤더/본문/액션 모두 width:100% + box-sizing:border-box

                        【통합】 v77.58의 단순 .esg-modal { padding:0 } 룰은 v77.60에
                        통합되어 제거 (중복 + 약한 specificity 였음).

                        【영향】
                        page-mypage.php (모바일 미디어쿼리 안)
                        모든 .esg-modal 컴포넌트 (7개)
                        PC 영향 없음

  v77.59 (2026-05-13) - v77.58 보강: 모달 우측/하단 빈 공간 픽스:

                        【배경 — 사용자 캡처】
                        v77.58 적용 후에도 모달 우측 상단과 우측 하단(등록 버튼 옆)에
                        살짝 빈 공간이 남음. 빨간 네모 표시.

                        【원인 가설 2가지】
                        (1) width:100% / height:100% 가 일부 환경에서 viewport와
                            정확히 일치 안 함. position:fixed 안에서 100%는 부모
                            의존이 아니라 viewport지만, 일부 모바일에서 스크롤바/UI
                            영역으로 차이날 수 있음.
                        (2) 모달 본문이 짧으면 sticky bottom:0이 박스 하단까지
                            안 내려가고 자기 자리에 머묾 → 등록 버튼 아래 공간.

                        【해결】
                        (1) viewport 단위 명시:
                            .esg-modal { width:100vw; height:100vh; height:100dvh }
                            .esg-modal-box { width:100vw; height:100vh; height:100dvh }
                            100dvh = dynamic viewport height (모바일 주소창 변동에도
                            정확). 미지원 브라우저는 100vh fallback.
                        (2) 박스를 flex column으로 + body가 flex:1로 남는 공간 채움
                            → 액션 영역이 항상 박스 하단에 위치 (sticky 의존 X).

                        【안전성 검토】
                        .esg-modal-box에 display:flex !important 박는 게 JS 토글
                        무력화 우려 있었음 (v77.55 교훈). 확인 결과: 모달 표시는
                        .on 클래스 토글이고 박스 자체 display는 JS가 안 건드림 → 안전.

                        【영향】
                        page-mypage.php (v77.58 블록 일부 교체 + flex 룰 추가)
                        모든 .esg-modal 컴포넌트 (7개 모달)
                        PC 영향 없음

  v77.58 (2026-05-13) - 모든 모달 모바일 fullscreen 일반화 (사용자 요청, 모든 페이지):

                        【배경 — 사용자 캡처 3장】
                        모바일에서 문의하기 모달이:
                        - 사진 1: 상단/좌우/하단에 빈 여백 (반투명 배경 보임), 등록
                                  버튼이 화면 밖
                        - 사진 2: 박스가 화면 중앙 작게 떠있음
                        - 사진 3: 등록 버튼이 박스 위에 떠있음
                        사용자: "다른 곳도 이런 모양은 전체가 이럼 모바일 버전에서
                                 이런 UI 가진 것 전부 수정해줘"

                        【원인】
                        v77.54에서 모달 fullscreen 처리할 때 ID 셀렉터 4개만 나열:
                          #esg-inquiry-modal, #esg-my-inquiry-view-modal,
                          #esg-review-modal, #esg-excuse-modal
                        그러나 .esg-modal 컴포넌트를 쓰는 모달은 총 7개:
                          위 4개 + esg-learnq-modal (학습 질문 작성)
                                + esg-cancel-modal (수강 취소 확인)
                                + esg-withdraw-modal (회원 탈퇴)
                        → 누락된 3개는 기본 .esg-modal { padding:20px;
                          align-items:center } 가 적용되어 박스가 화면 중앙
                          작은 사이즈로 표시.
                        + 결석 모달 인라인 style="width:560px;max-height:90vh;..."
                          가 일부 환경에서 ID 셀렉터의 specificity로 못 이긴 가능성.

                        【해결】
                        ID 셀렉터 나열 → .esg-modal 자체 셀렉터로 일반화.
                        page-mypage.php 모바일 미디어쿼리에 v77.58 블록 추가:
                          .esg-modal { padding:0; align-items:flex-start }
                          .esg-modal .esg-modal-box { position:fixed; inset:0;
                              width:100%; height:100%; border-radius:0; ... }
                          .esg-modal .esg-modal-hd { sticky 헤더 }
                          .esg-modal .esg-modal-x { 44×44 닫기 버튼 }
                          .esg-modal .esg-modal-body { padding 본문 }
                          .esg-modal .fg-action, .esg-modal-actions,
                          .esg-modal-footer { sticky 하단 액션 }
                        모두 !important로 인라인 style 무력화.

                        v77.54의 ID 셀렉터 블록은 그대로 유지 (deprecated이지만
                        제거 시 영향 매트릭스 검토 필요. v77.58이 더 일반적이라
                        실효성은 v77.58이 가짐).

                        【영향】
                        page-mypage.php (모바일 미디어쿼리 안에 ~80줄 추가)
                        모든 .esg-modal 컴포넌트 = 7개 모달 모바일에서 fullscreen
                        PC(>768px) 영향 없음
                        미래에 .esg-modal로 새 모달 추가해도 자동 적용

  v77.57 (2026-05-13) - 학습 카드 사진 2 디자인 정밀 맞추기 (v77.56 보강):

                        【배경】
                        v77.56에서 학습 카드 디자인 변경했으나 사용자가 사진 1·2
                        비교하며 5가지 잔여 차이 지적:
                        1) 강좌명 끝 → 화살표 표시 (사진 2에는 없음)
                        2) "아직 후기를 작성하지 않으셨습니다" 회색 글씨 (없어야 함)
                        3) "승인완료 이후 후기 작성 가능합니다" 회색 글씨 (없어야 함)
                        4) "⭐ 후기 작성" 버튼의 ⭐ 이모지 (없어야 함)
                        5) "📎 결석 사유서" 버튼의 📎 이모지 (없어야 함)
                        6) 결석 사유서 버튼이 오렌지로 보임 (노란 그라데이션 의도였음)

                        【원인 — 6번】
                        결석 버튼이 class="btn-review-new js-open-excuse" 로
                        btn-review-new 클래스 공유. v77.56의 CSS에서
                        .btn-review-new {background:오렌지 !important} 가 먹고,
                        .js-open-excuse에는 background가 명시 안 됐던 게 원인.

                        【해결】
                        page-mypage.php 변경:
                        - 라인 544: <span class="arr">→</span> 제거 (1곳)
                        - 라인 576, 591, 604: "⭐ 후기 작성" → "후기 작성" (모바일 카드 3곳)
                        - 라인 622: "📎 결석 사유서" → "결석 사유서"
                        - 라인 782, 787, 803: PC 카드 영역도 같이 (일관성)
                        - 모달 헤더(1843, 1882)·JS(3511) 이모지는 그대로 (제목 맥락)

                        v77.56 CSS 블록 끝에 v77.57 추가:
                        - #my-enroll .my-enroll-action .my-review-empty { display:none }
                        - .js-open-excuse 와 .btn-review-new.js-open-excuse 둘 다에
                          background:노란 그라데이션 !important 명시 (specificity 우선)

                        【영향】
                        page-mypage.php (HTML 7곳 + CSS ~14줄 추가)
                        v77.56 다른 작업(썸네일 등) 영향 없음
                        모바일 카드만 변화. PC 카드도 이모지/화살표는 같이 제거됐지만
                        그 외 PC 시각 변화 0 (모달, 다른 페이지 모두 그대로)

  v77.56 (2026-05-13) - 학습 목록 카드 UI를 사진 2 디자인으로 변경 (사용자 요청):

                        【배경】
                        v77.54에서 학습 카드 썸네일 64×64로 살짝 키웠지만 사용자가
                        사진 2를 첨부하며 "가로형 큰 포스터 썸네일 + 큰 액션 버튼"
                        디자인으로 변경 요청.

                        【해결 — page-mypage.php 2곳 변경】

                        1) HTML: 썸네일에 실제 포스터 이미지 표시
                          - course_id가 있으면 _course_poster 메타 → 대표이미지 →
                            이모지 순으로 fallback
                          - 포스터 있을 때 .has-poster 클래스 부여
                          - <img loading="lazy">로 모바일 성능 보호

                        2) CSS: v77.54의 my-enroll 블록을 v77.56으로 교체
                          - 썸네일 64×64 → 110×75 (가로형, 16:10 비율)
                          - .has-poster 클래스에서 그라데이션 배경 제거 + img cover
                          - 후기 작성: 오렌지 그라데이션 #f97316→#f59e0b (강화)
                          - 후기 수정: 흰 배경 + 오렌지 보더 (사진 2의 ✏️ 후기 수정)
                          - 결석 사유서: 노란 그라데이션 box-shadow 추가
                          - 비활성 버튼: 회색 톤
                          - 모든 액션 버튼 flex:1 1 0 으로 카드 폭 균등 분배

                        【영향】
                        page-mypage.php (HTML ~15줄 추가, CSS v77.54 블록을 v77.56로 교체)
                        v77.54의 다른 작업(1:1 문의, 알림 박스, 모달, 신청 내역)은
                        그대로 작동
                        PC(>768px) 영향 없음
                        포스터 이미지가 등록되지 않은 강좌는 기존처럼 이모지 + 그라데이션

  v77.55 (2026-05-13) - v77.54 부작용 픽스: 신청 내역 필터 버튼이 작동 안 함:

                        【배경】
                        v77.54 작업 5에서 신청 내역 카드를 3행으로 정리하려고
                        `#my-orders .my-order-card { display: flex !important;
                        flex-direction: column !important; }` 를 추가했음.

                        【원인】
                        필터 버튼(전체/입금대기/입금확인/수강확정/취소) 클릭 시 JS가
                        `c.style.display = 'none'` 으로 카드를 숨김. 그런데 위 CSS가
                        !important로 display:flex 박아놔서 인라인 style을 이김
                        → 필터가 작동해도 카드 안 숨겨짐.

                        【해결】
                        v77.54의 `display: flex !important` 룰 1개 (2줄) 삭제.
                        flex-direction:column 은 자식이 자연스럽게 위→아래 흐름이므로
                        애초에 불필요했음. 기본 display:block로 충분.
                        다른 v77.54 변경(헤더, 알림 박스, 모달, 학습 카드 등)은
                        영향 없이 그대로 유지.

                        【교훈】
                        JS의 인라인 style 토글이 있는 영역에는 !important + display 룰을
                        함부로 박으면 안 됨. 다음부터는 JS 토글 영역 사전 확인.

                        【영향】
                        page-mypage.php (4줄 삭제 + 주석 2줄 추가)
                        v77.54 다른 작업 모두 그대로 작동.

  v77.54 (2026-05-13) - PPT7 6장 슬라이드 통합 변경 (모바일 마이페이지/모달 정리):

                        【사용자 요청 — 6장 슬라이드 한 번에 처리】
                        1) 슬라이드 1: 나의 1:1 문의 헤더 우측에 ✏️ 작성 아이콘 추가
                                       + 답변완료 배지 우측 정렬
                        2) 슬라이드 2: 알림/마케팅 수신 박스가 무너져 라벨이 세로
                                       글자로 깨지던 문제 픽스
                        3) 슬라이드 3: 후기 작성 모달의 별점/내용 비율 확대
                        4) 슬라이드 4: 결석 사유서 모달 — 후기 모달과 같은 fullscreen
                                       패턴 적용 (sticky 헤더, sticky 액션 버튼)
                        5) 슬라이드 5: 신청 내역 카드 레이아웃 3행 명확화
                        6) 슬라이드 6: 학습 목록 카드 — 썸네일/액션 버튼 강조

                        【해결】
                        page-mypage.php 1곳에 HTML 1줄 추가(.my-mobile-back-action 버튼)
                        + 같은 파일 모바일 미디어쿼리 안에 v77.54 통합 블록 ~200줄 추가.
                        모든 셀렉터를 #my-qna, #my-profile, #esg-review-modal,
                        #esg-excuse-modal, #my-orders, #my-enroll 로 영역 한정해서
                        다른 영역 영향 0.

                        【주요 픽스 디테일】
                        - 답변완료 배지: .my-inq-title 을 flex로 만들어 .badge에
                          margin-left:auto 로 우측 끝 배치
                        - 알림 박스 깨짐: label에 display:flex / writing-mode:horizontal-tb
                          강제 (세로 글자 차단)
                        - 결석 모달: 후기 모달과 동일한 sticky 헤더/푸터로 통일
                        - 학습 카드 액션: btn-review-new / btn-review-edit / js-open-excuse
                          모두 flex:1 1 auto로 카드 폭 균등 분배

                        【영향】
                        page-mypage.php
                        PC(>768px) 영향 없음. 다른 페이지 영향 없음.

  v77.53 (2026-05-13) - 팝업 이미지가 길 때 위쪽이 잘리는 문제 (사용자 캡처):

                        【배경】
                        모바일에서 세로로 긴 이미지 팝업을 띄우면 이미지 상단이
                        잘려서 보임 (예: '캠페인' 텍스트가 '캠페이'로). 박스가
                        화면 중앙에 위치하고 max-height:85vh 인 상태에서 이미지가
                        그보다 크면 위/아래 모두 잘림 — 사용자는 위쪽 잘림으로 인지.

                        【원인 2가지 결합】
                        1) footer.php .esg-popup-box 정의 끝에 overflow:hidden 이
                           overflow-y:auto 보다 뒤에 와서 덮어씀 → 세로 스크롤 자체가
                           막혀 있었음. 사용자가 박스 안에서 위로 스크롤 불가능.
                        2) 이미지 자체에 max-height 제약 없음 → 박스보다 길면 잘림.

                        【해결】
                        .esg-popup-box:
                          - overflow:hidden 제거
                          - overflow-y:auto / overflow-x:hidden 로 분리
                            (세로 스크롤 허용, 가로 차단 — 둥근 모서리 보존)
                        .esg-popup-image-wrap img:
                          - max-height:80vh + object-fit:contain 추가
                            (이미지가 박스보다 크면 비율 유지하며 자동 축소)
                        @media (max-width:640px):
                          - .esg-popup-image-wrap img max-height:70vh (모바일은 더 보수적)

                        【결과】
                        - 짧은 이미지: 변화 없음
                        - 긴 이미지: 박스 안에서 비율 유지하며 축소되어 잘림 X
                        - 박스 콘텐츠가 박스보다 크면: 박스 내부 세로 스크롤 가능
                        - close X 버튼은 position:absolute라 스크롤해도 우측 상단 고정

                        【영향】
                        footer.php (2~3줄 수정)
                        style.css (changelog만)
                        PC 영향: PC에서도 동일하게 적용 (긴 이미지 잘림은 PC에서도 동일
                        잠재 문제였음). 새 기능이라기보다 버그 픽스.

  v77.52 (2026-05-13) - 수강신청 페이지: 클라이언트 필터 → 서버 사이드 필터 + 페이지네이션:

                        【배경】
                        v77.51에서 강사소개 페이지네이션 적용 시, 수강신청도 같이 하려
                        했으나 클라이언트 JS 필터(filterCourses)와 충돌 발견.
                        - 기존: 모든 카드를 DOM에 렌더 → JS가 보임/숨김 토글
                        - 페이지네이션 추가 시: 12개만 DOM에 있음 → 필터가 1페이지
                          내에서만 작동 → 부정확

                        【해결 — 옵션 A 선택 (사용자 결정)】
                        클라이언트 필터를 서버 사이드로 이동.

                        page-courses.php 변경:
                          - URL 파라미터 ?level= ?price= ?textbook= ?s= ?cpage= 처리
                          - get_posts로 가져온 결과에 array_filter로 level/price/textbook 적용
                          - 마감 정렬(v77.46) → 필터 → array_slice($all_courses, offset, 12)
                          - 카드 foreach가 $all_courses → $all_courses_paged 사용
                          - select에 selected, 검색 input에 value 표시 (현재 상태 반영)
                          - "X개 과정" 카운트도 $cpage_total 사용
                          - grid 끝 직후 esg_render_pagination_v2 호출

                        filterCourses() JS 함수 단순화:
                          - 기존: querySelectorAll로 .course-card 보임/숨김 + 카운트 갱신
                          - 새: URLSearchParams로 URL 재구성 후 location.href 변경
                          - 필터 변경 시 cpage 파라미터 자동 삭제 (1페이지로 리셋)
                          - 검색은 실시간 → 엔터키로 변경 (실시간은 새로고침 폭주)

                        【호환성 보존】
                        - ?s= (WP 검색 표준 파라미터) 그대로 사용
                        - 카테고리 탭 /courses/{slug}/ 경로 그대로
                        - esg_render_pagination_v2가 URL의 모든 쿼리 보존 + cpage만 갱신
                          → 페이지 이동해도 필터 유지

                        【영향】
                        page-courses.php (~50줄 추가, 필터 JS 함수 ~10줄로 축소)
                        functions.php / page-instructors.php 영향 없음
                        PC/모바일 공통

  v77.51 (2026-05-13) - 강사소개 페이지에 페이지네이션 추가 (사용자 요청, 모바일/PC 공통):

                        【배경】
                        강사/수강 카드가 페이지에 무한 누적되어 모바일에서 매우 길어짐.
                        사용자가 캡처와 함께 '‹ 1 2 ›' 형태 페이지네이션 요청.

                        【범위 — 이번 v77.51】
                        강사소개(page-instructors.php) 만 우선 적용.
                          페이지당 12명, ?cpage=N 쿼리로 페이지 이동.

                        【수강신청(page-courses.php)는 보류 — 다음 결정 필요】
                        수강신청 페이지는 클라이언트 사이드 JS 필터(filterCourses)가
                        document.querySelectorAll('.course-card')로 모든 카드를 잡고
                        보임/숨김 토글하는 구조. 페이지네이션으로 12개만 렌더하면
                        필터가 1페이지 카드에만 동작 → 부정확한 결과.

                        해결 옵션:
                          (a) 클라이언트 필터를 서버 사이드로 이동 (변경 범위 큼)
                          (b) 필터 활성 시 페이지네이션 무력화 (사용자 혼란)
                          (c) 그대로 클라이언트 필터 유지하고 페이지네이션도 적용
                              (필터-페이지네이션 충돌)
                        → 사용자 결정 필요. v77.52에서 결정 후 적용.

                        【해결 — v77.51 실제 구현】
                        functions.php에 esg_render_pagination_v2($total, $per_page, $cur_page)
                        헬퍼 신규 추가. 기존 esg_render_pagination()은 ?tab=&cpage= 형태
                        URL로 한정되어 범용성 부족 → 신규 함수는 현재 URL 쿼리 전부 보존
                        하고 cpage만 갱신. 시각 디자인은 기존과 100% 동일.

                        page-instructors.php:
                          - $instructors_all = get_posts(...) (전체 가져오기)
                          - array_slice로 현재 페이지 12명만 추출 → $instructors
                          - foreach 끝 직후 esg_render_pagination_v2 호출

                        【영향】
                        functions.php (헬퍼 1개 추가 ~80줄)
                        page-instructors.php (~15줄 변경)
                        page-community.php 영향 0 (기존 esg_render_pagination 유지)
                        PC/모바일 공통 적용

  v77.50 (2026-05-13) - 팝업 닫기 X 버튼이 타원으로 나오던 문제 (모바일):

                        【배경】
                        모바일 팝업의 닫기 X 버튼이 동그라미가 아니라 가로/세로 한쪽이
                        늘어난 타원으로 보임. CSS상 .esg-popup-close-x는 32×32 정사각형
                        + border-radius:50% 로 정의되어 있었음.

                        【원인】
                        style.css 모바일 미디어쿼리의 v68.71 글로벌 룰
                          button, .btn, ... { min-height: 40px !important; }
                        이 .esg-popup-close-x의 height:32px를 무시하고 40px로 확장.
                        결과 32×40 직사각형 + border-radius:50% = 타원.

                        【같은 패턴의 기존 픽스】
                        같은 미디어쿼리 내 v68.71/v77.30/v77.35 예외 목록 존재:
                          .hero-dot, .hero-ctrl-btn, .pop-nav, .pop-tab, .nc-nav,
                          .cc-heart, .esg-sb-prev/next/dot, .fm-search-go, .my-wish-heart
                        → 이 목록에 .esg-popup-close-x 한 줄만 추가하면 끝.
                        새 블록 추가 불필요, 같은 종류의 예외라 의미상 같은 자리.

                        【영향】
                        style.css (예외 목록에 셀렉터 1개 + 주석 1줄 추가)
                        footer.php 영향 없음 (close 버튼 자체 CSS는 그대로)
                        PC 영향 없음 (모바일 미디어쿼리 내부)

  v77.49 (2026-05-13) - 기업교육 문의 폼 select: native 외형 제거 + input과 정밀 통일 (모바일):

                        【배경】
                        v77.48에서 font-size를 .88rem로 통일했음에도 select가 input보다
                        시각적으로 작아 보임 (사용자 재보고). 원인은 native select의
                        OS-level 렌더링 차이 — font-size 같아도 line-height,
                        letter-spacing, font-weight가 브라우저·OS 기본값으로
                        다르게 렌더됨.

                        【해결 — A안 실행 (v77.48에서 미리 예고했던 다음 단계)】
                        select.inq-input에만 한정해서:
                          -webkit-appearance: none / -moz-appearance: none / appearance: none
                          → OS native dropdown 외형 제거
                          line-height: 1.5, letter-spacing: -.01em, font-weight: 400
                          → input과 동일 시각 톤 강제
                          background-image: SVG chevron data URI, position right .8rem center
                          → 커스텀 dropdown 화살표 (회색 #94a3b8)
                          padding-right: 2rem → 화살표 자리 확보
                          ::-ms-expand display:none → 구 IE/Edge fallback

                        【영향】
                        page-inquiry.php (인라인 style 블록의 모바일 미디어쿼리 안에
                          select.inq-input 전용 블록 약 20줄 추가)
                        v77.48 블록은 손대지 않음
                        PC(>760px) 영향 없음

                        【대안 검토】
                        이 픽스를 PC에도 확장하면 PC select 외형도 통일 가능. 다만
                        사용자가 모바일만 문제 제기 + Architect 룰 "합의된 스코프만"
                        준수로 모바일에만 적용. PC 미적용 사유는 안전성, 아니라
                        디자인 결정.

  v77.48 (2026-05-13) - 기업교육 문의 폼: select와 input 시각 크기 통일 (모바일):

                        【배경】
                        page-inquiry.php 폼에서 select(서비스 분류, 기업 유형, 임직원수)와
                        input(예상 교육인원, 기업명, 소속/이름/직급, 연락처) 의 글씨가
                        시각적으로 달라 보인다는 사용자 피드백. CSS 상으로는 둘 다
                        v68.78에서 .inq-input { font-size:.85rem } 로 통일되어 있지만,
                        브라우저가 native select와 input을 다르게 렌더링하면서 시각 차이
                        발생.

                        【해결】
                        사용자 표현 "두 글씨 크기의 중간으로 전체 수정" 그대로 적용:
                          .inq-input font-size .85rem → .88rem (모바일만)
                          padding .6rem/.75rem → .65rem/.8rem (글씨 키운 만큼)
                        두 요소가 같은 클래스라 한 곳만 바꿔도 select/input/textarea
                        모두 동기화됨. select native 렌더링 차이 자체는 그대로지만
                        둘 다 살짝 키우면 시각 차이가 덜 도드라짐.

                        【대안 검토】
                        select에 -webkit-appearance:none + 커스텀 화살표 추가하면
                        근본 픽스 가능하나 OS별 일관성/접근성 영향 큼 → 이번엔 미니멀
                        픽스만. 추후 필요 시 v77.49+에서 확장.

                        【영향】
                        page-inquiry.php (인라인 style 블록의 모바일 미디어쿼리 안 1곳)
                        style.css (changelog만)
                        PC(>760px) 영향 없음.

  v77.47 (2026-05-13) - 수강신청 페이지 필터 바 글씨 축소 (모바일만, 사용자 요청):

                        【배경】
                        page-courses.php 필터 영역의 select 3개(전체 난이도 / 전체 가격 /
                        교재 전체)와 검색 input이 인라인 스타일 font-size:.85rem로
                        모바일에선 크게 보였음.

                        【해결】
                        style.css 모바일 미디어쿼리에 v77.47 블록 추가:
                          - #fc-level, #fc-price, #fc-textbook { font-size:.72rem; padding:.32rem .55rem }
                          - #fc-q { font-size:.72rem }
                          - #fc-count { font-size:.72rem }
                        인라인 스타일 override를 위해 ID 셀렉터 + !important 사용.
                        PC(>768px) 영향 없음.

  v77.46 (2026-05-13) - 마감된 과정을 목록 뒤로 정렬 (사용자 요청, 모바일/PC 공통):

                        【배경】
                        모바일 캡처에서 신규 과정 목록 첫 카드가 이미 신청 마감된
                        과정(우측 하단 회색 '마감' 배지)이었음. 사용자가 마감 과정은
                        가장 뒤로 밀려나게 + 수강신청 페이지 카테고리 탭들도 동일 적용
                        요청. 모바일/PC 공통 적용 (콘텐츠 본질이라 디바이스별 분리는
                        오히려 혼란).

                        【해결】
                        functions.php에 헬퍼 함수 2개 신규 추가:
                          - esg_is_course_closed($post_id) → bool
                            ① _course_apply_end < 현재 시각
                            ② _course_capacity>0 AND 신청자 수(취소 제외) ≥ 정원
                            중 하나라도 해당하면 마감으로 판정.
                            enrollments 옵션은 static 변수로 캐시 → 매 호출마다
                            풀 카운트 안 함.
                          - esg_sort_courses_active_first($posts) → 안정 정렬:
                            활성 그룹(원래 순서 유지) → 마감 그룹(원래 순서 유지)

                        적용 위치 (2곳):
                          - index.php 신규 과정 (line 898 직후)
                          - page-courses.php 수강신청 (search merge 직후, line 96)
                        다른 곳은 영향 없음. 추후 인기/커스텀 등 확장 시
                        한 줄로 적용 가능하게 헬퍼 함수로 설계.

                        【영향】
                        functions.php (헬퍼 함수 2개, 약 70줄 추가)
                        index.php (3줄 추가)
                        page-courses.php (3줄 추가)
                        CSS 영향 없음. 시각적 변화는 데이터 순서 변경만.

  v77.45 (2026-05-13) - v77.44가 인기 과정에 안 먹는 진짜 원인 (DevTools 'flex' 라벨로 확정):

                        【발견 경위】
                        v77.44 적용 후 신규 과정은 정상이지만 인기 과정만 여전히 깨짐.
                        사용자 DevTools 캡처에서 <div class="...pop-sec-hd"> 옆에
                        'flex' 라벨 발견 → 현재 grid가 아니라 flex로 렌더링 중.

                        【원인】
                        v68.81의 `.pop-sec-hd.sec-hd-row { display: flex !important }`
                          (specificity 0,0,2,0)이
                        v77.35의 `.pop-sec-hd { display: grid !important }`
                          (specificity 0,0,1,0)를 이김.
                        둘 다 !important 라 specificity로 결정되는데
                        v68.81이 클래스 2개로 더 강함.
                        → v77.42/v77.44의 모든 grid-* 속성 전부 무시됨
                          (display: contents 만 살아있어 자식이 flex 부모 자식이 됨)

                        【왜 신규 과정만 정상이었나】
                        신규 과정 셀렉터 #new_courses .sec-hd-row (0,1,1,0) 는
                        ID specificity로 v68.81의 flex를 이미 이기고 있어서 grid로 렌더링.

                        【픽스】
                        인기 과정 셀렉터를 #popular_courses .pop-sec-hd 로 통일
                        (specificity 0,1,2,0) → ID + 클래스 2개로 v68.81의 flex를 깨끗이 이김.
                        v77.42/v77.44 블록은 그대로 보존, v77.45가 후순위 override.

                        【영향】
                        index.php (v77.44 블록 직후 v77.45 specificity 픽스 ~75줄 추가)
                        PC(>768px) 영향 없음.

  v77.44 (2026-05-13) - v77.42 후속 픽스: NEW/HOT 배지 늘어남 + 인기 과정 배지 위치 깨짐:

                        【버그 1】신규 과정 NEW 배지가 가로로 길게 늘어남
                          원인: v77.42에서 .sec-badge { grid-column: 1 / -1 }로
                                두 컬럼 모두 차지 → 가로 늘어남
                        【버그 2】인기 과정 HOT 배지가 제목 왼쪽 아래로 떨어짐
                          원인: v77.42 grid 전체에 align-items: end 걸어
                                배지가 행 내 셀 아래쪽 정렬 + 행 배치 명시 부재로
                                배지/제목/부제+더보기 자동 배치가 어긋남

                        【해결 — v77.42 위에 override 블록 추가】
                          1) grid-template-rows: auto auto auto 로 3행 명시
                          2) .sec-badge / .sec-title / .sec-desc 각각 grid-row 명시 (1/2/3)
                          3) .sec-badge: grid-column 1로 좁히고 justify-self:start,
                             width:max-content (콘텐츠 폭으로 고정, 늘어남 차단)
                          4) align-items: end 제거 → align-items: start
                          5) .sec-desc 와 더보기 버튼만 align-self: center 로
                             같은 baseline 유지

                        【영향】
                        index.php (v77.42 블록 직후 v77.44 픽스 블록 ~65줄 추가)
                        v77.42 자체는 손대지 않음 (changelog 컨벤션 보존)
                        PC(>768px) 영향 없음.

  v77.43 (2026-05-13) - 오시는 길 — 모바일 전용 지도 이미지 옵션 추가:

                        【배경】
                        모바일에서 좌표/iframe/이미지 모든 모드의 지도가 깨져 보임 (사용자 캡처).
                        세로 좁은 화면에 가로로 긴 지도 이미지/임베드가 안 어울림.
                        사용자 요청: 관리자에서 모바일 전용 이미지를 따로 업로드해서
                        모바일에서만 그 이미지가 나오게.

                        【해결】
                        기존 esg_get_page_content_options('location') 옵션 시스템에
                        map_mobile_image 키 한 개를 추가 (신규 시스템 X, 기존 패턴 재사용).
                          - functions.php defaults['location']에 'map_mobile_image'=>'' 추가
                          - 관리자 폼 fields['location']의 map_image 바로 다음에
                            type=image 항목 추가 (라벨: 📱 모바일 전용 지도 이미지)
                          - page-location.php에서 $map_mobile_image 변수 추가
                          - 지도 영역 위에 .esg-map-mobile-only 블록 신규 출력,
                            기존 지도 if 체인 전체를 .esg-map-pc-only wrapper로 감쌈
                          - CSS: PC에선 mobile-only 숨김 / 모바일(≤768px)에선 pc-only 숨김
                          - 모바일 이미지가 비어 있으면 wrapper 클래스도 미부여 →
                            기존 동작 100% 보존 (역호환)
                          - 모바일 이미지에도 map_link 적용 (탭하면 큰 지도 열림)

                        【영향】
                        functions.php (defaults 1줄 + fields 1항목 추가, 총 ~5줄)
                        page-location.php (모바일 이미지 출력 블록 ~25줄 + wrapper open/close)
                        PC(>768px) 영향 없음. 모바일 이미지 미설정 시 기존 동작 그대로.

  v77.42 (2026-05-13) - 인기/신규 과정 헤더 정리 + 탭 바 스크롤바 숨김 (모바일만):

                        【배경】
                        모바일 인기/신규 과정 섹션에서 사용자가 다음 요청:
                          ① ‹ › 화살표 버튼 안 보이게
                          ② 우측 상단 "더보기/전체 보기" 버튼을
                             부제(sec-desc) 라인 끝 오른쪽 정렬로 이동
                          ③ 인기 과정 탭 바(#pop-tabs)의 가로 스크롤바 안 보이게
                             (스크롤 동작 자체는 유지)

                        【해결】
                        index.php의 v77.35/v77.39 모바일 헤더 블록 끝에
                        v77.42 override 블록 추가 (기존 블록은 보존).
                          - .nc-ctrl .nc-nav, .pop-sec-hd .pop-mobile-nav → display:none
                          - .sec-hd-row를 grid 2열로, 좌측 wrapper를 display:contents로
                            해체해 sec-desc와 더보기 버튼이 같은 행에 배치되게 함
                            (v77.40 모바일 전체메뉴 픽스에 쓴 동일 트릭)
                          - .sec-desc: nowrap + overflow:hidden + ellipsis, font .76rem
                          - 더보기/전체 보기: grid-column:2 / justify-self:end
                          - #pop-tabs::-webkit-scrollbar { display:none } 등 3종 prefix

                        【영향】
                        index.php (모바일 미디어쿼리 내부에 약 90줄 추가)
                        style.css (상단 changelog만)
                        PC(>768px) 영향 없음.

  v77.41 (2026-05-13) - 커뮤니티 게시판 모바일 1줄 레이아웃 + 자료실 다운로드 콤팩트 버튼:

                        【배경】
                        모바일 커뮤니티(공지/FAQ/문의/자료실) 게시판이 v68.78~v77.38에서
                        2줄 카드형(제목 위, 메타 아래)이었음. 사용자 요청으로 한 줄로
                        압축 + 컬럼별 정렬(번호:좌, 제목:flex, 메타:중간, 날짜:우)로 변경.
                        PC(>768px)는 손대지 않음.

                        【해결】
                        style.css 라인 4080 부근에 ⑬-2 신규 블록 추가
                        (기존 v68.78~v77.38 블록은 그대로 보존, override로 덮어씀).
                          - tr { flex-wrap: nowrap }    → 줄바꿈 차단
                          - td { white-space:nowrap; overflow:hidden;
                                 text-overflow:ellipsis; min-width:0 }
                          - td:first-child = 번호 (flex:0 0, 좁게, 왼쪽)
                          - td:nth-child(2) = 제목 (flex:1 1 0, ellipsis 작동 핵심)
                          - td:last-child = 날짜 (margin-left:auto → 우측 끝)
                          - colspan tr은 :not(:has(td[colspan]))으로 예외 처리
                            (페이지네이션·빈 안내 행은 기존 동작 유지)

                        【자료실 특수 처리】
                        page-community.php 자료실 wrapper에 .is-pds-table 클래스 부여.
                        모바일에서 등록일(:nth-child(4)) 숨김 → "번호 / 제목 / 다운로드"
                        3컬럼만 노출. 다운로드 버튼은 font-size:.7rem, padding:.25rem .55rem
                        으로 축소해 1줄 높이 유지.

                        【영향 범위】
                        page-community.php (1줄 변경: 자료실 card에 클래스 1개 추가)
                        style.css (override 블록 약 80줄 추가, 기존 블록 보존)

  v68.94 (2026-05-07) - 신규과정 모바일 카드 빈 공간 — WordPress emoji 이미지 크기 제어 (진짜 진짜 진짜 원인):

                        【배경 — 사용자 F12 캡처 결정타】
                        v68.92에서 cc-rating의 실제 크기 측정 결과: 24.02 × 200
                        (폭 24px, 높이 200px). 별점 한 줄짜리가 200px 높이가
                        cc-bottom-row 전체를 200px로 만들어 카드 내부 빈 공간 원인.

                        cc-rating 자식 검사:
                          <span class="cc-rating-sep">
                            <img draggable="false" role="img" class="emoji" alt="💬"
                                 src="https://s.w.org/images/core/emoji/17.0.2/svg/1f4ac.svg">
                          </span>

                        【진짜 원인】
                        WordPress가 PHP 출력의 💬 이모지를 자동으로 <img class="emoji">
                        로 변환 (wp_staticize_emoji 필터). 이 SVG 이미지가 모바일에서
                        기본 크기 약 200px. cc-rating 안에 들어가서 cc-rating 높이를
                        200px로 만듦. cc-bottom-row가 그만큼 늘어나 카드 빈 공간 발생.

                        PC에서는 동일한 emoji img가 다른 CSS 영향으로 자연스럽게 작게
                        표시되어 문제 없었음 (WordPress 기본 emoji 스타일이 모바일에서만
                        무력화되는 듯).

                        【해결】
                        img.emoji 와 .emoji 에 1em 크기 + inline + vertical-align 강제.
                        전역 적용 (모든 페이지의 emoji 이미지에 영향).
                        WordPress 기본 동작과 동일한 inline 텍스트 크기.

                        【효과】
                        cc-rating 안의 💬 이미지가 한 줄 높이로 작아짐.
                        cc-rating = 한 줄 콘텐츠 크기로 정상화.
                        cc-bottom-row = 한 줄 높이.
                        카드 내부 빈 공간 사라짐.

                        【리뷰어 노트 — 8시간 디버깅 회고】
                        v68.78~v68.93 동안 cc-link/cc-body/cc-spacer/cc-thumb를 의심.
                        실제 진짜 원인은 WordPress 자동 emoji 변환이었음.
                        교훈: HTML 마크업의 모든 자식 요소(emoji img 같은 자동 삽입물)
                        도 검사해야 함. 사용자 F12 캡처에서 24×200 크기 측정한 것이
                        결정적 단서가 되었음.

  v68.93 (2026-05-07) - 신규과정 모바일 카드 — PHP cc-thumb inline style 제거 (세 번째 진짜 원인):

                        【배경】
                        v68.92 적용 후 사용자 F12 캡처에서 v68.91/v68.92의 JS 정리는
                        완벽 작동 확인됨 (cc-link, cc-body, cc-name 모두 inline style 없음).
                        그러나 cc-thumb에는 여전히 inline style이 박혀 있음:
                          <div class="cc-thumb"
                               style="background:none;padding:0;height:160px;
                                      overflow:hidden;position:relative">
                          <img style="width:100%;height:160px;object-fit:cover;display:block">

                        【원인】
                        functions.php line 13056~13057에서 esangedu_course_card 함수가
                        썸네일 마크업 생성 시 inline style로 height:160px 강제.
                        CSS의 aspect-ratio: 16/9 (모바일) 와 4/3 (PC) 가 무시됨.

                        【해결】
                        functions.php cc-thumb과 안의 img에서 inline style 제거.
                        CSS가 처리하도록 위임:
                          - 모바일: aspect-ratio: 16/9 (v68.90 블록)
                          - PC: aspect-ratio: 4/3 (line 2777)

                        level 뱃지의 position:absolute style은 보존 (실제 layout에 필요).

                        【효과】
                        cc-thumb이 CSS aspect-ratio로 처리되어 카드 폭에 비례한 자연스러운
                        높이. 모바일에서 카드 내부 layout이 v68.90 의도대로 작동.

  v68.92 (2026-05-07) - 신규과정 모바일 카드 — 두 번째 진짜 원인 발견 & 해결:

                        【배경 — v68.91 이후 사용자 캡처】
                        v68.91에서 .nc-* JS 강제 코드 삭제 후에도 모바일 카드에 빈 공간 잔존.
                        사용자 F12 캡처 분석:
                          <a class="cc-link" style="flex: 0 1 auto !important;">
                          <div class="cc-body" style="flex: 0 1 auto !important;">
                          <div class="cc-cat cc-cat-empty" data-mobile-hide="1"
                               style="display: none !important; height: 0px !important;...">
                          <div class="cc-spacer" data-mobile-hide="1"
                               style="display: none !important; height: 0px !important;...">

                        【두 번째 진짜 원인 — 영역 A】
                        assets/js/main.js line 281~297 (placeholder 강제 숨김):
                          - cc-summary-empty, cc-cat-empty, cc-meta-row-empty,
                            cc-badges-empty, cc-spacer 모두 display:none 강제
                          → 신규과정 카드의 placeholder들이 사라져 카드 높이가
                            콘텐츠 길이에 따라 다름 (단차 발생)
                          → cc-spacer가 사라져 별점·가격 위쪽 빈 공간 흡수 안 됨

                        line 299~304 (cc-link/cc-body flex 강제):
                          - cc-link, cc-link .cc-body 모두 flex: 0 1 auto 강제
                          → cc-link가 카드 가용공간 흡수 안 함
                          → 카드 stretch는 되지만 내부에 빈 공간 발생

                        【해결 — 신규과정 카드만 JS 영향 제외】
                        영역 A의 inline style 강제 코드를 .nc-slide 안에는 적용 안 함:
                          ① 각 셀렉터에 el.closest('.nc-slide') 체크 추가 → skip
                          ② 옛 inline style cleanup 코드 추가 (캐시 대비)

                        다른 페이지 카드(.cg 그리드, .cch 가로카드, 일반 .cc)는
                        영역 A 그대로 적용 → layout 변경 없음.

                        【효과】
                        v68.90 CSS의 .nc-track .nc-slide ... 규칙이 inline style에
                        가려지지 않고 정상 작동:
                          - cc-link flex: 1 1 auto → 카드 가용공간 흡수
                          - cc-body flex: 1 1 auto → cc-link 가용공간 흡수
                          - cc-spacer block + flex: 1 1 auto → 빈 공간 흡수
                          - cc-name/cc-summary min-height → 모든 카드 같은 행 수
                          - cc-meta-row-empty visibility:hidden + height 유지 → 자리 차지
                        결과: 모든 카드 같은 높이, 별점·가격 카드 하단 정확 위치,
                        빈 공간 사라짐.

                        【리뷰어 노트 — 7시간 디버깅 회고】
                        v68.78~v68.91 동안 CSS specificity만 봤음. JS가 inline style을
                        박는다는 사실을 사용자 F12 캡처 두 장 후에야 발견.
                        교훈: CSS만 보지 말고 JS도 같이 점검. setProperty('important')는
                        inline style로 박혀서 모든 CSS를 이김.

  v68.91 (2026-05-07) - 신규과정 모바일 카드 빈 공간 — JavaScript 인라인 강제 스타일 제거 (진짜 원인 해결):

                        【진단의 결정타 — 사용자 F12 캡처】
                        사용자가 모바일 viewport 상태에서 nc-track을 검사한 결과:
                          <div class="nc-track" style="align-items: flex-start !important;">
                          <div class="nc-slide" style="align-items: flex-start !important;
                                                       height: auto !important;
                                                       align-self: flex-start !important;">
                        각 .nc-slide와 .nc-track에 inline style이 박혀있음.
                        inline style은 CSS의 모든 !important를 이김 → CSS 작업이 안 먹힘.

                        【진짜 원인】
                        assets/js/main.js line 306~324 (v68.75 시점 추가):
                          if (isMobile) {
                            document.querySelectorAll('.nc-track').forEach(function(t){
                              t.style.setProperty('align-items', 'flex-start', 'important');
                            });
                            document.querySelectorAll('.nc-slide').forEach(function(s){
                              s.style.setProperty('align-items', 'flex-start', 'important');
                              s.style.setProperty('height', 'auto', 'important');
                              s.style.setProperty('align-self', 'flex-start', 'important');
                            });
                            document.querySelectorAll('.nc-slide .cc').forEach(function(c){
                              c.style.setProperty('height', 'auto', 'important');
                              c.style.setProperty('min-height', '0', 'important');
                              c.style.setProperty('align-self', 'flex-start', 'important');
                            });
                            document.querySelectorAll('.nc-slide .cc-link').forEach(function(l){
                              l.style.setProperty('height', 'auto', 'important');
                              l.style.setProperty('min-height', '0', 'important');
                            });
                          }

                        v68.75 시점 주석에 "CSS specificity 문제 우회: setProperty
                        ('important')는 인라인 style로 박힘 (가장 강력)"이라고 명시되어
                        있음 — 의도적으로 inline style을 박은 것이 6시간 후 모든 빈 공간
                        문제의 진짜 원인이었음.

                        【해결】
                        assets/js/main.js의 위 코드 블록(line 306~324) 통째 삭제.
                        대응되는 PC cleanup 블록(.nc-track 등 inline style 제거)도 dead
                        code 되어 같이 삭제.

                        영역 A (line 300~304: .cg/.cc-link/.cc-body/.cc-name/.cc-summary
                        모바일 inline style)는 다른 페이지 카드 layout에 필요할 수 있어
                        보존. 신규과정만 정확히 영향받도록 최소 변경.

                        【효과】
                        v68.90의 .nc-track .nc-slide ... CSS가 더 이상 inline style에
                        가려지지 않고 정상 작동.
                          - 카드 stretch (align-items: stretch) 정상 작동
                          - cc-link/cc-body/cc-spacer flex:1 메커니즘 작동
                          - 별점·가격이 카드 하단에 정확히 위치
                          - 모든 카드 같은 높이로 통일
                          - 빈 공간 사라짐

                        【리뷰어 노트】
                        v68.78~v68.90 동안 CSS만 보면서 specificity 끌어올리려 했음.
                        진짜 범인은 JavaScript inline style이었음. CSS만 검토하지 말고
                        JS도 같이 봐야 한다는 교훈. 사용자 F12 캡처 한 장에 진짜 원인이
                        한눈에 보였음.

  v68.90 (2026-05-07) - 신규과정 모바일 카드 — 사용자 요구 정확히 반영 + 코드 단순화:

                        【사용자 요구 정확히 정리】
                        1) 카드 크기 완전 고정 — 모든 카드 같은 폭/높이
                        2) 모든 요소 위치 고정 — 강사·신청기간·태그 자리 고정
                        3) 데이터 없는 행: 행 자체가 사라지는 게 아니라 그 자리만
                          빈 칸으로 (placeholder, visibility:hidden)
                        4) 별점·후기는 좌측, 가격은 우측 (양 끝, space-between)
                          ← v68.85~v68.89에서 잘못 우측 한 줄로 만들었음
                        5) 콘텐츠 길이가 카드 크기에 영향 X — 제목 길어도 카드 그대로
                          (제목 2줄 ellipsis로 잘림)

                        【해결】
                        v68.85 블록(354줄) 통째 삭제 → 깨끗한 v68.90 블록으로 재작성.
                        PC 코드는 절대 안 건드림 (모든 규칙이 @media max-width:768px 안).
                        모바일에서 PC 메커니즘 그대로 복원 + 다음 추가:

                        ① 카드 폭 고정: flex 0 0 82% + min/max/width 모두 82%
                          + overflow:hidden (콘텐츠 폭 넘치면 잘림)
                        ② 카드 높이 통일: align-items:stretch + 모든 placeholder가
                          자리 차지 → 모든 카드의 콘텐츠 행 수 동일 → 높이 자동 통일
                        ③ placeholder 자리 유지: display:block + visibility:hidden
                          + 명시적 height/min-height
                          글로벌 [data-mobile-hide=1] { display:none } 규칙을
                          attribute selector 함께 사용해 specificity로 무력화:
                          .cc-cat-empty[data-mobile-hide="1"], .cc-cat-empty { ... }
                        ④ cc-bottom-row: justify-content:space-between
                          → 별점·후기 좌측, 가격 우측 (사용자 요구 ④ 정확)
                        ⑤ 제목/부제 ellipsis + min-height 강제 → 행 수 통일

                        【삭제된 코드】
                        v68.85 블록(line 3710~4063, 354줄) 전체 삭제.
                        PC 기본 .cc-bottom-row { display: contents } 도 동일하게
                        v68.90 블록 안에서 재정의 (PC 영향 없도록 미디어쿼리 밖으로).

                        【리뷰어 노트】
                        v68.78~v68.89 동안 사용자 요구를 잘못 해석:
                          - "한 줄 우측 정렬" ← 잘못 (사용자 요구는 양 끝 분리)
                          - "콘텐츠 크기 카드" ← 잘못 (사용자 요구는 고정 크기)
                          - "placeholder 숨김" ← 잘못 (사용자 요구는 자리 유지)
                        사용자가 명시적으로 "위치 고정", "강사 안 적으면 빈 칸으로 비워둠"
                        이라고 표현해줘서 v68.90에서 정확히 짚음.

  v68.89 (2026-05-07) - 신규과정 모바일 카드 빈 공간 — 진짜 원인 발견 & 해결:

                        【진단의 결정타】
                        사용자가 PC 브라우저로 같은 페이지를 본 결과: PC에서는 카드가
                        완벽하게 표시됨 (별점·가격·찜·수강신청이 카드 하단에 정확히 위치,
                        빈 공간 없음). 즉 모바일과 PC의 차이를 만드는 규칙이 범인.

                        【원인 — 모바일 미디어쿼리의 PC 메커니즘 무력화】
                        PC가 잘 작동하는 이유는 본 정의의 다음 4가지 메커니즘 덕분:
                          .nc-slide .cc { height: 100% } (line 2930)
                          .cc-link { flex: 1 } (line 2562)
                          .cc-link .cc-body { flex: 1 } (line 2563)
                          .cc-spacer { flex: 1 1 auto } (line 2598)
                        이 4개가 협력해서: 카드 stretch + cc-link/cc-body가 가용공간 흡수
                        + cc-spacer가 짧은 콘텐츠의 빈 공간 흡수 → 별점·가격·찜·수강신청
                        이 카드 하단에 정확히 위치.

                        그런데 v68.69에서 추가한 모바일 무력화 규칙(line 3302~3304):
                          .cc-link { flex: 0 1 auto !important }
                          .cc-link .cc-body { flex: 0 1 auto !important }
                          .cc-spacer { display: none !important }
                        이 4개 메커니즘을 모두 끊음. + v68.85에서도 cc-link/cc-body/cc-spacer
                        를 콘텐츠 크기로 강제. → 모바일에서 카드 stretch는 일어나지만
                        내부에서 가용공간 흡수가 일어나지 않아 빈 공간 발생.

                        【해결 — 모바일에서 PC 메커니즘 복원】
                        v68.85 블록을 수정해서 .nc-slide 영역만 PC와 동일하게 작동:
                        ① .nc-track align-items: flex-start → stretch (PC default)
                        ② .nc-slide align-self: flex-start → stretch
                        ③ .cc height: auto → 100% (PC와 동일)
                        ④ .cc-link flex: 0 0 auto → 1 1 auto (PC default 복원)
                        ⑤ .cc-link .cc-body flex: 0 0 auto → 1 1 auto (PC default 복원)
                        ⑥ .cc-spacer display: none → block + flex: 1 1 auto + visibility: hidden
                          (가용공간 흡수, 보이지 않게)

                        line 3302~3304는 다른 카드(.cg .cc 그리드 카드)에는 필요할 수
                        있어서 건들지 않고, .nc-track .nc-slide ... specificity로
                        이 영역만 PC 메커니즘 강제.

                        cc-spacer가 [data-mobile-hide="1"] 속성을 가지고 있어서 글로벌
                        [data-mobile-hide="1"] { display: none } 규칙이 잡으므로,
                        cc-spacer 활성화 셀렉터에 attribute selector 같이 사용해
                        specificity 일치시킴.

                        【cc-bottom-row wrapper는 그대로 유지】
                        functions.php의 wrapper는 별점·가격 한 줄 우측 정렬에 필수.
                        PC에서 잘 작동 중 (display: contents). 모바일도 wrapper 활성화.

                        【리뷰어 노트】
                        v68.78~v68.88 동안 빈 공간 원인을 stretch 또는 카드 크기로
                        오해했음. 진짜 원인은 카드 stretch + 내부 메커니즘(flex:1) 조합
                        의 일부만 사용해서 깨진 것이었음. PC가 잘 되는 부분을 보면
                        진단이 빨랐을 텐데 6시간 헤맸음. 사용자에게 정중히 사과.

  v68.88 (2026-05-07) - v68.87 변경 되돌림 — v68.85 원상태로 복귀:

                        【배경】
                        v68.87에서 카드 stretch + 가용공간 흡수로 변경했으나, 사용자가
                        보낸 목표 사진은 정반대 — 카드를 콘텐츠 크기로 두고 별점·가격을
                        태그 바로 아래에, 찜·수강신청을 별점·가격 바로 아래에 두어
                        빈 공간 자체가 없게.

                        【수정 — v68.87 변경 3개 되돌림】
                        ① .cc-link: flex: 1 1 auto → flex: 0 0 auto (콘텐츠 크기)
                        ② .cc-body: flex: 1 1 auto → flex: 0 0 auto (콘텐츠 크기)
                        ③ .cc-bottom-row: margin auto 0 0 → margin .25rem 0 0
                          (태그 바로 아래)

                        【결과 — v68.85 원상태】
                        카드 = 콘텐츠 크기 (cc, cc-link, cc-body 모두 콘텐츠 크기)
                        cc 안의 자식들이 위에서 아래로 자연스럽게 쌓임:
                          썸네일 → 카테고리 → 제목 → 부제 → 강사·신청기간 → 태그
                          → 별점·가격 → 찜+수강신청 (cc-body-action)
                        빈 공간 없음. 카드별 높이는 콘텐츠에 따라 다름 (영화 < Next-Gen).

                        【사과】
                        v68.78~v68.87 동안 사용자 신규과정 카드 모양에 대한 진짜 목표를
                        파악 못해 6시간 헤맸음. 진짜 목표는 단순히 "콘텐츠 길이대로 카드,
                        빈 공간 없이"였음. v68.85 원상태가 이미 그것이었으나, 사용자가
                        layout 설정을 horizontal로 두고 있어 .cc 카드 CSS가 안 보였고,
                        피드백 사진을 잘못 해석해 v68.87에서 stretch + 흡수로 갔다가
                        v68.88에서 원복.

  v68.87 (2026-05-07) - 신규과정 모바일 카드 미세조정 (사용자 피드백):

                        【배경】
                        v68.86에서 누적 패치 정리 + 사용자가 워드프레스 관리자에서
                        신규과정 카드 레이아웃을 vertical로 변경 → v68.85의 .cc 카드
                        CSS가 정상 적용됨. 카드 모양이 사용자가 원하는 형태(썸네일 위 +
                        강사/신청기간 + 별점·가격 + 찜+수강신청 버튼)로 표시 ✓

                        【남은 문제】
                        카드 높이가 stretch로 통일되는데, 짧은 콘텐츠 카드(영화 감상문 등)는
                        별점·가격 줄과 찜+수강신청 버튼 사이에 큰 빈 공간이 생김.

                        【원인】
                        v68.85에서 .cc-link / .cc-body 모두 flex: 0 0 auto로 두어 콘텐츠
                        크기로만 차지. 카드 자체는 stretch이고 cc-link는 콘텐츠 크기라
                        cc-link 끝 ~ 카드 하단(찜+수강신청 시작) 사이에 빈 영역 발생.

                        【수정 — v68.85 블록 직접 편집】
                        ① .cc-link { flex: 1 1 auto } — 가용공간 흡수
                          → 찜+수강신청 버튼이 카드 하단에 붙음
                        ② .cc-link .cc-body { flex: 1 1 auto } — cc-link 안에서 가용공간 채움
                          → 빈 영역 cc-body 안으로 흡수
                        ③ .cc-bottom-row { margin: auto 0 0 } — flex column에서 top auto로
                          별점·가격을 cc-body 하단으로 밀어냄
                          → 별점·가격이 cc-body의 가장 아래, 그 바로 위에 태그·메타,
                            그 위 빈 공간(짧은 카드의 경우)

                        【리뷰어 노트】
                        v68.85에서 flex: 0 0 auto로 둔 게 사용자 피드백 "큰 틀 고정"의
                        다른 해석이었음. 진짜 의미는 "카드 폭/높이 통일하되 빈 공간을
                        깔끔하게 흡수"였던 것으로 재해석. 이번에 flex: 1 1 auto로 바꾸면서
                        카드 stretch에 자연스럽게 적응하도록 수정.

                        v68.85 블록을 직접 수정함 — 새 블록 추가하지 않음. v68.86 정리
                        효과 유지.

  v68.86 (2026-05-07) - 누적 패치 5개 삭제 — 신규과정 모바일 카드 코드베이스 정리:

                        【배경】
                        v68.75/78(첫 블록)/79/80/82/84 — 신규과정(.nc-slide) 모바일 카드에
                        대한 6개 누적 블록이 서로 partial conflict를 일으켜 카드별로 다른
                        모습이 나타났음. 사용자 5장 사진에서 카드 폭 들쭉날쭉, 별점·가격
                        가운데 정렬 등 일관성 없는 결과.

                        v68.85 단일화 블록 추가 후, 이번에 누적 패치들을 모두 통째로 삭제.

                        【삭제된 블록】 (style.css에서 813줄 제거)
                        - v68.75 — 신규과정 슬라이더 카드 빈 영역 최종 수정
                        - v68.78 (첫 번째) — PPT9 모바일 카드 빈 영역 해결 S1/S2/S15
                        - v68.79 — S15 카드 테두리 깨짐 해결
                        - v68.80 — 신규과정 카드 세로 레이아웃 복원
                        - v68.82 — 신규과정 카드 폭 균일화 + 빈 영역 제거
                        - v68.84 — 신규과정 카드 크기 고정 + 별점·가격 한 줄 우측 정렬

                        【보존된 블록】
                        - v68.78 (두 번째) — 인기과정 .cch 가로카드 별점/후기 정렬
                          (이건 인기과정 작업이므로 절대 보존)
                        - v68.85 — 신규과정 단일화 블록 (현재 유일한 신규과정 모바일 정의)
                        - functions.php의 cc-bottom-row wrapper (v68.84 DOM 변경) 유지

                        【효과】
                        신규과정 모바일 카드 모든 정의가 v68.85 한 곳에 명확히 통합됨.
                        앞으로 신규과정 카드 변경 시 한 블록만 수정하면 됨 — 누적 충돌 없음.
                        cascade specificity 추측 디버깅 더 이상 필요 없음.

                        【리뷰어 노트】
                        v68.75~v68.84 동안 같은 영역을 패치로 덮어쓰며 진행한 것이 문제.
                        근본 원인을 잡기 어렵게 만들었음. 한 카드에 대해 여러 패치가 누적되면
                        반드시 정리 — "기능 추가 = 패치 1개"가 아니라 "기능 변경 = 기존 정의
                        교체"여야 함.

  v68.85 (2026-05-07) - 신규과정 모바일 카드 — 누적 규칙 정리 & 단일화 (긴급):

                        【사용자 피드백 (5장 사진)】
                        ① 카드 폭이 들쭉날쭉 — 영화 카드 좁고, Next-Gen 카드 화면 가득
                        ② 별점·가격이 일부 카드에서 가운데 정렬됨 (사진 3·4·5)
                        ③ 빈 영역 큼 (사진 2 5,500원, 사진 5 AI 설치하기)

                        【진단 — 누적 규칙 충돌】
                        v68.75/v68.78/v68.80/v68.82/v68.84 → 6개 블록이 .nc-slide
                        모바일 규칙 누적 정의. 각 블록의 결정이 부분적으로 상충:
                          - v68.78: cc-link grid 2열
                          - v68.80: cc-link flex column 복원
                          - v68.82: align-items flex-start, rating·price absolute
                          - v68.84: align-items stretch 복원, cc-bottom-row wrapper
                        결과: 어느 규칙이 이기는지 카드별로 미묘하게 다름.
                        v68.84의 cc-bottom-row { display: flex; justify-content: flex-end }
                        가 일부 카드에서 안 먹힌 듯 (specificity 또는 cascade 순서 추정).

                        【해결 — 단일 블록으로 정리】
                        .nc-track .nc-slide ... (specificity 0,0,2+) 셀렉터로 모든 이전
                        규칙 압도. 카드 구조를 한 곳에서 명확히 재정의:
                          1) 폭: flex 0 0 82%, max-width 82%, width 82% 삼중 강제
                          2) 높이: 콘텐츠 크기 (stretch X — 빈 영역 자체 안 만듦)
                          3) 썸네일: 풀폭 16:9
                          4) cc-body: flex column, gap .35rem, padding 1rem 1rem .85rem
                            text-align: left + 자식 모두 text-align: left 강제
                          5) cc-bottom-row: flex row, justify-content: flex-end,
                            text-align: right 보강 → 별점·가격 우측 한 줄
                          6) cc-spacer: display:none (stretch 안 하니 무의미)
                          7) 빈 placeholder: 일괄 display:none

                        【중요 결정 — v68.84 일부 되돌림】
                        v68.84의 stretch 복원 + cc-spacer 부활 결정을 v68.85에서 다시
                        뒤집음. 사용자 피드백상 "큰 틀 고정"의 실제 의미는
                        "빈 영역 없게 깔끔하게" 였던 것으로 재해석. 콘텐츠 크기 카드
                        + 빈 영역 없음이 가장 깔끔.
                        cc-bottom-row wrapper(functions.php DOM 변경)는 유지 — 별점·
                        가격 한 줄 우측 정렬에 필수.

  v68.84 (2026-05-07) - 신규과정 모바일: 카드 크기 고정 + 별점·가격 한 줄 우측 정렬:

                        【사용자 피드백】
                        ① 카드 가로/세로가 콘텐츠 길이에 따라 변동 → "큰 틀은 고정"
                        ② 별점·후기가 좌하단, 가격이 우하단으로 멀리 떨어짐
                          → "별점은 가격 바로 왼쪽"
                        ③ 강사 등 콘텐츠 없는 행은 보이지 않게

                        【원인】
                        ① v68.82에서 .nc-track align-items:flex-start로 stretch 차단
                          → 카드 높이가 콘텐츠 길이대로 변동.
                        ② v68.82에서 cc-rating(left:1rem)·cc-price-big(right:1rem)을
                          각각 absolute로 분리 배치 → 좌우 끝에 떨어짐.
                          DOM 상 두 요소는 형제(sibling)라 wrapper 없이 한 줄 우측
                          정렬이 어려움.

                        【해결】
                        A) 카드 크기 고정 — stretch 복원
                          .nc-track align-items: stretch
                          .nc-slide align-self: stretch
                          .cc height:100%, .cc-link flex:1 1 auto, .cc-body flex:1 1 auto
                          .cc-spacer flex:1 1 auto + visibility:hidden
                          → 모든 카드 같은 높이, 짧은 콘텐츠는 spacer가 자리채움,
                             별점·가격은 카드 하단으로 밀려남
                        B) 별점·가격 한 줄 우측 정렬 — wrapper 추가 (functions.php 변경)
                          esangedu_course_card 세로형 카드의 cc-rating + cc-price-big
                          을 <div class='cc-bottom-row'> wrapper로 감쌈.
                          PC: .cc-bottom-row { display: contents } — wrapper가 layout에
                              영향 없음 (자식들이 부모의 직계 자식처럼 동작) → 기존 디자인 보존.
                          모바일: .cc-bottom-row { display: flex; justify-content: flex-end;
                                  align-items: center; gap: .6rem }
                                  → 별점이 가격 바로 왼쪽, 둘 다 우측 끝.
                          v68.82의 absolute 처리는 모두 해제 (position: static 강제).
                        C) 빈 placeholder는 v68.78~v68.82에서 처리한 대로 유지
                          (cc-meta-row-empty, cc-summary-empty 등 display:none).
                          단 cc-spacer는 다시 활성화했으므로 :not(.cc-spacer)로 제외.

                        【중요 — DOM 변경】
                        functions.php esangedu_course_card 세로형 카드에 cc-bottom-row
                        wrapper 추가. PC에 영향 없도록 display:contents 사용.
                        가로형 카드(.cch)는 변경 없음.

  v68.83 (2026-05-07) - 인기과정 모바일 미세조정 (사용자 피드백):

                        【사용자 피드백】
                        ① 헤더의 ‹ ›버튼이 부제 라인과 겹쳐 보임
                          → "전체보기와 같은라인 위치만 밑으로 내림"
                        ② 카드 안 카테고리/제목/신청기간 사이 세로 간격이 너무 큼
                          → "간격 줄이기"

                        【원인】
                        ① v68.81에서 align-items:center로 두어 ‹ ›가 좌측 컬럼의
                          수직 중앙 = 부제 라인쯤에 위치. 사용자는 가장 아래 행
                          (전체 보기 버튼 행)에 같이 두기를 원함.
                        ② .cch-body 본 정의(line ~2521)의 justify-content:space-between
                          이 자식들(meta-top/title/info-block/bottom)을 위·아래로 끝까지
                          분산시킴. 모바일에서 이 정의가 그대로 상속되어 큰 빈 공간 발생.

                        【해결】
                        ① align-items: center → flex-end
                          ‹ ›버튼이 좌측 컬럼 마지막 자식(.pop-mobile-allbtn = "전체 보기"
                          버튼)의 baseline에 정렬됨 → 같은 행에 보임
                        ② .pop-panel .pop-cards .cch-body 에서:
                          - justify-content: flex-start !important (space-between 차단)
                          - gap: .3rem 으로 자식 간 작은 간격
                          - padding: .6rem .75rem (default보다 약간만)
                          그리고 자식들의 margin/마진 모두 0으로 통일 → cch-body gap이
                          유일한 간격 컨트롤. 카드 높이 압축됨.

  v68.82 (2026-05-07) - 신규과정 모바일: 카드 폭 균일화 + 빈 영역 제거 + 별점·후기 정렬:

                        【사용자 피드백】
                        ① 카드 폭이 과정명 길이에 따라 들쭉날쭉
                          - 짧은 제목("유행어") → 좁고, 긴 제목("Next-Gen") → 넓음
                          - 캐러셀에서 카드 폭 불균일 → UX 저하
                        ② 보라색 영역(별점/후기와 가격 사이) 빈 공간 아직 남음
                          - v68.80에서 cc-spacer 숨겼지만 빈 공간 유지됨
                        ③ 별점·후기 정렬 미세 어긋남 (주황선 표시)

                        【원인】
                        ① .nc-slide flex-basis가 브레이크포인트마다 다름:
                            line 2679 @720px → 50%, line 2680 @480px → 82%
                          폰 폭에 따라 다른 값 적용됨. + min-width:auto로 콘텐츠
                          긴 카드는 basis 이상으로 늘어남.
                        ② .nc-track align-items:stretch (default) +
                          본 정의 .cc {height:100%} + .cc-link/.cc-body {flex:1}
                          → 가장 긴 카드 높이로 다른 카드 stretch + flex:1로
                            콘텐츠 영역이 가용공간 채워 빈 공간 발생.
                          v68.78에서 일부 height:auto 처리했지만 cc-link/cc-body
                          flex:1은 그대로였음.
                        ③ 별·숫자·이모지 line-height/baseline 차이.

                        【해결 — v68.82 블록 추가 (style.css 끝)】
                        A) 카드 폭 균일화:
                          ▶ .nc-slide { flex: 0 0 82% !important; max-width: 82% }
                            768px 이하 모두 82%로 통일 (브레이크포인트별 차이 차단)
                          ▶ .nc-slide, .cc, .cc-link, .cc-body 모두 min-width: 0
                          ▶ 카드 내 텍스트에 word-break: keep-all + overflow-wrap: anywhere
                        B) 빈 영역 제거:
                          ▶ .nc-track { align-items: flex-start } — stretch 차단
                          ▶ .nc-slide .cc { height: auto; align-self: flex-start }
                          ▶ .nc-slide .cc-link { flex: 0 0 auto } — flex:1 무효화
                          ▶ .nc-slide .cc-link .cc-body { flex: 0 0 auto } — flex:1 무효화
                          ▶ .nc-slide .cc-body { padding-bottom: 2.4rem } — 별점·가격
                            absolute 영역 확보 후 padding 끝
                          ▶ .cc-rating, .cc-price-big 둘 다 absolute로 같은 줄에 배치
                            (cc-rating left:1rem bottom:.85rem,
                             cc-price-big right:1rem bottom:.85rem)
                            → 별점(좌) + 가격(우) 정확히 같은 줄
                        C) 별점·후기 baseline 정렬:
                          ▶ cc-rating display:inline-flex; align-items:center
                          ▶ 자식들(star/num/sep/count) line-height:1, vertical-align:middle

                        【중요 변경 사항】
                        v68.80에서는 cc-rating은 in-flow, cc-price-big만 absolute였음.
                        v68.82에서 cc-rating도 absolute로 변경 → 두 요소 bottom 위치를
                        정확히 일치시킴 (.85rem). 폰트 크기 차이가 있어도 같은 줄로 보임.

  v68.81 (2026-05-07) - 인기과정 모바일 헤더/카드 — v68.79 셀렉터 오류 수정 + 헤더 wrap 차단:

                        【배경】
                        v68.79 작업 후 사용자 1번 이미지에서:
                          ① 헤더의 ‹ › 네비 버튼이 우측이 아니라 좌측 컬럼 아래에 쌓여있었음
                          ② 카드 과정명이 1줄로 압축되지 않고 2~3줄로 길게 표시되었음

                        【원인 분석】
                        ① 헤더: 글로벌 .sec-hd-row 규칙에 flex-wrap:wrap 가 있어서,
                                .pop-sec-hd-left가 폭을 다 차지하면 .pop-mobile-nav가
                                다음 줄로 줄바꿈됨. v68.79에서 flex-direction만 row로
                                강제했지 flex-wrap을 nowrap으로 강제하지 않았음.
                        ② 카드: v68.79에서 사용한 셀렉터(.cch-name, .cch-period,
                                .cch-stats, .cch-meta)가 모두 잘못된 이름. 실제 마크업은
                                .cch-title, .cch-info-row, .cch-bottom 임. 잘못된 셀렉터는
                                dead code였고 default .cch-title { line-clamp:2; min-height:3.92rem }
                                가 그대로 적용되고 있었음.

                        【해결 — v68.81】
                        ① 헤더: .pop-sec-hd.sec-hd-row 에 flex-wrap:nowrap !important 추가
                                + align-items:center 로 ‹ ›가 부제 높이에 위치 (2번 이미지와 일치)
                                + .pop-mobile-nav margin-top 제거 (align-items:center로 대체)
                        ② 카드: 잘못된 셀렉터 모두 올바른 셀렉터로 교체:
                                  .cch-name → .cch-title
                                  .cch-period/.cch-date → .cch-info-row (+ .cch-info-block, .cch-info-label)
                                  .cch-stats/.cch-meta → .cch-bottom
                                  (.cch-rating, .cch-tags 는 이미 정확했음)
                                .cch-title 에 line-clamp:1 + ellipsis + min-height:0 적용
                                  → default min-height:3.92rem (2.5줄 분량) 해제
                                .cch-info-row 폰트 .78rem, 라벨 min-width 5rem→3.5rem
                                .cch-bottom margin/padding 최소화로 카드 압축

                        【교훈 / 리뷰어 노트】
                        v68.79는 셀렉터를 검증하지 않고 작성한 명백한 오류임.
                        앞으로 카드 압축 작업 시 functions.php의 실제 마크업
                        (esangedu_course_card 함수)을 먼저 확인 후 셀렉터 작성할 것.

  v68.80 (2026-05-07) - 신규과정 카드 세로 레이아웃 복원 (사용자 요청):

                        【배경】
                        v68.78에서 신규과정 모바일 카드를 가로 레이아웃(110px 썸네일 좌 + 텍스트 우)으로
                        변경했으나, 사용자가 다시 세로 레이아웃(썸네일 풀폭 위 + 콘텐츠 아래)으로
                        되돌리길 요청. 사용자 제공 2번 이미지가 목표 모양임.

                        【변경 내용 — style.css에 v68.80 블록 추가】
                        ① cc-link을 grid 2열 → flex column으로 되돌림
                          ▶ display:flex, flex-direction:column
                          ▶ padding:0 (썸네일이 카드 가장자리까지 닿게)
                        ② 썸네일 — 풀폭 16:9, 상단 모서리만 둥글게 (14px 14px 0 0)
                        ③ cc-body — 풀폭, padding 1rem .85rem, position:relative (가격 absolute 배치용)
                        ④ 과정명 2줄 허용 (이전 1줄 강제 해제)
                        ⑤ 부제(.cc-summary) 다시 노출
                          ▶ v68.78 display:none 해제, 2줄 ellipsis로 표시
                          ▶ 단, .cc-summary-empty(빈 placeholder)는 계속 숨김
                        ⑥ 강사/신청기간 두 행 모두 노출
                          ▶ cc-meta-row를 grid(60px 라벨 + 1fr 값)로
                          ▶ dt 라벨 다시 노출 (v68.78 display:none 해제)
                          ▶ "신청 " prefix 제거 (라벨이 있으므로 중복)
                          ▶ cc-meta-row-empty(빈 placeholder)는 계속 숨김
                        ⑦ 별점·후기(좌) + 가격(우) 한 줄 정렬
                          ▶ cc-price-big을 position:absolute (right:1rem, bottom:.85rem)
                          ▶ cc-body padding-bottom .85rem로 가격 영역 확보
                          ▶ cc-rating은 자연스럽게 좌측 마지막 자식으로 배치

                        【보존】
                        - cc-spacer / cc-summary-empty / cc-meta-row-empty / cc-badges-empty 숨김 유지
                        - 카드 하단 찜+수강신청 버튼 영역(cc-body-action) 그대로 유지
                        - PC 동작은 영향 없음 (모든 변경이 @media max-width:768px 안)
                        - v68.79의 인기과정 헤더 재구성 변경도 그대로 유지

  v68.79 (2026-05-07) - 인기과정 모바일 헤더/카드 재구성 (PPT 슬라이드 3, 사용자 요청 2건):

                        【1. 카드 내부 압축 (이미지 2번 요구사항)】
                        ① 과정명 1줄 (이전 2줄 → 1줄, line-clamp:1)
                          ▶ 한국어 가독성보다 "초록색 여백 제거" 우선 (사용자 명시 요청)
                          ▶ font-size .92rem, line-height 1.3
                        ② 신청기간/태그 글씨 크기 축소
                          ▶ 신청기간: .72rem, 1줄 ellipsis
                          ▶ 태그: .68rem, 패딩 .15rem .5rem
                        ③ 별점/후기 영역 폰트 .72rem, 위쪽 여백 .1rem로 축소
                        효과: 카드 내부 빈 공간(초록색 여백) 제거, 보라색 영역 축소

                        【2. 모바일 헤더 재구성 (이미지 3번 요구사항)】
                        ① "더보기 →" 버튼 제거 (전체 보기와 중복 역할)
                          ▶ .pop-mobile-ctrl 영역 전체 삭제
                        ② "전체 보기 →" 버튼을 부제 아래로 이동 (.pop-mobile-allbtn)
                        ③ ‹ › 네비게이션 버튼을 헤더 우측 상단으로 이동 (.pop-mobile-nav)
                        ④ PC: 기존 동작 유지 (.pop-pc-allbtn 헤더 우측, 모바일 전용 요소 숨김)
                        ⑤ JS 핸들러 fallback 추가 — 헤더의 ‹ › 버튼은 .pop-panel 밖이므로
                          closest 실패 시 활성 패널(.pop-panel.on)로 fallback

                        역할 원칙: Architect/Designer/Developer/Reviewer 4단계 적용
                        토큰: navy #0F2040, blue #1B6FD8 (기존 디자인 토큰 준수)

  v68.78 (2026-05-07) - 모바일 수정 16건 (PPT9):

                        【🅐 카드 시스템 (S1, S2, S15)】
                        ① S1 신규과정 카드 — 빈 영역 완전 제거 (8회 시도 끝에 해결)
                          ▶ CSS Grid 2열로 가로 레이아웃 변경: 110px 썸네일 + 1fr 콘텐츠
                          ▶ 콘텐츠: 카테고리 → 제목(2줄 max) → 신청기간 → 태그(1줄) → 별점·후기·가격
                          ▶ 빈 영역 element들(.cc-summary, .cc-spacer, .cc-meta-row-empty 등)
                            모두 display:none + max-height:0 강제
                        ② S2 인기과정 가로카드(.cch) 별점/후기 정렬 통일
                          ▶ inline-flex + align-items center로 baseline 통일
                        ③ S15 카드 테두리 깨짐
                          ▶ overflow:hidden + border-radius:14px + 슬라이더 padding 추가

                        【🅑 페이지 구조 (S3, S6, S13)】
                        ④ S3 오시는 길 — 좌표 미설정 시 fallback 안내
                          ▶ 비관리자도 카카오/네이버/OpenStreetMap 검색 링크 표시
                          ▶ 주소/연락처 grid 모바일 1열
                        ⑤ S6 이용약관 — 항상 모달로 (URL 무시, 개인정보처리방침과 통일)
                        ⑥ S13 전체메뉴 고객지원 — "1:1 문의"/"나의 강의실" 삭제,
                          "기업교육 문의" 추가

                        【🅒 게시판 (S7~S10)】
                        ⑦~⑩ 커뮤니티 게시판 (.tbl) 카드 → 1줄 콤팩트
                          ▶ 기존: 큰 박스 + 모든 td block (4-5줄 차지)
                          ▶ 변경: tr flex 가로 + 1행 제목 + 2행 [날짜·작성자·상태 가로 배치]

                        【🅓 푸터 (S5, S11-①)】
                        ⑪ S5 푸터 이메일 한줄 내림 (모바일 전용 br 추가)
                        ⑫ S11-① "문의하기" 링크 → 커뮤니티 1:1 문의(?tab=qna)로 변경

                        【🅔 인기과정 컨트롤 (S12)】
                        ⑬ "더보기" 모바일 버튼 숨김 (PC "전체 보기"와 중복 제거)
                          ▶ "전체 보기"는 모바일에서도 우상단 유지
                          ▶ < > 버튼만 우측 정렬

                        【🅕 글씨/이미지 (S4, S11-②, S14)】
                        ⑭ S4 개인정보처리방침/이용약관 모달 — 시행일 select 글씨 축소
                        ⑮ S11-② 기업교육 문의 폼 — input/select/textarea 폰트 .85rem
                        ⑯ S14 메인 슬라이더 배너 — background-size:cover 강제
                          ▶ 원래 100% 100% → 모바일에서 가로 늘려져 글씨 눌림
                          ▶ cover로 비율 유지 (양옆 잘려도 OK)

  v68.77 (2026-05-07) - PC 수정사항 9건 (PPT9):

                        【🅐 비밀번호 가시성 토글 + CAPS LOCK 안내 (S2,6,7,8,9)】
                        ▶ 공통 컴포넌트 .esg-pw-wrap + JS attachPwToggle()
                        ▶ data-pw-toggle 속성 또는 .esg-pw-wrap 부모만 있으면 자동 부착
                        ▶ 눈 아이콘: SVG 32×32 우측 absolute, 클릭 시 type 토글
                        ▶ CAPS LOCK 안내: 노란 hint 박스 input 하단에 슬라이드 표시
                        ▶ 적용 위치 (총 7개 input):
                          · page-register.php — 비밀번호 + 비밀번호 확인
                          · functions.php (esg_check_user_reauth) — 본인확인 비밀번호
                          · page-mypage.php — 현재/새/새확인 비밀번호 + 회원탈퇴 비밀번호

                        【🅑 검색 오류 수정 (S5)】
                        ▶ esgLiveSearch() fetch에 nonce 누락 → "검색 중 오류" 발생
                        ▶ esangeduAjax.live_search_nonce 추가 전송으로 수정

                        【🅒 나의 학습 (S3, S4)】
                        ▶ S3: 최근 학습 영역 (.my-enroll-item) status 따라 배지 색상 동적
                          · 취소/학습취소 = b-red (빨강) — CSS specificity 강제 추가
                          · 검토중 = b-orange / 승인완료·학습중 = b-blue / 학습완료 = b-gray
                        ▶ S4: 취소된 수업은 "리뷰 달기" 버튼 비활성 (회색)
                          · $_recent_can_review 플래그 + 비활성 disabled 버튼
                          · 안내 문구: "취소된 수업은 리뷰를 작성할 수 없습니다"

                        【🅓 기업교육 문의 결과 안내 (S1)】
                        ▶ 제출 후 빈 화면 → 안내 영역 견고화
                          · $done_title/$done_desc 빈 옵션이어도 fallback 텍스트
                          · ✅ 큰 아이콘 + 제목 + 설명 + 1544-4781 + CTA 2개
                          · 옵션 비어있는 경우에도 "문의가 정상적으로 접수되었습니다" 등 표시

                        ▶ Reviewer 시뮬레이션 검증:
                          · 비밀번호 토글: 3개 input 모두 wrapper+버튼+caps hint 부착 ✅
                          · 토글 클릭 시 password ↔ text 정상 전환 ✅
                          · CAPS LOCK 시뮬레이션 시 노란 안내 표시 ✅
                          · PHP 문법 OK, JS 문법 OK, CSS 정합성 OK

  v68.76 (2026-05-07) - 모바일 교육 상세페이지 + 수강신청 모달 14건 (PPT8):

                        【🅐 교육 상세페이지 — single-course.php】
                        ① 상단 히어로(.esg-course-hero) 모바일 숨김 — 정보 중복
                        ② 갤러리 슬라이더 모바일 풀폭 (좌우 -1rem 마진 + width 100%+2rem)
                        ③ 카테고리/제목 재배치 + 강사·차시(.esg-course-titlebar-meta) 삭제,
                           공유·찜 아이콘 우상단 absolute
                        ④ 모바일 전용 정보 박스(.esg-mobile-info-box) 추가:
                           신청기간/교육기간/교육시간/수업요일/정원
                        ⑤ 메뉴탭(.esg-anchor-nav) 가로 스크롤 + 폰트 .85→.95rem (1pt UP)
                        ⑥ 메뉴라인 sticky 모바일에서 top:0으로 정상 작동
                        ⑦ 하단 바: 찜 버튼(44×44) + "총 금액" 라벨 + CTA flex:1 (가로 2배)
                        ⑧ 우측 결제 카드(.esg-course-aside) 모바일 숨김 — 퀵결제로 통일
                        ⑨ 모바일 TOP 버튼(.esg-mobile-top): 스크롤 200px↑에서 표시,
                           하단 바 위(bottom:5.5rem), 44×44, 반투명 navy
                           
                        【🅑 수강신청 모달 — single-course.php 안의 #esg-enroll-modal】
                        ⑩ 모달 풀화면 (top/left/right/bottom:0, width/height:100%,
                           border-radius:0, transform:none) — 위쪽 빈공간 제거 통합
                        ⑪ 위쪽 틈 현상은 ⑩의 풀화면 처리로 해결
                        ⑫ 제목 "수강 신청" 폰트 1.1→1.5rem,
                           이모티콘(📝) 제거: font-size:0 + ::after content
                        ⑬ X 버튼 32→44×44, 배경 회색→투명, 폰트 1.2→2rem
                        ⑭ 주문 완료 모달(.esg-em-panel-complete)도 ⑩과 동일 풀화면

                        ▶ Reviewer 시뮬레이션 검증:
                          · 모바일 380px: 14건 모두 정상 (스크린샷 m_top.png, modal_mobile.png)
                          · PC 1280px 회귀: 모바일 전용 요소 모두 display:none 유지 ✅
                          · 메뉴탭 sticky top:0 작동 ✅
                          · 모달 풀화면 + X 44×44 + 제목 큰 폰트 ✅

  v68.75 (2026-05-07) - 신규과정 카드 빈 영역 최종 — JS + CSS 3중 강제:
                        ▶ v68.74 wp_head inline style 적용 확인됐으나 사용자 환경
                          (모바일에서 직접 캡처/새로고침/캐시지움 모두 시도)에서
                          여전히 빈 영역 큼 — Image 3,4,5 모두 신규과정 카드
                        ▶ 인기과정 가로카드(.cch)는 완벽 작동, 신규과정만 안 됨
                        ▶ 진단: CSS 적용은 됐지만 슬라이더 컨테이너의 flex 메커니즘이
                          예상과 다르게 작동. specificity 또는 cascading 우선순위
                          이슈 가능성

                        【3중 강제】
                        ① style.css 맨 끝에 강제 규칙 추가 (cascading order 우위)
                          - .nc-track align-items: flex-start
                          - .nc-slide height: auto + align-self: flex-start
                          - .nc-slide .cc height: auto + min-height: 0
                          - .nc-slide .cc-link height: auto + flex: 0 1 auto
                        ② functions.php wp_head priority 999 inline style (v68.74 유지)
                        ③ main.js JS로 element.style.setProperty('!important') 강제 추가
                          - 인라인 style은 가장 강력한 specificity (1,0,0,0)
                          - CSS 어떤 규칙도 못 이김
                          - 리사이즈/탭전환 시에도 다시 적용

                        ▶ 콘솔 로그: [esangedu v68.75] mobile card fix: { ncSlides: N }
                          → 슬라이더 갯수 확인 가능

  v68.74 (2026-05-07) - 신규과정 빈 영역 진짜 원인 해결 (사용자 페이지 소스 분석):
                        ▶ 사용자 환경 확인:
                          · 페이지 소스에 esangedu theme version: 1.0.68.73 확인됨 ✅
                          · esg-mobile-card-fix-v68-73 inline style 박혀있음 ✅
                          · 카드에 data-mobile-hide="1" 속성 박혀있음 ✅
                          · 캐시 플러그인 없음, 워드프레스 정식 업로드 ✅
                          → 코드는 적용됐는데 화면이 안 변하는 것 = 100% 제 코드 문제

                        ▶ 사용자 페이지 소스 직접 분석으로 진짜 원인 발견:
                          신규과정 카드가 일반 그리드(.cg)가 아니라 슬라이더(.nc-slide)
                          안에 들어있음:
                            <div class="nc-track">
                              <div class="nc-slide">       ← 슬라이더 컨테이너
                                <div class='cc'>            ← 카드
                          
                          style.css L2400-2401 본 정의:
                            .nc-slide { display:flex; flex-direction:column }
                            .nc-slide .cc { height: 100% }   ← 🎯 진짜 원인!

                          → 카드가 슬라이드 컨테이너 높이만큼 강제로 늘어남
                          → 가장 긴 카드 높이에 모든 카드 맞춰짐
                          → 짧은 카드는 콘텐츠 아래 빈 공간 발생

                        ▶ v68.68~v68.73의 모든 수정이 카드 내부 빈 placeholder만
                          처리했지 .nc-slide의 .cc { height: 100% } 강제는 처리 못함

                        【핵심 변경】 wp_head inline style에 슬라이더 카드 처리 추가
                        ▶ .nc-slide .cc { height: auto !important; min-height: 0 !important }
                        ▶ .nc-slide { align-items: flex-start; height: auto }
                        ▶ .nc-track { align-items: flex-start }
                        ▶ .nc-slide .cc-link { height: auto; min-height: 0 }

                        ▶ Reviewer 시뮬레이션 검증 (사용자 페이지 구조 그대로 재현):
                          · 슬라이드 1 (긴 카드): 447px → 카드 447px (자연 높이) ✅
                          · 슬라이드 2 (짧은 카드): 299px → 카드 299px ✅
                            (이전엔 슬라이드가 447로 강제, 카드는 299만큼만 차지해서
                             아래 148px 빈 공간 → 사용자 사진의 빈 영역의 정체)
                          · 빈 placeholder 9개 모두 display:none ✅
                          · 카드 bottom vs 수강신청 bottom 차이: 1px (빈 공간 거의 0) ✅

                        ▶ 학습:
                          시뮬레이션을 가짜 HTML이 아닌 사용자 페이지 진짜 구조로
                          하지 않으면, 같은 이슈가 반복될 수 있음. 사용자 페이지 소스
                          직접 분석이 가장 빠른 진단 방법.

  v68.73 (2026-05-07) - 신규과정 미반영 + 인기과정 텍스트 컴팩트 (PPT6 슬라이드 2건):
                        ▶ 사용자 보고: "1번은 아예 적용이 안 되네" — v68.72 적용했어도
                          신규과정 카드 빈 영역 그대로 유지
                        ▶ 진단: 외부 CSS / main.js / style.css 등이 사용자 환경의 캐시
                          플러그인에 의해 갱신 안 되거나 일부만 적용됨

                        【핵심 변경】 wp_head에 inline <style> 직접 출력 (모든 캐시 우회)
                        ▶ 위치: functions.php (wp_head 액션 priority 999)
                        ▶ 동작: 페이지 HTML에 <style id="esg-mobile-card-fix-v68-73">
                                직접 박혀나옴 → 외부 CSS 파일과 무관하게 작동
                        ▶ priority 999: 모든 다른 wp_head 출력보다 마지막 = cascading 우위
                        ▶ inline <style>은 PHP가 매 요청마다 출력 = 캐시 플러그인이
                                CSS 합치기/압축할 때도 누락 불가 (HTML 본문에 박힘)
                        ▶ 강제 적용 대상:
                          · 빈 placeholder 5종 + data-mobile-hide 속성 모두 display: none
                          · PC 카드 정렬 메커니즘 5종 (.cg, .cc, .cc-link, .cc-body, name/summary)
                          · cc-rating margin-bottom .35 → .15rem (별점 아래 빈 영역 축소)
                          · cc-price-big margin/padding 0
                          · cc-body / cc-body-action 패딩 컴팩트
                        ▶ 시뮬레이션 검증: 빈 영역 모두 height 0px, 카드 485px (콤팩트)

                        【이슈 1】 신규과정 카드 빈 영역 (S1 — v68.72 미반영)
                        ▶ 위 wp_head inline CSS로 강제 적용
                        ▶ 카드 element 위치 검증: thumb→cat→name→summary→meta→badges→
                                                  rating→price→action 모두 빽빽하게 붙음

                        【이슈 2】 인기과정 가로카드 텍스트 컴팩트 (S2)
                        ▶ 사용자 요청: "과정명을 1줄 또는 태그를 1줄로 변경해서 여백 없애기"
                        ▶ 결정: 한국어 가독성 위해 과정명 2줄 유지, 태그를 1줄로 제한
                        ▶ 해결:
                          · .cch-name -webkit-line-clamp 3 → 2
                          · .cch-tags flex-wrap: nowrap + overflow: hidden + max-height
                          · 첫 줄 태그만 보이고 나머지는 잘림 (가로 폭 제한적이므로)
                        ▶ 시뮬레이션 검증: 카드 높이 206 → 160px (22% 축소)

                        ▶ Reviewer 시뮬레이션 결과:
                          · 모바일 380px: 빈 영역 height 0px, cc-spacer 사라짐 ✅
                          · 카드 element 모두 콤팩트 (스크린샷 확인) ✅
                          · 인기과정 가로카드: 과정명 2줄 + 태그 1줄, 높이 160px ✅
                          · PC 1280px 회귀: placeholder display block, cc-link flex 1 유지 ✅

  v68.72 (2026-05-07) - v68.71 미반영 + 신규 (PPT5 슬라이드 2건):
                        ▶ 사용자 보고: v68.71 적용 후에도 카드 빈 영역 그대로
                        ▶ 진단: CSS+JS 모두 시뮬레이션 검증 통과했음에도 사용자 환경에서
                                작동 안 함 → 캐시 플러그인/CDN/브라우저 캐시가 v68.71의
                                main.js와 style.css를 갱신 안 한 상태로 추정
                        ▶ 새 전략: PHP 단에서 빈 placeholder div에 직접 HTML 속성 추가
                                  (PHP는 매 요청마다 실행 = 캐시 플러그인이 우회 못함)

                        【핵심 변경 1】 PHP 카드 함수에 data-mobile-hide="1" 속성 추가
                        ▶ 위치: functions.php > esangedu_course_card()
                        ▶ 빈 placeholder 5종에 data-mobile-hide="1" HTML 속성 부여:
                            cc-meta-row-empty (강사/신청기간 빈 자리)
                            cc-badges-empty (태그 빈 자리)
                            cc-summary-empty (한 줄 소개 빈 자리)
                            cc-cat-empty (카테고리 빈 자리)
                            cc-spacer (위치 통일용 빈 영역)
                        ▶ CSS 매칭: [data-mobile-hide="1"] { display: none ... }
                                    specificity (0,2,0)으로 본 정의 (0,1,0) 이김
                        ▶ 효과: class 셀렉터로 안 잡히는 케이스도 잡음. 외부 캐시가
                                CSS 갱신 안 했어도 PHP 단에서 속성이 박혀나옴

                        【핵심 변경 2】 페이지 소스에 테마 버전 노출
                        ▶ 위치: header.php
                        ▶ 사용자 페이지 소스보기(Ctrl+U) 또는 모바일 보기소스에서
                          <!-- esangedu theme version: 1.0.68.72 --> 검색 가능
                        ▶ 사용자가 실제로 어느 버전을 보고 있는지 직접 확인 가능

                        【이슈 1】 카드 빈 영역 (S1 — v68.71 미반영)
                        ▶ data-mobile-hide 속성으로 강제 숨김 (위 핵심 변경 1)
                        ▶ 사용자 환경 캐시 무관하게 작동

                        【이슈 2】 인기과정 썸네일 위아래 여백 (S2)
                        ▶ 사용자 요청: PC 2번처럼 꽉차게 다 보이게
                        ▶ 원인: v68.71의 align-self: stretch + aspect-ratio: auto
                                → 텍스트 길이에 따라 비율 변형 + contain의 letter-box
                        ▶ 해결: aspect-ratio: 4/3 고정 + cover로 꽉 채움
                                이미지가 4:3에 가까우면 잘림 거의 없고 빈 영역 0
                                align-self: flex-start (텍스트 길이와 무관하게 4:3 유지)

                        ▶ Reviewer 시뮬레이션 검증:
                          · [data-mobile-hide="1"] 요소 모두 display: none ✅
                          · cc-spacer height: 0px ✅
                          · 인기과정 썸네일: object-fit: cover, 108×81 (4:3 비율) ✅
                          · dot 정원 (1:1 비율) 유지 ✅
                          · 카드 콤팩트 표시 (스크린샷 확인) ✅

  v68.71 (2026-05-07) - v68.70 미반영 / 부작용 + 신규 4건 (PPT4 슬라이드 4건):
                        ▶ 이번 버전부터 [Reviewer] 단계에서 실제 브라우저 시뮬레이션 도입
                          (Puppeteer + Chromium 헤드리스 + 모바일 380px 뷰포트)
                          → CSS 적용 여부, 빈 placeholder display 값 등을 직접 측정 검증

                        ▶ 핵심 발견:
                        시뮬레이션에서 v68.70 코드는 "정상 작동" 확인됨.
                        그러나 사용자 환경에서 일부 변경 안 보임 → 외부 환경 요인:
                          · 워드프레스 캐시 플러그인이 inline <script>를 누락시킴
                          · CDN 캐시 / 호스팅 캐시
                          · 브라우저 캐시
                        해결: footer.php inline JS → main.js로 이전 (wp_enqueue_script
                              등록되어 캐시 플러그인이 무시 못함)

                        【핵심 변경】 모바일 카드 강제 처리 JS 위치 이전
                        ▶ footer.php inline <script> 삭제
                        ▶ assets/js/main.js 끝에 추가
                        ▶ 디버그 로그: 콘솔에 [esangedu v68.71] 적용 여부 출력
                        ▶ window.esgApplyMobileCardFix 노출 (탭 전환 등에서 재실행 가능)

                        【이슈 1】 dot 동그라미 모양 (S1 — v68.70 부작용)
                        ▶ 사용자 보고: dot이 타원으로 보임, 동그라미로 변경 요청
                        ▶ 진단(시뮬레이션): width 8px 줬는데 height가 40px로 측정됨
                        ▶ 진짜 원인 발견: v68.67 통합블록의
                                  `button { min-height: 40px !important }` 가
                                  .hero-dot에도 적용되어 8×40 타원 발생
                        ▶ 해결:
                          · 600px 미디어쿼리 dot/active 모두 정원(border-radius:50%)
                            inactive 8×8, active 14×14
                          · button min-height 40 규칙에 예외 추가:
                            .hero-dot, .hero-ctrl-btn, .pop-nav, .pop-tab, .nc-nav,
                            .cc-heart 는 min-height: auto
                        ▶ 시뮬레이션 검증: width=8px, height=8px, ratio=1.00, 정원 확인 ✅

                        【이슈 2,3】 신규과정 카드 빈 placeholder + 여백 (S2,S3 — 일부 미반영)
                        ▶ 외부 JS 파일로 이전 (위 핵심 변경)
                        ▶ 콘솔 로그로 적용 여부 즉시 확인 가능
                        ▶ cc-body-action 패딩 0.9 → 0.5rem (수강신청 아래 여백 축소)
                        ▶ 시뮬레이션 검증: 빈 placeholder 5종 모두 display:none ✅

                        【이슈 4】 인기과정 가로카드 이미지 잘림 (S4)
                        ▶ 사용자 요청: PC 2번처럼 이미지 전체가 보이게 (잘리지 않게)
                        ▶ 원인: 본 정의 .cch-thumb img { object-fit: cover !important }
                                → 비율 다른 이미지의 좌우/위아래가 잘림
                        ▶ 해결: 모바일에서 object-fit: contain + 빈 영역 navy 배경
                        ▶ 시뮬레이션 검증: object-fit: contain 적용 확인 ✅

                        ▶ Reviewer 시뮬레이션 결과:
                          · 모바일 380px: 빈 placeholder 5종 display:none, 카드 콤팩트
                          · PC 1280px (회귀 검증): placeholder들 display:flex/block 유지,
                            cc-name min-height 62.72px, cc-link flex:1 (PC 정렬 정상)
                          · dot inactive 8×8, active 14×14 모두 정원
                          · 인기과정 가로카드: flex-direction:row, 가로 스크롤 작동

  v68.70 (2026-05-07) - 모바일 미반영 5건 강제 적용 (PPT3 슬라이드 3건):
                        ▶ 사용자 보고: v68.69 적용 후에도 일부 미반영 + 부작용 추가
                        ▶ 핵심 결정: CSS specificity가 일부 환경에서 무력해지는 문제 →
                                     JS로 인라인 style을 박는 강제 방식 도입
                                     (인라인 style은 모든 외부 CSS를 이김)

                        【이슈 1A】 히어로 배너 비율 안 맞음 (S1-① — v68.69 부작용)
                        ▶ v68.69의 contain은 좌우/위아래 둘 다 빈 영역(letter-box) 발생
                        ▶ 사용자 의도: "안쪽 네모만큼만 보여도 OK" = 빈 영역 없이 가득
                        ▶ 해결: background-size: 100% 100% (약간 변형되지만 빈 영역 0)
                                aspect-ratio 16/10 → 16/8 (가로 긴 PC 분위기 유지)

                        【이슈 1B】 컨트롤 버튼/dot 길쭉이 (S1-② — v68.68 부작용)
                        ▶ 원인: v68.68의 padding+content-box 트릭 + active dot width 18px
                                → background-clip: content-box로 padding이 투명되며
                                  보이는 영역 18×8 (2.25:1) = 길쭉한 막대로 보임
                        ▶ 해결: padding 트릭 완전 제거, active dot 14×7px (2:1) 자연 비율로
                                컨트롤 버튼 36×36 + padding 4px → 32×32 + padding 0 (단순)
                                터치 영역은 부모 .hero-controls의 padding으로 확보

                        【이슈 2,3】 신규과정 카드 세로 김/여백 큼 (S2 — v68.68/69 미반영 또)
                        ▶ 분석: CSS 규칙은 specificity 충분 + !important 있는데도 적용 안 됨
                                (캐시/CDN/플러그인 등 외부 환경 요인 가능성)
                        ▶ 결정적 해결: footer.php에 모바일 카드 강제 정리 JS 추가
                                · 인라인 style.setProperty('display','none','important')는
                                  모든 외부 CSS를 이김 (캐시 무관)
                                · 빈 placeholder 5종 (cc-summary-empty/cc-cat-empty/
                                  cc-meta-row-empty/cc-badges-empty/cc-spacer) 강제 숨김
                                · PC 카드 정렬 메커니즘 4종 (grid 1fr / cc-link flex:1 /
                                  cc-link cc-body flex:1 / cc-name min-height / cc-summary
                                  min-height) 모바일에서 강제 풀어줌
                                · resize 이벤트 대응 (회전/창 리사이즈 시 PC↔모바일 전환)

                        【이슈 5】 인기과정 가로카드 썸네일 밑 잘림 (S3 — v68.69 부작용)
                        ▶ 원인: v68.69의 align-self: flex-start + aspect-ratio 4/3
                                → 텍스트가 길면 thumb은 작은 채로 남고 카드 하단까지 안 늘어남
                                → "위는 둥근 모서리, 아래는 직각 잘린" 모양으로 보임
                        ▶ 해결: align-self: stretch — thumb이 카드 height 가득 늘어남
                                aspect-ratio 제거 + min-height 120px (콘텐츠 짧은 경우 대응)
                                border-radius 10px 명시

  v68.69 (2026-05-07) - v68.68 미반영분 재수정 (PPT 4슬라이드 5건):
                        ▶ 사용자 보고: v68.68 적용했으나 일부 미반영 또는 부작용 발생
                        ▶ [Architect] 원인 진단 후 [Developer] 재수정

                        【이슈 1】 히어로 배너 좌우 잘림 (S1 — v68.68 부작용)
                        ▶ 원인: v68.68에서 background-size: cover로 변경했는데, cover는
                                컨테이너를 채우려 가로 긴 PC 배너의 좌우를 잘라냄
                        ▶ 사용자 요청: PC 비율 그대로 모바일에서도 전체 노출
                        ▶ 해결: cover → contain 변경, aspect-ratio: 16/10 명시,
                                빈 영역(letter-box) 배경을 navy(#0F2040)로 자연스럽게

                        【이슈 2,3】 신규과정 카드 세로 김/여백 큼 (S2 — v68.68 미반영)
                        ▶ 원인: v68.68의 .cc-summary-empty 등 display:none은 적용됐으나,
                                PC 카드 정렬 메커니즘이 더 강력했음:
                                · .cg { grid-auto-rows: 1fr } — 모든 카드 동일 높이 강제
                                · .cc-link { flex: 1 } — 본문이 카드 높이만큼 확장
                                · .cc-link .cc-body { flex: 1 } — 본문 가용공간 채움
                                · .cc-spacer { flex: 1 1 auto } — 가용공간을 spacer가 흡수
                                이 4개가 빈 공간을 만드는 진짜 원인이었음
                        ▶ 해결: 모바일에서 4개 규칙 모두 풀어줌 (1열에서는 정렬 강제 불필요)
                                · grid-auto-rows: auto / cc-link, cc-body flex: 0 1 auto
                                · cc-spacer display: none

                        【이슈 4】 인기과정 가로카드 이미지 사라짐 (S3 — v68.68 부작용)
                        ▶ 원인: v68.68에서 .cch-thumb에 height:auto + aspect-ratio:auto
                                자식 img는 position:absolute; height:100% → 부모 0 높이 →
                                자식 이미지 안 보임
                        ▶ 해결: aspect-ratio: 4/3 유지 (자식 이미지가 정상 렌더되도록)
                                width 40% → 42% + max-width 명시, padding/gap 보강

                        【이슈 5】 인기과정 < > 버튼 무반응 (S3 — v68.68 미반영)
                        ▶ 원인: querySelector('.cc, .cch, > *')의 '> *' 가 표준 X
                                일부 브라우저에서 핸들러 자체 실패 가능
                        ▶ 해결: '> *' 제거, firstElementChild로 fallback
                                · capture: true로 등록 (카드 안의 <a> 링크보다 먼저 잡힘)
                                · e.preventDefault() + e.stopPropagation() 추가
                                · scrollBy 미지원 폴백: scrollLeft += step
                                · gap 파싱: gap → columnGap 폴백

                        【이슈 6】 푸터 가로 3열 여백 큼/왼쪽 정렬 (S4 — v68.68 미세조정)
                        ▶ 원인: v68.68의 repeat(3, 1fr)이 화면을 3등분 — 컬럼 사이에 큰 여백
                        ▶ 사용자 요청: 좌측 정렬 + 컬럼 간 여백 축소
                        ▶ 해결: 768px에서 repeat(3, auto) + justify-content: start + gap 1.5rem
                                560px gap 0.65 → 1rem (가독성)
                                380px gap 0.45 → 0.65rem

  v68.68 (2026-05-07) - 모바일 화면 깨짐 8건 수정 (PPT 사진 5장 기반):
                        ▶ 역할 분담 작업: [Architect] 설계 → [Designer] 토큰 → [Developer] 구현

                        【이슈 1】 헤더 로고 이미지 너무 큼/잘림 (Slide 1)
                        ▶ 원인: header.php:568 인라인 style="height:40px;max-width:200px"
                                가 모바일에서도 그대로 적용
                        ▶ 해결: .site-logo img[style] 셀렉터로 specificity 강화,
                                Designer 토큰 max-height 32px / max-width 140px 적용

                        【이슈 2】 히어로 슬라이드 배너 깨짐 (Slide 2-①)
                        ▶ 원인: PHP 인라인 background-size/position이 PC용 그대로 들어감
                        ▶ 해결: .hero-slide .hero-bgimg[style] 강한 셀렉터로 cover/center 강제
                                .hero-slider min-height: 320 → 240px (모바일 컴팩트)
                                position: relative + bgimg position: absolute inset:0 명시

                        【이슈 3】 히어로 컨트롤 버튼 작음/깨짐 (Slide 2-②)
                        ▶ 원인: 480px 이하 .hero-ctrl-btn 30×30 (iOS HIG 44 미달)
                        ▶ 해결: 시각 36×36 + padding 4px = 실 터치 44×44 (Designer 토큰)
                                dot 8px / active 18px + padding 6px (background-clip: content-box)

                        【이슈 4】 신규과정 카드 세로 너무 김 (Slide 3-①)
                        ▶ 원인: .cc-summary-empty/cc-cat-empty/cc-meta-row-empty/cc-badges-empty
                                placeholder가 모바일에서도 약 12rem(192px) 빈 공간 차지
                                (PC 카드 정렬용인데 모바일은 1열이라 불필요)
                        ▶ 해결: 모바일에서 모든 *-empty placeholder display: none

                        【이슈 5】 카드 내부 여백 과다 (Slide 3-②)
                        ▶ 해결: .cc-body padding .9 → .75rem,
                                .cc-foot padding-top .75 → .5rem,
                                .cc-meta margin/padding 컴팩트화

                        【이슈 6】 카드 태그 4개 안 들어감/한쪽 쏠림 (Slide 3-③)
                        ▶ 원인: .cc-tags flex-wrap: wrap 으로 모바일 좁은 폭에서 wrap 발생
                        ▶ 해결: Designer 토큰 — 가로 스크롤 (스크롤바 숨김)
                                .cc-tags, .cc .cc-badges에 nowrap + overflow-x auto
                                ::-webkit-scrollbar display: none + 스크롤바 표준 속성도 차단

                        【이슈 7】 인기과정 가로 슬라이드 + < > 버튼 (Slide 4) ⭐ 큰 작업
                        ▶ 원인: v68.67 통합블록이 인기과정도 1열 + 가로카드→세로카드 강제 변환
                        ▶ 사용자 요청: 가로카드 유지 + < > 버튼으로 옆으로 보기
                        ▶ 해결: [Architect 옵션 B] CSS + JS 컨트롤 버튼 추가
                                · index.php: .pop-cards 클래스 추가 (식별자), .pop-mobile-ctrl 영역 추가
                                · CSS: 모바일에서 .pop-panel .pop-cards를 가로 flex로 변환
                                       (Designer 토큰: 카드 폭 88%, gap .75rem, 컨트롤 40×40)
                                · JS: .pop-nav 클릭 핸들러 — 카드폭+gap만큼 scrollBy
                                · 가로카드(.cch) 세로변환을 무력화하여 가로 유지

                        【이슈 8】 푸터 메뉴 가로 3열 (Slide 5)
                        ▶ 원인: v68.67 통합블록이 .site-footer .f-nav-cols를 강제 2열로 덮음
                                (768px) + 강제 1열로 덮음 (480px)
                                footer.php 자체는 가로 3열을 정상 처리 중이었음
                        ▶ 해결: v68.67 통합블록의 .f-nav-cols 강제 규칙 2개 제거
                                footer.php에서 560px 이하 1fr 1fr → 가로 3열 유지로 변경
                                (작은 화면은 폰트/간격 축소로 대응 — 380px 이하 추가 축소)

  v68.67 (2026-05-07) - 전체 코드 점검 & 정리 (1+2+3 번호 우선순위):

                        【1순위】 모바일 CSS 통합 마무리:
                        ▶ 진단:
                          · v68.66 changelog는 "v68.62~v68.65 구버전 모바일 CSS 모두
                            삭제" 라고 적혀있었으나, 실제로는 구버전 768px 블록(70줄)과
                            구 600px 블록(10줄), 구 480px 블록(10줄)이 그대로 남아있었음
                          · 같은 @media (max-width: 768px) 가 두 번 정의되어
                            !important 99개 + 25개가 같은 페이지에서 충돌
                          · 신버전이 뒤라 결과적으로 적용은 되지만, 중복 부담만 발생
                        ▶ 해결:
                          · 구 768px / 구 600px / 구 480px 블록 완전 제거
                          · 구 블록에만 있던 9개 규칙 그룹을 신 블록에 흡수:
                            ① 일반 h1~h3 폰트 크기
                            ② 한글 줄바꿈 word-break: keep-all
                            ③ iOS 자동확대 방지 input font-size: 16px (★ 필수)
                            ④ 폼 필드 width 100% (.fg, .esg-input 등)
                            ⑤ 버튼 터치 영역 min-height: 40px
                            ⑥ 일반 table 가로 스크롤 (.tbl 카드형 별도 보존)
                            ⑦ 섹션 상하 패딩 2.5rem
                            ⑧ 그리드 1열 셀렉터 추가 (.notice-grid, .esg-about-grid 등)
                            ⑨ 탭 가로 스크롤 셀렉터 추가 (.esg-nav-tabs 등)
                          · 푸터 정렬 강화 + 네비 버튼 크기 + 480px 정책링크
                          · 결과: 768px 블록이 2개 → 1개로 단일화
                        ▶ 보존된 블록:
                          · 900px (2190~) — 태블릿 영역, 별도 유지 필요 (.hero-in 등)
                          · 600px (2007~) — 컴포넌트 지역 미디어쿼리, 유지
                          · 960/720/480 (2063~2065) — .nc-slide 컴포넌트 전용

                        【2순위】 죽은 코드 + nonce 명명 컨벤션 정리:
                        ▶ 호출 JS가 없어 작동 불가능했던 죽은 AJAX 핸들러 3개 제거:
                          · wp_ajax_esangedu_create_pages — 헬퍼 함수는 보존
                            (esangedu_health_page에서 직접 호출됨)
                          · wp_ajax_esg_fix_templates — 헬퍼 함수는 보존
                          · wp_ajax_esg_toggle_page_menu — 완전 제거
                        ▶ nonce 액션명 혼재 문제 정리:
                          · esg_inquiry (기업교육 문의) → esg_corp_inquiry_nonce 로 통일
                          · 1:1 문의는 그대로 esg_inquiry_nonce 사용
                          · 변경 위치: functions.php:12780, page-inquiry.php:256

                        【3순위】 nonce 미보호 5건 보강 (CSRF 보호):
                        ▶ 핸들러에 check_ajax_referer 추가:
                          · esg_live_search_handler        → esg_live_search_nonce
                          · esg_get_course_info_handler    → esg_course_info_nonce
                          · esg_cart_add_handler           → esg_cart_nonce
                          · esg_cart_remove_handler        → esg_cart_nonce (공유)
                          · esg_check_username_handler     → esg_register_nonce (공유)
                        ▶ esangeduAjax localize에 nonce 4종 추가:
                          · cart_nonce / course_info_nonce / register_nonce / live_search_nonce
                        ▶ 호출 JS 4곳에 nonce 전송 추가:
                          · footer.php (get_course_info)
                          · single-course.php (cart_add)
                          · page-mypage.php (cart_remove)
                          · page-register.php (check_username)

                        【추가 발견 — 별도 결정 필요】
                        ▶ 헤더 검색바 esgLiveSearch() JS 함수 미정의
                          · 핸들러는 있고 호출만 비어있어 실시간 검색 작동 안 함
                          · 별도 작업으로 처리 예정

  v68.66 (2026-05-06) - 모바일 반응형 통합 재작성:
                        ▶ 사용자 보고:
                          · 휴대폰에서 다 깨져 보임 (사진 4장)
                          · 강력 새로고침 + 캐시 플러그인 X 인데도 변화 없음
                        ▶ 진단:
                          · CSS는 적용되지만 더 강한 인라인 스타일에 덮임
                          · v68.62~v68.65에서 모바일 CSS 누적 → 자기들끼리 충돌
                          · 복잡해서 효과 없음
                        ▶ 해결:
                          · v68.62~v68.65의 모바일 CSS 모두 삭제
                          · 단일 미디어 쿼리에 16개 항목 통합 재작성
                          · 모든 규칙에 !important로 인라인 스타일 무력화
                        ▶ 16개 통합 항목:
                          ① 가로 넘침 절대 방지 (overflow-x: hidden)
                          ② 폰트 비례 조정 (calc(11px + 0.6vw))
                          ③ 모든 요소 max-width: 100%
                          ④ 헤더 컴팩트 (56px)
                          ⑤ 본문 좌우 패딩 1rem
                          ⑥ 모든 그리드 1열 강제 (인라인 포함)
                          ⑦ 가로카드(.cch) → 세로 변환
                          ⑧ 세로카드 1열
                          ⑨ 인라인 width:240px 등 무력화
                          ⑩ 마이페이지 사이드바 → 상단
                          ⑪ 배너 슬라이드 (cover/center)
                          ⑫ 푸터 정렬
                          ⑬ 게시판 → 카드형
                          ⑭ 모달 컴팩트
                          ⑮ 인기과정 탭 가로 스크롤
                          ⑯ 섹션 타이틀 폰트
                        ▶ 480px 이하: 추가 컴팩트 (작은 폰)
                        ▶ PC(769px+): 기존 그대로
  v68.65 (2026-05-06) - 모바일 강력 보정 (사진 4장 분석 기반):
                        ▶ 사용자 보고: 휴대폰에서 다 깨져 보임
                        ▶ 사진별 진단 + 수정:
                          ① 푸터 — 콘텐츠가 너무 왼쪽 끝에 붙음
                            · .site-footer 좌우 패딩 1.25rem 강제
                            · .f-nav-cols, .f-info 좌우 1rem 추가
                          ② 카드 그리드 — 1열 변환 안 됨 (인라인 스타일 우선)
                            · 모든 grid 강제 1열 (인라인 무시)
                            · div[style*="grid-template-columns"] 선택자
                            · 통계 카드만 2x2 유지
                          ③ 배너 슬라이드 — 이미지가 화면 잘림
                            · hero-bgimg background-size: cover 강제
                            · background-position: center
                            · 100% 100% 인라인 무시
                          ④ 인라인 width 무시
                            · [style*="width:240px"] → 100% 강제
                            · 가로카드 → 세로카드 강제 변환
                          ⑤ 인기과정 탭 → 가로 스크롤 허용
                        ▶ 핵심 전략:
                          · 인라인 스타일을 누를 high-specificity 선택자
                          · width:240px 같은 PC용 스타일을 모바일에서 무시
                          · cover/center로 이미지 중앙 정렬
                        ▶ 480px 추가 보정
                          · 더 작은 패딩 (1rem / 0.85rem)
  v68.64 (2026-05-06) - 가로카드 강사 줄 제거 + 모바일 비율 조정 (fluid):
                        ▶ 사용자 보고 ①: 가로카드 강사 줄 빼서 카드 높이 통일
                          ▷ 변경: 가로카드(.cch)에서 강사 출력 제거
                          ▷ 이유: 강사 + 신청기간 + 개폐강 확정일 = 3줄
                                  → 카드 height 210px 초과 → 태그/별점 잘림
                          ▷ 결과: 신청기간 + 개폐강 확정일만 표시 (최대 2줄)
                                  → 카드 안에 모든 콘텐츠 정상 표시
                          ▷ 강사 정보는 카드 클릭 후 상세 페이지에 그대로 표시됨
                        ▶ 사용자 보고 ②: 핸드폰에서 비율대로 조정되게
                          ▷ 변경: html font-size를 고정값 → fluid (vw 기반)
                          ▷ 768px 이하: calc(11px + 0.6vw)
                            · 320px 화면 → 12.9px
                            · 480px 화면 → 13.9px
                            · 600px 화면 → 14.6px
                            · 768px 화면 → 15.6px
                          ▷ 480px 이하: calc(10.5px + 0.6vw)
                            · 320px → 12.5px
                            · 480px → 13.4px
                          ▷ 결과: 화면 너비가 변하면 폰트도 비례해서 조정
                                  → 큰 핸드폰/작은 핸드폰 모두 보기 편함
                        ▶ 추가 모바일 가로 보호 강화:
                          · *, *::before, *::after { max-width: 100% } 적용
                          · 모든 요소의 가로 넘침 절대 방지
                          · img/video/iframe 비율 유지
                        ▶ PC(769px+)는 기존 그대로
  v68.63 (2026-05-06) - 인기과정 가로카드 콘텐츠 잘림 수정 (v68.61 회귀):
                        ▶ 사용자 보고:
                          · 강사 위에 빈 공간이 생김
                          · 아래쪽 태그(AI, ChatGPT, 인공지능)와 별점이 잘림
                        ▶ 원인:
                          · v68.61에서 추가한 cch-info-empty 빈 자리 코드
                          · 가로카드 height: 210px 고정인데
                            누락된 메타에 빈 줄을 채우면서 콘텐츠 높이 초과
                          · overflow:hidden 때문에 하단 콘텐츠가 잘림
                        ▶ 수정:
                          · v68.61 빈 자리 채우는 코드 제거
                          · 강사/신청기간/개폐강 확정일은 있을 때만 표시 (원래대로)
                          · 카드 안의 콘텐츠는 정상 표시되며 높이 안 넘침
                        ▶ 카드 높이 통일:
                          · 카드 자체 height: 210px 고정으로 이미 통일됨
                          · 빈 자리 채우기는 불필요 (오히려 역효과)
                        ▶ 결과:
                          · 가로카드의 모든 콘텐츠 정상 표시
                          · 태그/별점/리뷰 수 모두 카드 안에 보임
  v68.62 (2026-05-06) - 모바일 반응형 강화 (휴대폰 표시 최적화):
                        ▶ 사용자 보고:
                          · PC에서 가로 줄여보면 모바일이랑 똑같이 보임
                          · 비율대로 줄이지 않고 단순히 축소된 형태
                        ▶ 원인 분석:
                          · viewport meta는 정상
                          · 미디어 쿼리는 있지만 콘텐츠가 모바일 최적화 부족
                          · 헤더, 카드, 게시판 등이 PC 레이아웃 그대로 압축
                        ▶ 추가된 모바일 최적화 (768px 이하):
                          ▣ 헤더/네비
                            · 로고 압축 (.95rem)
                            · 헤더 높이 축소 (68 → 56px)
                            · 좌우 패딩 축소
                          ▣ 마이페이지
                            · 사이드바 → 상단 카드로 (1열 변환)
                            · 통계 카드 4개 → 2x2 그리드
                            · 학습/수료증 카드 1열 표시
                          ▣ 배너 슬라이드
                            · 높이 축소 (500 → 320px)
                            · 제목/설명 폰트 압축
                            · 통계 표시 컴팩트화
                          ▣ 카드 그리드
                            · 모든 그리드 1열 (cg, cg-h, nc-grid)
                            · 가로 카드(cch) → 세로 카드 변환
                            · 썸네일 16:9 비율로 변환
                          ▣ 게시판 테이블
                            · 카드형으로 변환 (가로 스크롤 X)
                            · 헤더 숨김
                            · 모바일 최적화 카드 디자인
                          ▣ 폰트 크기
                            · html base: 16px → 14px
                            · body line-height: 1.55
                            · 제목 크기 자동 축소
                          ▣ 모달
                            · 좌우 1.5rem 마진
                            · 패딩 컴팩트
                        ▶ 작은 모바일 (480px 이하) 추가:
                          · html base: 13.5px
                          · 헤더 더 컴팩트 (50px)
                          · 섹션 패딩 추가 축소
                        ▶ PC 영향 없음:
                          · 모든 변경은 max-width 미디어 쿼리 내부
                          · 769px 이상 화면은 기존 그대로
  v68.61 (2026-05-06) - PPT 3-1 수정사항 (3개 항목 일괄):
                        ▶ #1 수강신청 카드 - 글씨 밀림 방지
                          · cc-summary에 min-height: 2.34rem 추가
                          · 1줄/2줄 summary 모두 같은 영역 차지
                          · 카드 높이 들쑥날쑥 문제 해결
                        ▶ #2 인기과정 썸네일 - 글씨 밀림 방지
                          · 가로 카드(.cch)에서 메타 정보 라인 수 통일
                          · 강사/신청기간/개폐강 확정일 중 누락된 항목은
                            "투명한 빈 줄"로 자리 차지 (visibility:hidden)
                          · 모든 카드의 메타 영역 높이가 동일해짐
                          · 결과: 카드 높이 일정, 텍스트 밀림 없음
                        ▶ #3 수료증 빨간 숫자 - 확인하면 사라지게
                          · 변경 전: 발급된 수료증 개수 항상 표시
                          · 변경 후: 미확인 수료증만 빨간 배지로 표시
                          · user_meta '_esg_certs_last_seen' 으로 마지막 확인 추적
                          · 신청내역 #4(v68.47) 패턴과 동일
                          · certs 탭 진입 → 즉시 확인 처리 → 배지 사라짐
                          · "보기/인쇄" 누르려면 어차피 certs 탭 들어가야 하므로
                            진입 시점에 확인 처리되는 것과 동일 효과
  v68.60 (2026-05-06) - 대시보드 막대그래프 표시 수정:
                        ▶ 사용자 보고: 막대그래프가 안 보이고 가로선만 표시됨
                          숫자만 위에 떠있고 색깔 막대가 안 그려짐
                        ▶ 원인 1 — CSS height:% 적용 안 됨:
                          · 자식 div에 height:100% 누락
                          · 부모(140px) 안에서 자식 컨테이너 높이가 결정 안 됨
                          · → 막대(width:100%;height:5%)가 시각적으로 안 보임
                        ▶ 원인 2 — 비율 보정 없음:
                          · 가장 큰 값(33)과 작은 값(1)의 비율 차이가 너무 큼
                          · 작은 값은 약 3% 높이 → 거의 보이지 않음
                          · min-height:2px만으로는 부족
                        ▶ 수정:
                          · 자식 div에 height:100% + justify-content:flex-end
                          · 데이터 있는 경우 최소 막대 높이 8% 보장
                          · min-height: 6px (데이터 있을 때) / 1px (없을 때)
                          · 0건인 날짜는 회색으로 표시 (시각적 구분)
                          · 숫자는 데이터 있을 때만 표시 (0 숨김)
                        ▶ 결과:
                          · 모든 날짜의 막대가 정상 표시
                          · 작은 값도 시각적으로 인식 가능
                          · 0건 날짜는 회색 막대로 명확히 구분
  v68.59 (2026-05-06) - 학습현황 + 학습목록 완전 통합 (사용자 제안):
                        ▶ 사용자 분석:
                          ① 학습현황과 학습목록 내용이 중복 → 하나만 남기자
                          ② 학습신청중 카드 누르면 신청중 내용이 바로 보이게
                          ③ 학습현황만 남기고 학습목록 섹션 사라져도 OK
                        ▶ 변경 내용:
                          ① 통계카드 클릭 → 필터 적용 + 같은 페이지에 결과 표시
                            · 클릭한 카드는 active 표시 (테두리 + 살짝 위로)
                            · "학습 목록 — ✏️ 학습중 (3개)" 같이 명확한 라벨
                          ② "최근 수강 과정" → "📚 학습 목록" (동적)
                            · 기본: 학습중 과정 표시
                            · 학습중 클릭: 학습중 과정 표시
                            · 학습신청중 클릭: 학습신청중 과정 표시
                            · 학습완료 클릭: 학습완료 과정 표시
                            · 학습취소 클릭: 학습취소 과정 표시
                            · 한 페이지에서 모든 필터 결과 확인 가능
                          ③ "↩ 기본 보기로" 링크 (필터 적용 시 표시)
                          ④ 학습목록 섹션 (my-list) 완전 숨김
                            · 코드는 보존 (?tab=list 호환성)
                            · 화면에는 표시 안 됨
                          ⑤ "전체 학습목록 보기" 버튼 제거
                            · 더 이상 필요 없음 (필터로 모두 표시)
                          ⑥ JS showTab() 단순화
                            · enroll + list 통합 처리 제거
                            · 단순 1:1 패널 매칭으로 회복
                        ▶ 결과:
                          · 한 페이지에 통계 + 학습 목록 모두
                          · 클릭 한 번으로 필터 결과 즉시 확인
                          · 중복 섹션 없음 (UI 단순화)
                          · 페이지 길이 짧아짐
  v68.58 (2026-05-06) - PPT 수정사항 6 — 3개 항목 일괄 반영:
                        ▶ #1 슬라이드 배너:
                          ① 텍스트+이미지 모드 설정창 — 이미 모든 슬라이드에 동일 적용
                          ② 이미지만 모드 설정창 — 이미 모든 슬라이드에 동일 적용
                          ③ 이미지 클릭 시 URL 이동 안 됨 → 수정 ✓
                            · index.php hero-slide div에 onclick 추가
                            · image_only 모드 + img_url 있을 때만 활성화
                            · cursor:pointer 표시
                        ▶ #2 관리자 대시보드 - 총 수강신청 0 표시 버그:
                          · 원인: get_option('esangedu_total_enrollments', 0) 캐시값
                            업데이트 누락 케이스
                          · 수정: get_option('esangedu_enrollments', [])에서
                            실시간 count()로 표시
                          · 캐시 옵션도 동기화 (다른 사용처 호환)
                        ▶ #3 통계분석 누적 매출 - 확정 + 수료완료만:
                          · 변경 전: 수강확정/승인완료/입금확인 (입금확인까지 포함)
                          · 변경 후: 수강확정/승인완료/수료/완료/학습완료
                          · 입금확인은 아직 수강 미확정 상태이므로 매출에서 제외
                          · 카드 설명: "확정된 수강만 집계" → "수강확정 + 수료완료 금액 집계"
  v68.57 (2026-05-06) - 마이페이지 UI 정리:
                        ▶ 사용자 보고:
                          ① 헤더 "🛒 장바구니" 메뉴 — 더 이상 필요 없음
                          ② 나의학습 페이지 학습목록이 어색하게 분리되어 보임
                        ▶ 수정 ① — 헤더 장바구니 제거
                          · header.php h-cart-lnk 영역 제거
                          · "나의 강의실 → 로그아웃" 깔끔하게 표시
                        ▶ 수정 ② — 학습목록 섹션 시각적 연결
                          · 두꺼운 border-top: 2px → 부드러운 그라데이션 라인
                          · 좌우 10% 페이드 처리로 자연스럽게 끝남
                          · 섹션 타이틀에 "(전체 신청 내역)" 부제 추가 (역할 명확화)
                          · "최근 수강 과정"과 "학습목록"의 흐름이 자연스러움
  v68.56 (2026-05-06) - 마이페이지 메뉴 정리 (B안 — 단계적 통합):
                        ▶ 사용자 의견: 학습현황과 학습목록이 같아 보임,
                          장바구니는 거의 안 씀, 신청내역은 결제이력으로 분리
                        ▶ 변경 내용:
                          ① 학습현황 + 학습목록 → "📝 나의 학습"으로 통합
                            · 위쪽: 통계 카드 + 최근 수강 과정 (기존 학습현황)
                            · 아래쪽: 학습목록 + 필터/정렬 (기존 학습목록)
                            · 한 페이지에서 모두 확인 가능
                          ② 🛒 장바구니 메뉴 제거 (사이드바)
                            · 패널은 코드 보존 (?tab=cart 접속 시 자동 enroll로 리다이렉트)
                            · 장바구니 자체 기능은 그대로 (담기/제거 등)
                          ③ 💳 신청내역 → "결제 내역" 그룹으로 명확화
                            · 결제 상태 추적 전용 (입금대기/확인/확정)
                          ④ 메뉴 그룹 라벨: "나의 주문" → "💳 결제 내역"
                        ▶ URL 호환성:
                          · ?tab=list 접속 시 자동으로 ?tab=enroll로 리다이렉트
                          · ?tab=list&f=studying 같은 필터 URL도 작동
                          · ?tab=cart 접속 시 enroll로 리다이렉트
                          · 기존 북마크/링크 깨지지 않음
                        ▶ "전체 학습목록 보기" 버튼:
                          · 더 이상 페이지 이동 X
                          · 같은 페이지 아래 학습목록으로 부드럽게 스크롤
                        ▶ 메뉴 구조 (Before → After):
                          [Before] 학습현황 / 학습목록 / 수료증 / 장바구니 /
                                   신청내역 / 찜한 학습 / 개인정보 / 비밀번호 / 문의
                          [After]  나의 학습 / 수료증 / 신청 내역 / 찜한 학습 /
                                   개인정보 / 비밀번호 / 문의
                          (8개 → 7개로 감소, 중복 제거)
  v68.55 (2026-05-06) - 자료실 비밀번호 입력 버튼 깨짐 수정:
                        ▶ 사용자 보고: "🔒 비밀번호 입력" 버튼이 2줄로 깨짐
                          (자물쇠 이모지 따로, 텍스트 따로)
                        ▶ 원인:
                          · 파일 컬럼 너비 130px이 한국어 + 이모지에 부족
                          · display:inline-flex만 있고 white-space:nowrap 없음
                          · 좁은 컬럼에서 줄바꿈 발생
                        ▶ 수정:
                          · 파일 컬럼 너비 130px → 160px (한 줄 확보)
                          · 버튼에 white-space:nowrap 추가 (줄바꿈 방지)
                          · align-items:center + gap:.25rem (이모지와 텍스트 정렬)
                          · 다운로드 버튼에도 동일 적용 (일관성)
                        ▶ 결과:
                          · 비밀번호 입력 버튼 한 줄 표시 ✓
                          · 좁은 화면에서도 깨짐 없음
  v68.54 (2026-05-06) - 자료실 단일 페이지 비밀번호 보호 (Critical):
                        ▶ 사용자 보고: 비밀번호 입력 누르면 그냥 다운로드됨
                        ▶ 원인:
                          · 자료실 글 클릭 시 single.php 글 본문 페이지 진입
                          · single.php 본문에 첨부파일 다운로드 버튼이
                            비밀번호 검증 없이 노출됨
                          · 사용자가 글 본문에서 그냥 다운로드 가능했던 보안 구멍
                        ▶ 수정 (single.php):
                          · 자료실 글이고 비밀번호 보호 시:
                            · 첨부파일 영역 색상 변경 (파란색 → 노란색)
                            · 아이콘 변경 (📎 → 🔒)
                            · "🔐 비밀번호 보호 자료입니다" 문구
                            · 다운로드 링크 → "🔒 비밀번호 입력" 버튼 (주황)
                          · 비밀번호 모달 + JS 추가
                          · 동일한 esg_pds_verify_pw_handler 사용
                        ▶ 결과:
                          · 자료실 목록에서 다운로드 시도 → 비밀번호 필요
                          · 자료실 글 본문에서 다운로드 시도 → 비밀번호 필요
                          · 모든 경로에서 일관되게 보호됨
                        ▶ 관리자 디버그:
                          · 비밀번호 보호 자료의 file_url은 클라이언트로 노출 안 함
                          · 검증 통과 후 AJAX 응답으로만 URL 전달
                          · 단, file_url 자체가 wp-content/uploads/ 경로면
                            URL을 알면 우회 가능 (근본 보안은 아님)
                          · 보다 강한 보안이 필요하면 .htaccess 또는
                            PHP proxy download가 필요
  v68.53 (2026-05-06) - 자료실 비밀번호 보호 기능:
                        ▶ 사용자 요청: 자료실 자료에 비밀번호 걸어 아무나 다운로드 못하게
                        ▶ 관리자 — 자료 등록/수정 화면
                          · 발행 설정 박스 위에 "🔒 비밀번호 보호" 박스 추가
                          · 비밀번호 입력란 (선택 사항)
                          · 비워두면 누구나 다운로드 (기존 동작 유지)
                          · 입력 시: 사용자가 다운로드 전 비밀번호 입력 필요
                          · 현재 상태 표시 (🔐 설정됨 / 🔓 없음)
                          · 비밀번호는 wp_hash_password()로 해싱하여 저장
                        ▶ 사용자 — 자료실 목록
                          · 비밀번호 보호 자료는 제목 앞에 🔒 표시
                          · 다운로드 버튼이 "🔒 비밀번호 입력" (주황색)로 변경
                          · 클릭 시 비밀번호 입력 모달 표시
                        ▶ 비밀번호 입력 모달
                          · 자료 제목 + 비밀번호 입력 필드
                          · ✅ 인증 성공 시 자동 다운로드 시작
                          · ❌ 실패 시 빨간 메시지 + 입력란 자동 선택
                          · ESC로 닫기, 모달 배경 클릭으로 닫기
                          · Enter 키로 제출
                        ▶ AJAX 검증 (esg_pds_verify_pw_handler)
                          · 로그인/비로그인 모두 가능 (wp_ajax_nopriv)
                          · wp_check_password() 로 안전한 검증
                          · 성공 시 파일 URL + 파일명 반환 → 자동 다운로드
                          · 실패 시 명확한 오류 메시지
                        ▶ 보안:
                          · 비밀번호는 평문이 아닌 해시로만 저장
                          · nonce 검증
                          · post_type + board_type 이중 검증
  v68.52 (2026-05-06) - 수료증 자동 발급 시스템:
                        ▶ 사용자 보고: 교육 기간 지나도 수료증이 자동으로 안 뜸
                        ▶ 원인:
                          · esg_is_cert_eligible() 함수는 자격 검사만 함
                          · esg_issue_certificate()는 관리자 수동 호출만 처리
                          · 자동 발급 트리거 부재
                        ▶ 신규 함수 esg_auto_issue_user_certificates($user_id)
                          · 사용자의 모든 신청 순회
                          · 자격 충족 (수강확정 + 종료일 지남) 신청 자동 발급
                          · 일괄 처리 (1번의 update_option으로 효율적)
                          · cert_issuer = 'auto' 로 자동 발급 표시
                        ▶ 호출 시점: 마이페이지 진입 시
                          · 사용자가 마이페이지 어떤 탭에 들어가도 자동 검사
                          · 즉, 교육 종료 후 마이페이지 첫 방문 시점에 자동 발급
                        ▶ 자동 발급 조건 (변경 없음, 기존 esg_is_cert_eligible)
                          · 상태: 수강확정 / 승인완료 / 입금확인 / 학습중 / 수강중
                          · _course_end_date < 현재 시각
                          · cert_issued 가 아직 false
                          · 상태 '취소' 또는 '학습취소' 아님
                        ▶ 결과:
                          · 교육 종료된 과정 → 마이페이지 진입 시 자동 발급
                          · 수료증 탭에 즉시 표시됨
                          · 수료증 PDF 다운로드 가능
  v68.51 (2026-05-06) - 게시판 페이지네이션 (커뮤니티 4개 탭 모두):
                        ▶ 사용자 보고: 글이 많아지면 한 페이지에 다 표시되어
                          화면이 길어짐. 1, 2, 3 페이지로 나뉘어야 함
                        ▶ 적용 게시판 (모두 페이지당 10개):
                          · 📢 공지사항 (중요 공지는 항상 상단 고정 + 일반 공지만 페이징)
                          · ❓ FAQ
                          · 💬 문의게시판
                          · 📂 자료실
                        ▶ 페이지네이션 디자인:
                          · ‹ 이전 | 1 2 [3] 4 5 ... 10 | 다음 ›
                          · 현재 페이지: 파란색 강조
                          · 이전/다음 비활성화 시 회색 처리
                          · 5페이지 이상 시 ... 줄임 표시
                          · hover 시 색상 전환 효과
                        ▶ URL: ?tab=notice&cpage=2 형식
                        ▶ 게시글 번호:
                          · 페이지에 따라 번호 자동 계산
                          · 예: 30개 글, 페이지 2 → 20부터 11까지
                        ▶ esg_render_pagination() 헬퍼 함수로 재사용
  v68.50 (2026-05-06) - 수료증 패널 누적 버그 완전 해결 (Critical Fix):
                        ▶ 사용자 보고 (정확한 증상):
                          · 수료증 → 신청내역 클릭 시 두 패널이 동시에 표시
                          · 새로고침하면 수료증 빈 화면
                          · 또 새로고침하면 정상
                          · 다른 탭 클릭하면 또 누적 표시
                        ▶ 정확한 원인 발견:
                          · page-mypage.php의 JS showTab() 함수 panels 배열에
                            'certs' 누락
                          · ['enroll','list','wish','profile','password','qna','orders','cart']
                          · → 'certs' 패널은 JS가 hide/show 처리 안 함
                          · → 다른 탭 클릭 시 certs 패널 그대로 남고 추가 패널 표시
                          · → 새로고침 시 PHP는 정상 처리 (URL 기반)
                          · → 다시 certs 클릭 시 JS 처리 안 됨 → 다른 패널 안 숨김
                            + certs 패널 안 보임 → 빈 화면
                        ▶ 수정:
                          · panels 배열에 'certs' 추가 (v68.48 valid_tabs와 동일)
                          · ['enroll','list','wish','profile','password','qna','orders','cart','certs']
                        ▶ 정리:
                          · v68.49 디버그 박스 제거 (원인 파악 완료)
                          · 안전 처리($__my_certs 재초기화)는 유지
                        ▶ 결과:
                          · 모든 탭 전환 정상 동작
                          · 패널 누적 문제 완전 해결
                          · 빈 화면 문제 완전 해결
  v68.49 (2026-05-06) - 수료증 빈 화면 추가 진단 + 안전 처리:
                        ▶ v68.48 valid_tabs 수정 후에도 빈 화면 발생
                        ▶ 안전 처리:
                          · 패널 시점에서 $__my_certs 재초기화 (사이드바 변수 영향 차단)
                          · 함수 존재 여부 안전 확인
                        ▶ 관리자 디버그 정보 추가 (관리자에게만 표시)
                          · 현재 탭, 사용자 ID, 수료증 수, 함수 존재 여부
                          · 빈 화면 원인 파악용 (캐시/코드/데이터 중 무엇인지)
                        ▶ 사용자 행동 가이드:
                          1. 브라우저 캐시 강력 새로고침 (Ctrl+Shift+R)
                          2. 관리자 계정으로 마이페이지 → 수료증 진입
                          3. 노란색 디버그 정보 박스 확인하여 원인 보고
  v68.48 (2026-05-06) - 수료증 탭 빈 화면 버그 수정 (Critical):
                        ▶ 사용자 보고: 마이페이지 → 수료증 클릭 시 완전 빈 화면
                        ▶ 원인 발견:
                          · page-mypage.php의 $valid_tabs 배열에 'certs'가 누락
                          · ['enroll','list','wish','profile','password','qna','orders','cart']
                          · 'certs' 누락 → ?tab=certs 접속 시 유효하지 않다 판단
                          · → $cur_tab을 'enroll'로 강제 변경
                          · → 모든 패널이 display:none (enroll 패널은 화면 안 보일 수 있음)
                          · → 사이드바는 certs를 active 표시하지만 본문은 비어있음
                        ▶ 수정:
                          · $valid_tabs 배열에 'certs' 추가
                          · 9개 탭(enroll/list/wish/profile/password/qna/orders/cart/certs)
                            모두 정상 동작 확인
                        ▶ 결과:
                          · 수료증 탭 정상 표시
                          · 발급된 수료증 있으면 → 카드 그리드
                          · 없으면 → v68.47에서 만든 친절한 빈 상태 (수강 중 과정 표시)
  v68.47 (2026-05-06) - PPT 수정사항 6개 항목 일괄 반영:
                        ▶ #1 자주 묻는 질문 "더보기"
                          · 메인 → community?tab=faq 로 직접 이동
                          · 기존: /community (전체 커뮤니티)
                          · 변경: /community?tab=faq (FAQ 탭으로 바로)
                        ▶ #2 오시는 길 — 이미 편집 가능
                          · 관리자 → ⚙️ 사이트설정 → 📄 페이지 → 오시는길
                          · 모든 본문 텍스트, 주소, 연락처, 운영시간, 지도 편집 가능
                        ▶ #3 수료증 탭 빈 화면 개선
                          · 친절한 안내 + 현재 수강 중인 과정 목록 표시
                          · "교육이 끝나면 수료증 자동 발급" 안내
                          · 수강 중 과정 없으면 → "수강 가능한 과정 보기" 버튼
                        ▶ #4 신청 내역 옆 숫자 — 확인하면 사라지게
                          · 변경 전: 전체 신청 개수 표시 (계속 표시)
                          · 변경 후: 마지막 확인 시각 이후 미확인 신청만 카운트
                          · user_meta '_esg_orders_last_seen' 으로 추적
                          · orders 탭 진입 즉시 확인 처리 (배지 사라짐)
                        ▶ #5 자료실 클릭 → 자료실 페이지
                          · cards_3col 레이아웃 카드를 클릭 가능하게 (a 태그)
                          · card{N}_url 명시 + 자동 매핑 (제목 키워드)
                          · 자동 매핑: 자료실/FAQ/공지사항/커뮤니티/문의/강사/오시는길
                          · footer.php 자료실 링크도 ?tab=pds 추가
                        ▶ #6 강사 이름 표시 (썸네일에 ID 숫자가 아닌 이름)
                          · esg_course_instructor_name() 헬퍼 함수 적용 확대
                          · 적용 위치:
                            · 관리자 강좌 목록 (functions.php:5344)
                            · esangedu_course_card 함수 (functions.php:12904)
                            · 마이페이지 찜 카드 (page-mypage.php:648)
                            · 교육일정 (page-schedule.php:25)
                          · 강사 CPT ID(숫자) → 강사명으로 자동 변환
                          · 텍스트 직접 입력도 안전하게 처리
  v68.46 (2026-05-06) - 챗봇 관리자 설정 페이지 + 관리자 화면 차단:
                        ▶ 사용자 질문 1: 관리자 페이지에 챗봇 보이는지
                          · 답: 안 보이도록 처리 (!is_admin() 조건 추가)
                          · 어드민에서는 챗봇 위젯 표시 안 됨
                        ▶ 사용자 질문 2: 챗봇 내용 관리자가 직접 편집 가능한지
                          · 답: 전용 설정 페이지 추가
                          · 경로: 관리자 → 💬 고객응대 → 🤖 챗봇 설정
                        ▶ 챗봇 설정 페이지 기능
                          ① 기본 설정
                            · 챗봇 활성화 ON/OFF
                            · 챗봇 제목 (헤더 표시명)
                            · 환영 메시지 (첫 인사말 직접 편집)
                            · FAQ 자동 검색 활용 여부
                          ② 빠른 답변 버튼 관리
                            · 환영 메시지 아래 표시되는 버튼들 직접 등록
                            · 버튼 텍스트 + 자동 답변 + 자세히 링크
                            · 추가/삭제 자유롭게
                            · 등록 안 하면 인기 FAQ 자동 표시
                          ③ 챗봇 전용 Q&A 관리
                            · FAQ 게시판과 별개로 챗봇 전용 답변
                            · 사용자 입력 키워드와 매칭되어 답변
                          ④ 미리보기 링크
                            · 저장 후 사이트로 바로 이동해 확인
                        ▶ 신규 옵션
                          · esg_chatbot_enabled, esg_chatbot_title
                          · esg_chatbot_welcome, esg_chatbot_use_faq
                          · esg_chatbot_quick_buttons (배열)
                          · esg_chatbot_qa (배열)
  v68.45 (2026-05-06) - 혼합형 챗봇 위젯 (FAQ 봇 + 1:1 문의 전환):
                        ▶ 사용자 요청: 고객센터 버튼 → 실시간 채팅 도우미
                        ▶ 동작 흐름
                          · 우측 퀵메뉴의 💬 고객센터 클릭
                          · 챗봇 창 슬라이드 오픈 (380×560px, 우측 하단)
                          · 환영 메시지 + 인기 FAQ 5개 빠른 답변 버튼
                          · 사용자 입력 → FAQ 자동 검색 (키워드 매칭 + 점수)
                            · 결과 1개: 즉시 답변 + 만족도 체크
                            · 결과 여러개: 가장 가까운 질문 선택 유도
                            · 결과 없음: 1:1 문의 전환 안내
                          · 1:1 문의 전환 시 단계별 폼
                            · 비로그인: 이름 → 이메일 → 제목 → 내용
                            · 로그인: 제목 → 내용 (정보 자동)
                          · 접수 완료 → 마이페이지 링크 안내
                        ▶ FAQ 검색 알고리즘
                          · 제목 매칭 가중치 3점, 본문 매칭 1점
                          · 전체 문구 일치 시 +5점 보너스
                          · 점수 높은 순 상위 3개 표시
                          · 등록된 FAQ 50개까지 검색
                        ▶ UI 디테일
                          · 부드러운 슬라이드 애니메이션
                          · 타이핑 인디케이터 (...)
                          · 카톡 스타일 말풍선 (사용자/봇 구분 색상)
                          · 빠른 답변 버튼 (둥근 pill 모양)
                          · ESC로 닫기
                          · 모바일 풀스크린 모드
                        ▶ 1:1 문의 전환
                          · 비로그인: esangedu_inquiry 액션 (post로 저장)
                          · 로그인: esg_inquiry_submit (게시판 통합)
                          · 챗봇에서 검색한 키워드도 본문에 포함
                          · 모든 문의는 관리자 → 고객응대 → 문의 답변에 표시
  v68.44 (2026-05-06) - 카드 썸네일 비율 강제 고정 (v68.43에서 미해결):
                        ▶ 문제 1 (세로 카드)
                          · 이미지 있는 카드와 없는 카드의 썸네일 크기가 여전히 다름
                          · 원인: 다른 CSS가 cc-thumb 스타일을 덮어씀
                          · 해결: !important + position absolute로 강제 고정
                        ▶ 문제 2 (가로 카드)
                          · "AI 일러스트" 카드는 썸네일 위/아래 빈 영역
                          · "포토샵 AI" 카드는 본문 짧아서 카드 자체가 작음
                          · 원인: align-self:start만으로는 카드 높이 차이 못 막음
                          · 해결:
                            · 카드 자체 height: 210px 고정
                            · 썸네일 width 240px / height 180px 고정 (!important)
                            · align-self: center로 위/아래 같은 여백
                        ▶ 결과
                          · 모든 세로 카드 썸네일 동일 크기 (4:3)
                          · 모든 가로 카드 동일 높이 + 썸네일 중앙 정렬
  v68.43 (2026-05-06) - 카드 썸네일 4:3 비율 정확히 수정:
                        ▶ 문제 1 (세로 카드, 신규/인기 과정)
                          · 이미지 없는 카드(sdf, df)와 있는 카드의 썸네일 높이가
                            서로 달라 보이는 문제
                          · 원인: 이미지가 부모 박스를 완전히 채우지 못함
                          · 수정: img에 position:absolute + inset:0 적용
                            → 어떤 비율의 이미지든 4:3 박스에 정확히 맞춤
                        ▶ 문제 2 (가로 카드, 인기 과정)
                          · 일부 카드("포토샵 AI" 등) 썸네일 위/아래에 빈 공간 발생
                          · 원인: .cch에 min-height: 200px이 설정돼있고,
                            썸네일은 240×180 (4:3)이라 본문이 짧으면
                            min-height에 맞춰 카드가 늘어나면서 썸네일 위아래 공백
                          · 수정: min-height 제거 + align-items:stretch +
                            썸네일 align-self:start
                            → 썸네일은 정확히 4:3, 카드 높이는 본문에 따라 자연 결정
                        ▶ 결과
                          · 모든 카드의 썸네일이 정확히 4:3 비율
                          · 이미지 유무와 무관하게 동일한 박스 크기
                          · 가로 카드의 빈 영역 완전 제거
  v68.42 (2026-05-06) - 1:1 문의를 워드프레스 댓글이 아닌 게시판 시스템으로 통합:
                        ▶ 문제 원인:
                          · 비로그인 사용자의 1:1 문의가 wp_insert_comment로 저장되어
                            워드프레스 기본 Comments 페이지로 분산됨
                          · 관리자가 두 곳에서 문의를 관리해야 함 (혼란)
                        ▶ 변경: 모든 1:1 문의를 post 게시판으로 통일
                          · esangedu_handle_inquiry: wp_insert_comment → wp_insert_post
                          · _esg_board_type='inquiry' 메타로 분류
                          · 비로그인 문의도 자동으로 비공개 처리 (개인정보 보호)
                          · _esg_guest_name, _esg_guest_email 메타로 작성자 추적
                        ▶ 레거시 데이터 마이그레이션 도구
                          · 관리자 → 고객응대 → 💬 문의 답변
                          · 페이지 상단에 알림 배너 + "🔄 N건 이전하기" 버튼
                          · 클릭 시 모든 esangedu_inquiry 댓글 → 1:1 문의로 이전
                          · 댓글 본문의 [제목: ...] 자동 파싱
                          · 작성자 이름/이메일 메타로 보존
                          · 원본 댓글은 휴지통으로 이동 (완전 삭제 X)
                          · _esg_legacy_comment_id로 원본 추적
                        ▶ 워드프레스 Comments 페이지 정리
                          · comments_clauses 필터로 esangedu_inquiry 타입 숨김
                          · 어드민 상단 댓글 카운트(빨간 배지)에서도 제외
                          · 결과: WP Comments는 일반 댓글만, 1:1 문의는 자체 페이지
                        ▶ 결과: 모든 1:1 문의가 한 곳에서 통합 관리됨
                          · 마이페이지에서 작성 → 관리자 → 고객응대 → 문의 답변
                          · 헤더 모달에서 작성 → 관리자 → 고객응대 → 문의 답변
                          · 비로그인이 작성 → 관리자 → 고객응대 → 문의 답변
  v68.41 (2026-05-06) - PPT5 수정사항 정리 (9개 항목 일괄 반영):
                        ▶ #1 관리자 대시보드 — 최근 수강신청 안 뜨는 문제
                          · 원인: 'enrollment' CPT를 조회했는데 실제 데이터는
                            'esangedu_enrollments' 옵션 배열에 저장됨
                          · 수정: get_option('esangedu_enrollments') 사용
                          · 표시 로직도 배열 키(name/course/status/date)로 변경
                          · 상태별 색상도 수강신청 현황 페이지와 통일
                        ▶ #2 수강 신청 현황 - 상태별 버튼 정확히 분리
                          · 입금대기 → ✅ 입금확인만 (수강확정/취소 제거)
                          · 입금확인 → 🎓 수강확정만 (취소 제거)
                          · 수강확정 → ❌ 취소만
                          · 취소     → 버튼 없음 ('—' 표시)
                        ▶ #3-4 썸네일 4:3 비율로 변경
                          · 세로 카드 .cc-thumb: 400/250 → 4/3
                          · 가로 카드 .cch-thumb: aspect-ratio 4/3 적용
                          · 가로 카드 그리드: 220px → 240px (4:3 = 240×180)
                          · object-position: center로 우측 빈 영역 방지
                          · width: 100% 명시
                        ▶ #5-6 카드 텍스트 위치 라인 정렬 (실제 동작 보장)
                          · .cc-cat-empty가 height:0이라 라인 정렬 깨지던 문제
                          · 수정: min-height 추가하여 빈 카테고리도 자리 차지
                          · 모든 카드 카테고리/요약/메타가 같은 라인에 위치
                        ▶ #7 1:1 문의 → 마이페이지 연동 (실제 동작 보장)
                          · 원인: header.php의 모달이 호출하는 submitInquiry()
                            함수가 정의되지 않아 동작하지 않았음
                          · 수정: header.php에 submitInquiry() 함수 정의
                          · 로그인 사용자: 게시판(esg_inquiry_submit)으로 전송
                            → 마이페이지 → 나의 문의에서 답변 확인 가능
                          · 비로그인: 기존 esangedu_inquiry 액션 (관리자 메일)
                          · 모달 안에 안내문구 추가 + 결과 표시
                        ▶ #8 슬라이드 텍스트+이미지 모드 — 모든 설정 유지 ✓
                          · v68.32에서 이미 처리됨 (변경 없음)
                        ▶ #9 슬라이드 이미지만 모드 — UI 단순화 (재구성)
                          · 신규 영역 추가: '이미지만 모드 설정'
                          · 좌측: 클릭 시 이동 URL 입력 + 빠른 선택 버튼
                          · 우측: 이미지 선택 (16:9 미리보기)
                          · 신규 메타: img_url (클릭 시 이동 URL)
                          · 모드 토글 시 esg-slide-imageonly-fields도 함께 처리
  v68.40 (2026-05-06) - 페이지 헤더 영문 부제 제거:
                        ▶ 수강신청 페이지: "COURSES" 라벨 숨김
                        ▶ 교육일정 페이지: "SCHEDULE" 라벨 숨김
                        ▶ 결과: 한글 타이틀과 부제만 남아 더 깔끔함
  v68.39 (2026-05-06) - 빵부스러기(breadcrumb) 일괄 숨김:
                        ▶ 사용자 요청: 일반 수강생은 "홈 › 수강신청" 같은 경로 안내 불필요
                        ▶ CSS로 일괄 숨김 처리 (모든 페이지 적용)
                          · .breadcrumb (about, community, instructors, location, privacy, terms, single)
                          · .esg-breadcrumb (single-course, single-instructor)
                          · .esg-page-breadcrumb (page-courses, page-courses 신규 클래스)
                          · .cp-breadcrumb (page-custom)
                        ▶ 적용 페이지: 수강신청, 교육일정, 강사소개, 안내 등 거의 모든 페이지
                        ▶ 디자인 일관성: 모든 페이지 헤더가 깔끔한 단일 타이틀 형태
  v68.38 (2026-05-06) - 교육일정 레이아웃 재배치:
                        ▶ 월/년 네비게이션 위치 변경
                          · 화면 전체 중앙 → 달력 카드 안 상단 중앙
                          · 달력 그리드의 진짜 중앙에 표시됨
                          · 달력 카드와 시각적으로 일체화
                        ▶ 범례(대면/비대면) 위치 변경
                          · 상단 → 우측 패널 위로 이동
                          · 별도 카드로 표시되어 우측 영역 일관성 향상
                          · 안내문구(※ 교육 시작일 기준)도 함께 이동
                        ▶ 결과: 달력은 좌측에서 자체 중앙 정렬, 범례는 우측 첫 항목
  v68.37 (2026-05-06) - 교육일정 카드에서 가격 표시 제거:
                        ▶ 사용자 요청에 따라 우측 패널의 일정 카드에서 가격 숨김
                        ▶ 표시 항목: 과정명 / 기간 / 대면·비대면 / 난이도 / 회차
                        ▶ 가격은 과정 상세 페이지에서 확인하도록 유도
  v68.36 (2026-05-06) - 교육일정 상단바 정리:
                        ▶ 월/년 네비게이션 중앙 정렬 (좌측 정렬 → 중앙)
                        ▶ "오늘" 버튼 제거 (사용자 요청)
                        ▶ 월 표시 옆 화살표(▾) 아이콘 제거 (드롭다운 미구현이라 혼란)
                        ▶ 결과: 좌측 ‹ + 중앙 "2026년 5월" + 우측 › 만 표시
                        ▶ 범례(대면/비대면/안내문구)는 아래 줄에 중앙 정렬
  v68.35 (2026-05-06) - 교육일정 인터랙티브 캘린더 (page-schedule.php 재작성):
                        ▶ 문제: 기존 캘린더는 셀당 3개 정도 일정만 표시되고
                          더 많은 과정이 등록되면 보이지 않음
                        ▶ 해결: 좌측 달력 + 우측 동적 패널 구조
                        ▶ 새로운 기능
                          · 달력 날짜 클릭 시 → 우측에 그날의 모든 일정 표시
                            (3개 제한 X, 모든 강의 표시)
                          · 선택된 날짜 강조 (네이비 원 표시)
                          · 각 날짜 셀에는 dot만 표시 (대면=초록 / 비대면=주황)
                          · 페이지 로드 시 자동으로 오늘 날짜 선택
                        ▶ UI 디테일
                          · 우측 카드에 과정명/기간/유형/회차/가격 모두 표시
                          · 카드 클릭 → 과정 상세 페이지로 이동
                          · 사이드 카드는 sticky로 스크롤 시 따라옴
                          · 커스텀 스크롤바 (max-height 560px)
                          · 모바일: 캘린더 위 + 패널 아래 (1열)
                        ▶ 데이터 처리
                          · _course_session_dates → 회차별 날짜 모두 인덱싱
                          · _course_weekdays + 시작/종료일 fallback 계산
                          · 신규 메타: _course_class_type (online/offline)
                            없으면 제목 기반 자동 판단
                          · events_by_date JSON으로 직렬화하여 클라이언트 렌더링
  v68.34 (2026-05-06) - 마이페이지 문의 모달에 비공개 체크박스 추가:
                        ▶ 마이페이지 → 나의 문의 → 문의하기 모달
                          · 비공개 체크박스 누락돼있던 문제
                          · 커뮤니티 모달과 동일한 비공개 옵션 추가
                        ▶ 추가된 기능
                          · 개인정보 보호 안내 박스 (노란색)
                          · 🔒 비공개로 문의 체크박스
                          · 모달 열 때 체크박스 자동 초기화
                          · POST 시 private=1/0 함께 전송
                          · 서버측에서 _esg_inquiry_private 메타 저장 (기존 핸들러 활용)
                        ▶ 적용 결과
                          · 커뮤니티 (page-community.php): 이미 적용됨
                          · 마이페이지 (page-mypage.php): 신규 적용 ✓
                          · 고객센터 1:1 문의 (page-support.php): v68.32 #13에서 강제 비공개
  v68.33 (2026-05-06) - 과정 빈 영역 표시 토글 + 회원탈퇴/수강취소 디자인 모달:
                        ▶ 1. 과정 상세페이지 빈 영역 표시 모드
                          · 관리자: 과정 편집 → 과정 상세 정보 메타박스
                            → "🆕 빈 영역 표시" 라디오 옵션 추가
                            · "준비중입니다" 표시 (기본) — 안내문구 노출
                            · 완전히 숨김 — 내용 없으면 섹션 자체 미표시
                          · 저장 메타: _course_empty_mode (show/hide)
                          · single-course.php: 과정 요약 섹션 + 앵커 네비게이션
                            모두 모드에 따라 동작
                          · 다른 섹션(목표/대상/커리큘럼 등)은 이미 자동 숨김
                            구조라 영향 없음
                        ▶ 2. 회원 탈퇴 / 수강 취소 — 디자인 모달 UI
                          · 기존: 못생긴 브라우저 confirm() / prompt()
                          · 변경: 사이트 디자인과 일관된 커스텀 모달
                          · 수강 취소 모달
                            · 노란색 경고 아이콘 + 과정명 / 주문번호 표시
                            · "아니오" / "취소하기" 버튼
                          · 회원 탈퇴 모달 (2단계)
                            · 1단계: 빨간 경고 + 삭제 항목 리스트 표시
                              (회원 계정, 수강 내역, 수료증, 문의, 장바구니)
                            · 2단계: 비밀번호 입력 필드 (Enter 키 지원)
                            · "취소" / "탈퇴하기" 버튼
                          · 결과 알림: 토스트 (브라우저 alert 대체)
                            · 성공/오류 색상 구분, 자동 사라짐
                          · ESC 키로 모달 닫기
                          · 모달 외부 클릭 시 닫기
                          · 부드러운 페이드+스케일 애니메이션
  v68.32 (2026-05-06) - PPT 수정사항 정리 (14개 항목 일괄 반영):
                        ▶ #1 메인 배너 이미지 사이즈 변경 시 뭉개짐 수정
                          · image-rendering: crisp-edges, optimize-contrast
                          · backface-visibility: hidden 추가
                        ▶ #2 가로/세로 카드 썸네일 → 400x250 비율 통일
                          · height: 160px → aspect-ratio: 400/250
                          · 화면 크기 변경 시 비율 유지
                        ▶ #3-4 신규/인기/수강신청 카드 글씨 라인 정렬
                          · 기존 .cc-cat-empty, .cc-meta-row-empty 등 빈 영역 처리 활용
                          · grid-auto-rows: 1fr로 카드 행 높이 동일
                        ▶ #5 학습현황 페이지 '수료증' 카테고리 카드 삭제
                          · 통계 5개 → 4개 (수료증은 사이드 메뉴에 별도 존재)
                        ▶ #6 "총 N개 과정 학습중" — 학습중만 카운트
                          · count($my_enrolls) → $stat_studying
                          · 학습신청중/학습취소/학습완료 제외
                        ▶ #7 장바구니 → 수강신청 시 장바구니에서 자동 제거
                          · esg_enroll_submit_handler 처리 후 esg_cart 제거
                        ▶ #8 "주문 내역" → "신청 내역" 일괄 변경
                          · 사이드 메뉴, 탭 제목, 안내 메시지 모두 변경
                        ▶ #9 수료증 배경 이미지 반영 안 되는 현상 수정
                          · cert-overlay 투명도 .85 → .45 (배경 이미지 잘 보이게)
                          · background-color/repeat 정리
                        ▶ #10 기업문의 제출 오류 (v68.28에서 이미 완료)
                        ▶ #11-12 슬라이드 배너 모드별 입력 화면 분리
                          · '텍스트+이미지' 모드: 모든 필드 표시
                          · '이미지만' 모드: 텍스트 영역(배지/제목/설명/버튼/통계) 숨김
                            → 배경 이미지 영역만 강조 표시
                          · 모드 변경 시 esgSlideToggleMode JS로 즉시 토글
                          · '이미지만' 선택 시 안내 박스 자동 표시
                        ▶ #13 1:1 문의 → 마이페이지 연동
                          · 로그인 사용자: 게시판 시스템(esg_inquiry_submit)으로 통합
                          · 마이페이지 → 나의 문의에서 답변 확인 가능 (직링크 안내)
                          · 비로그인은 기존 esangedu_inquiry 액션 유지 + 안내
                          · 작성자 정보(이름/이메일)를 본문 끝에 자동 첨부
                          · 개인정보 포함이므로 비공개 기본
                        ▶ #14 승인된 교육 재신청 차단
                          · 신규 헬퍼: esg_user_active_enrollment_status($course_id)
                            반환: false (재신청 가능) 또는 status (차단)
                          · 서버측 esg_enroll_submit_handler에 중복 검증 추가
                          · 카드 신청 버튼: "✓ 신청완료" 비활성 표시
                          · 수강신청 페이지 카드 동일 처리
                          · 과정 상세페이지(single-course): CTA·바로신청·모바일CTA 모두 차단
                          · 취소된 신청은 재신청 가능 (의도된 동작)
                          · 수료한 신청은 재신청 가능 (다음 기수 등록 가능)
  v68.31 (2026-05-06) - 마이페이지: 수강 취소 + 회원 탈퇴 기능:
                        ▶ 수강 취소 (주문 카드 안)
                          · "❌ 수강 취소" 버튼 추가
                          · 취소/수료 상태가 아닌 신청건만 표시
                          · 본인 신청건 검증 (이메일 또는 user_id 매칭)
                          · 2단계 확인: 과정명+주문번호 표시 후 confirm
                          · 취소 시 status='취소', cancel_date, cancel_by='user' 기록
                          · 관리자에게 메일 알림 (호스팅 환경에서 작동)
                        ▶ 회원 탈퇴 (프로필 탭 하단)
                          · 빨간색 위험 영역으로 시각적 경고
                          · 본인확인 통과 시에만 표시
                          · 3단계 확인: 안내 confirm → 비밀번호 입력 → 최종 confirm
                          · 비밀번호 검증 (잘못된 비번이면 차단)
                          · 관리자 계정은 자가 탈퇴 불가 (보안)
                        ▶ 탈퇴 시 데이터 처리
                          · 회원 작성 문의 글: 완전 삭제
                          · 수강 신청 이력: 보존(통계용) + 개인정보 마스킹
                            (이름/이메일/연락처 → [탈퇴회원]) + withdrawn 플래그
                          · 본인확인 토큰 정리
                          · 관리자 메일 알림
                          · 자동 로그아웃 → 홈으로 이동
  v68.30 (2026-05-06) - 수강 신청 현황 필터 기능 추가:
                        ▶ 상태 카드 클릭으로 필터링
                          · 📋 전체 / 🟡 입금대기 / 🔵 입금확인 / 🟢 수강확정 / ❌ 취소
                          · 클릭 시 해당 상태만 표시 (선택 카드 진하게 표시)
                          · 신규: 📋 전체 카드 추가 (총 건수 표시)
                        ▶ 과정별 필터 드롭다운
                          · 모든 과정명 + 신청 건수 함께 표시
                          · 신청 많은 과정 순으로 정렬
                          · 선택 시 즉시 필터 적용 (자동 submit)
                        ▶ 검색 기능
                          · 신청자 / 이메일 / 주문번호 / 연락처 통합 검색
                          · 부분 일치 (대소문자 무관)
                        ▶ 필터 결합 가능
                          · 상태 + 과정 + 검색 동시 적용
                          · 필터 결과 건수 / 과정명 표시
                          · "✕ 필터 초기화" 버튼으로 한 번에 해제
                        ▶ 필터 결과 0건 시 친절한 안내
                          · "🔍 필터 결과가 없습니다" + 전체 보기 링크
  v68.29 (2026-05-06) - 문의게시판 비공개 보호 강화 (개인정보 보호):
                        ▶ 정책: 기본값 공개 / 사용자 선택으로 비공개 가능
                        ▶ 작성 폼 강화 (page-community.php)
                          · 개인정보 포함 시 비공개 작성 권장 안내 박스 추가
                          · 비공개 체크박스 영역 시각적 강조 (배경색)
                        ▶ 검색·아카이브·홈 쿼리에서 비공개 문의 자동 제외
                          · pre_get_posts 훅으로 메인쿼리 필터링
                          · 비로그인: 모든 비공개 글 제외
                          · 로그인 사용자: 본인 글이 아닌 비공개 글만 제외
                          · 관리자: 모두 표시
                        ▶ 문의게시판 목록 보호 강화 (이미 있던 기능 강화)
                          · 비공개 글: 제목 → "🔒 비공개 게시글입니다"
                          · 비공개 글: 작성자명 마스킹 (예: 김** )
                          · 비공개 글: 답변 상태 가림 (─ 표시)
                          · 비공개 글: 작성일 가림 (─ 표시)
                        ▶ 단일글 직접 URL 접근 보호 (기존 기능)
                          · 본인+관리자만 조회 가능
                          · 외부인 접근 시 안내 페이지 표시
  v68.28 (2026-05-06) - 기업문의 제출 시 "오류가 발생했습니다" 수정:
                        ▶ 원인: 두 핸들러가 동일한 AJAX 액션(esg_inquiry_submit)에
                          등록돼있어서 충돌
                          · 583줄: 커뮤니티 게시판 핸들러 (nonce 키: esg_inquiry_nonce)
                          · 12627줄: 기업문의 핸들러 (nonce 키: esg_inquiry)
                          · 같은 hook → 늦게 등록된 게 덮어씀 → nonce 검증 실패
                        ▶ 해결: 기업문의 액션 이름을 esg_corp_inquiry_submit으로 변경
                          · functions.php 12627~12628줄 hook 이름 변경
                          · page-inquiry.php 255줄 ajax 호출 액션명 일치
                          · 커뮤니티/마이페이지/고객지원의 esg_inquiry_submit는 그대로
                            (커뮤니티 게시판 핸들러가 처리)
  v68.27 (2026-05-06) - 로그인 안 되는 문제 진짜 수정 (HOTFIX 2):
                        ▶ 원인 1: site_url('wp-login.php', 'login_post')의
                          'login_post' scheme이 워드프레스에 정의 안 된 값
                          → 잘못된 URL 생성 가능
                        ▶ 원인 2: login_url 필터가 다양한 곳에서 호출되며
                          예측 못한 부작용 발생
                        ▶ 수정 내용
                          · 폼 action: site_url('wp-login.php') 단순 사용
                          · login_url 필터 완전 제거 (위험성 너무 큼)
                          · login_init 훅 단순화: GET + 비로그인 + action≤login 시만
                            우리 /login으로 리다이렉트 (POST/표준 액션은 그대로)
                        🙇 거듭 죄송합니다 — 로그인 같은 핵심 기능엔 더 신중했어야 했습니다
  v68.26 (2026-05-06) - 로그인 자체가 안 되던 치명적 버그 수정 (HOTFIX):
                        ▶ 원인: v68.20에서 login_url 필터로 wp_login_url() 결과를
                          /login으로 바꾸면서, 로그인 폼 action까지 /login으로 가서
                          POST 데이터가 처리되지 못함 (그냥 같은 화면 다시 표시)
                        ▶ 수정: page-login.php 폼 action을 site_url('wp-login.php', 'login_post')
                          으로 직접 명시 (필터 우회)
                        ▶ login_url 필터: GET 시점에만 /login 반환, POST는 표준대로
                        ▶ 영향: 로그인 정상 작동, /wp-login.php 직접 접근은 여전히 우리 페이지로
                        🙇 죄송합니다 — 더 면밀히 테스트 못 한 점 사과드립니다
  v68.25 (2026-05-06) - /lostpassword 라우팅 강제 + 디버그 모드:
                        ▶ 핵심 수정: /lostpassword URL 직접 라우팅
                          · template_redirect에서 URL 경로 직접 검사
                          · /lostpassword 경로면 page-lostpassword.php 강제 include
                          · 404 우회, status 200 보장
                          · 워드프레스 페이지 라우팅에 의존하지 않음
                        ▶ 페이지 시작 부분 모든 리다이렉트 제거
                          · 로그인 체크 리다이렉트 제거 (이전: 홈으로 보냄)
                          · 무슨 일이 있어도 페이지를 떠나지 않음
                        ▶ 디버그 정보 표시 (관리자 전용)
                          · 페이지 하단에 펼치기 디버그 박스
                          · REQUEST_METHOD, POST/GET 키, 사용자 ID 등 표시
                          · POST 도달 여부 / nonce 검증 결과 표시
                          · 사용자가 어디서 막히는지 직접 확인 가능
                        ▶ 오류 메시지 친절화
                          · 이메일 미일치 vs 이름 미일치 구분 안내
                          · 디버그 모드에서 입력값 vs 가입값 비교
  v68.24 (2026-05-06) - 아이디 찾기 폼 제출 시 홈으로 가던 문제 진짜 수정:
                        ▶ 의심한 원인: 폼 action 미명시 + lostpassword 페이지 없음
                        ▶ 수정 1 — 폼 action을 정확한 페이지 URL로 명시
                          · /lostpassword?tab=findid 등 절대 URL 사용
                          · POST 데이터가 다른 라우터에 안 잡히게 보장
                        ▶ 수정 2 — query_vars 등록
                          · tab, key, login 등 우리가 사용하는 query var를
                            워드프레스에 등록 → 라우팅 안정화
                        ▶ 수정 3 — lostpassword 페이지 강제 생성
                          · 옵션 키(_v2) 트리거 안 되면 페이지가 안 만들어짐
                          · 매 admin 진입 시 페이지 존재 확인 + 없으면 즉시 생성
                          · 템플릿(page-lostpassword.php)도 자동 보정
  v68.23 (2026-05-06) - 아이디 찾기 폼 제출 시 홈으로 가던 문제 1차 시도:
                        ▶ 원인: 로그인된 사용자가 /lostpassword 페이지 진입 시
                          무조건 홈으로 리다이렉트하던 코드 때문에
                          관리자가 테스트할 때 폼 제출 → 홈으로 가버림
                        ▶ 해결
                          · 일반 로그인 사용자: 홈으로 (기존 동작 유지)
                          · 관리자(manage_options): 페이지 접근 허용 (테스트 가능)
                          · 비밀번호 재설정 링크(?tab=resetpw): 로그인 무관 허용
  v68.22 (2026-05-06) - SMTP 메일 발송 설정 (호스팅 옮기면 즉시 작동):
                        ▶ 신규 카드: 사이트 설정 → 사이트설정 → 📧 메일 발송 (SMTP)
                          · SMTP 호스트 / 포트 / 사용자명 / 비밀번호
                          · 암호화 (TLS / SSL / 없음)
                          · 발신자 이메일 / 발신자 이름
                          · 활성화 토글 (체크해야 SMTP 사용)
                          · 비밀번호 입력 안 하면 기존값 유지 (수정 시에만 입력)
                        ▶ phpmailer_init 훅 — wp_mail이 자동으로 SMTP 사용
                          · 활성화 + 정보 완료 시에만 SMTP 사용
                          · 미설정/비활성화 시 워드프레스 기본 mail() 함수 사용
                        ▶ 테스트 메일 발송 버튼
                          · 즉시 테스트 가능, 결과 표시 (성공/실패)
                        ▶ Gmail / 네이버 / 회사메일 가이드 (펼치기)
                          · Gmail 앱 비밀번호 만드는 법 안내
                          · 네이버 SMTP 활성화 방법
                        ▶ 메일 실패 시 친절한 안내
                          · 일반 사용자: "메일 서비스가 일시적으로 이용 불가합니다"
                          · 관리자: SMTP 설정 페이지로 직접 안내 링크
                        ▶ 메일 오류 자동 기록 (esg_last_mail_error)
                          · 관리자가 SMTP 카드에서 최근 오류 확인 가능
                        ▶ 호스팅 이전 후 SMTP 정보만 입력하면 바로 작동
  v68.21.1 (2026-05-06) - 아이디·비밀번호 찾기 페이지 디자인 깨짐 수정:
                        ▶ 원인: page-login.php는 인라인 스타일 블록을 갖고 있는데,
                          새로 만든 page-lostpassword.php는 같은 클래스를 쓰면서
                          스타일 블록이 없어서 디자인이 깨짐 (좌측 정렬, 카드 X)
                        ▶ 해결: page-login.php의 동일한 스타일 블록을
                          page-lostpassword.php에도 추가 (max-width 480px 카드 등)
  v68.21 (2026-05-06) - 아이디·비밀번호 찾기 분리 + 페이지 자동 생성 보장:
                        ▶ 1. 자동 페이지 생성 버전 키 v1 → v2
                          · v68.19에서 추가한 lostpassword 페이지가 생성 안 되던 문제
                          · 키 갱신으로 관리자 진입 시 자동 생성 트리거
                        ▶ 2. 아이디 찾기 + 비밀번호 찾기 통합 페이지 (탭 분리)
                          · /lostpassword?tab=findid — 아이디 찾기 (이름 + 이메일)
                          · /lostpassword?tab=findpw — 비밀번호 찾기 (이메일 또는 아이디)
                          · /lostpassword?tab=resetpw — 새 비밀번호 입력 (메일 링크)
                          · 깔끔한 탭 전환 UI (밑줄 강조)
                        ▶ 아이디 찾기 보안
                          · 이름 + 가입 이메일이 모두 일치해야 결과 표시
                          · 화면에는 마스킹된 아이디 (예: kim***) 표시
                          · 가입 이메일로 전체 아이디 별도 발송
                        ▶ 비밀번호 찾기 보안
                          · 이메일/아이디 일치하지 않아도 "발송됐습니다" 메시지 (열거 공격 방지)
                          · 24시간 유효 링크 (워드프레스 표준)
                        ▶ 메일 본문 URL 새 형식 적용 (?tab=resetpw)
  v68.20 (2026-05-06) - 로그인 정보 저장 + 워드프레스 로그인 페이지 차단:
                        ▶ 1. 로그인 정보 저장 기능 실제 작동
                          · 체크 시 → 아이디를 쿠키에 30일 저장
                          · 다음 로그인 페이지 진입 시 자동 채움
                          · 체크 해제하고 로그인 시 → 기존 쿠키 즉시 삭제
                          · 비밀번호는 절대 저장 안 함 (보안)
                          · wp_authenticate 액션으로 안전하게 처리
                        ▶ 2. 워드프레스 로그인 페이지 → 우리 로그인 페이지로
                          · /wp-login.php 직접 접근 → /login
                          · auth_redirect() 호출 → /login
                          · login_url 필터 → /login
                          · /mypage 비로그인 접근 → /login (with redirect_to)
                          · POST 로그인 시도 / 로그아웃 / 활성화 등은 표준 동작 유지
                        ▶ 3. UX 개선
                          · 저장된 아이디 있으면 비밀번호 칸에 자동 포커스
                          · 없으면 아이디 칸에 포커스 (기존 동작)
                          · 체크박스 자동 체크 (저장된 아이디 있을 때)
  v68.19 (2026-05-06) - 비밀번호 찾기 자체 페이지 + SNS 미설정 안내:
                        ▶ 비밀번호 찾기를 워드프레스 기본 페이지에서 자체 페이지로
                          · 신규: page-lostpassword.php (자동 생성)
                          · URL: /lostpassword
                          · 우리 사이트 디자인과 일관성
                        ▶ 비밀번호 찾기 흐름
                          1. /lostpassword → 이메일 또는 아이디 입력
                          2. 재설정 메일 자동 발송 (24시간 유효 링크)
                          3. 메일 링크 클릭 → /lostpassword?action=resetpw
                          4. 새 비밀번호 입력 → 변경 완료 → 로그인
                        ▶ 워드프레스 표준 흐름 모두 우리 페이지로 리다이렉트
                          · wp_lostpassword_url() → /lostpassword
                          · /wp-login.php?action=lostpassword → /lostpassword
                          · /wp-login.php?action=rp/resetpass → /lostpassword?action=resetpw
                          · 메일 본문 링크 → /lostpassword
                        ▶ 보안: 사용자 존재 여부를 정확히 알리지 않음 (열거 공격 방지)
                        ▶ SNS 미설정 안내 (관리자에게만 표시)
                          · 일반 사용자: SNS 영역 자체 숨김
                          · 관리자: "SNS 로그인 키를 등록하세요" 안내 박스
                          · 사이트 설정 → SNS 로그인 페이지로 직링크
  v68.18 (2026-05-06) - SNS 로그인 직접 구현 (네이버 + 구글):
                        ▶ 로그인 페이지 SNS 버튼 활성화
                          · 카카오 버튼 제거 (요청)
                          · 네이버 / 구글만 표시
                          · 키 미설정 시 자동으로 영역 숨김
                        ▶ OAuth 2.0 인증 흐름
                          · 보안 nonce(state) + transient으로 위조 방지
                          · code → access_token → 사용자 정보 흐름
                          · 콜백 URL: ?esg_naver_callback=1, ?esg_google_callback=1
                        ▶ 사용자 처리 로직
                          · 1순위: provider+uid 매핑된 사용자 찾기
                          · 2순위: 동일 이메일의 기존 계정에 SNS 연동
                          · 3순위: 신규 가입 (sns_naver_xxxx 형태 username)
                        ▶ 관리자 화면
                          · 사이트 설정 → 사이트설정 → SNS 로그인 (🔑) 카드
                          · 네이버: Client ID + Secret
                          · 구글: Client ID + Secret
                          · Callback URL을 화면에 표시 (복사하여 개발자센터 등록)
                          · 활성/미설정 상태 표시 (요약 영역)
                        ▶ 사전 작업 (사용자가 직접)
                          · 네이버 개발자센터 (developers.naver.com) 앱 등록
                          · 구글 클라우드 (console.cloud.google.com) OAuth 클라이언트 만들기
                          · 콜백 URL 정확히 등록
                        ▶ 보안: state nonce 10분 유효, 재사용 불가
  v68.17 (2026-05-06) - 본인확인 토큰 탭별 독립 분리:
                        ▶ 기존: 한 번 인증하면 두 탭(개인정보·비밀번호) 모두 풀림
                        ▶ 변경: 두 탭이 각각 독립적으로 본인확인 필요
                          · 토큰 키: esg_reauth_profile_{uid} / esg_reauth_password_{uid}
                          · 개인정보 수정 인증 → 비밀번호 변경 탭 가면 다시 인증
                          · 비밀번호 변경 인증 → 개인정보 수정 탭 가면 다시 인증
                        ▶ esg_is_reauth_valid($tab_name) — 탭 이름 인자 추가
                        ▶ 비밀번호 변경 성공 시 두 탭 토큰 모두 만료 (보안)
                        ▶ 사용자에게 더 안전한 보안 모델
  v68.16 (2026-05-06) - 헤더 상단 4개 사이트 버튼 간격 확장:
                        ▶ "버튼 간격" 슬라이더의 max값 24 → 80px 상향
                        ▶ 0~8: 거의 붙음 / 20~40: 보통 / 50~80: 넉넉히 떨어짐
                        ▶ 위치: 사이트 설정 → 디자인 → 헤더 버튼
                          → 상단 버튼 스타일 조정 → 버튼 간격
  v68.15 (2026-05-06) - 프론트 admin bar 완전 숨기기:
                        ▶ 워드프레스 관리자 도구막대(상단 28px 검정 바)를
                          프론트에서 모든 사용자에게 숨김 (관리자 포함)
                        ▶ 처리 방식
                          · show_admin_bar 필터 → false
                          · admin-bar CSS/JS 큐 제거
                          · html margin-top:0 강제 (잔여 공백 방지)
                        ▶ 관리자 페이지 접근 방법
                          · 사이트 URL + /wp-admin 직접 입력
                          · 또는 로그아웃 후 /wp-login.php 로그인
                        ▶ 영향: 관리자도 사이트 디자인을 일반 방문자 시점으로 확인 가능
  v68.14 (2026-05-06) - 마이페이지 본인확인 시스템 (개인정보·비밀번호):
                        ▶ 보안 강화 — 개인정보 수정 / 비밀번호 변경 탭 진입 시
                          이메일 + 비밀번호 재입력으로 본인 확인 후 접근 가능
                        ▶ 인증 토큰 (transient) 10분 유효
                          · 인증 후 10분 동안은 자유롭게 정보 수정 가능
                          · 만료되면 다시 본인 확인 요구
                        ▶ 비밀번호 변경 성공 시 본인확인 토큰 자동 만료
                          · 새 비밀번호로 다시 인증해야 함 (보안 정책)
                        ▶ 핸들러 측 이중 검증 (직접 POST 우회 방지)
                          · 폼 제출 시에도 본인확인 토큰 유효성 재확인
                        ▶ 신규 함수
                          · esg_is_reauth_valid() — 인증 상태 확인
                          · esg_handle_reauth() — 본인확인 처리 (admin-post)
                          · esg_render_reauth_form($tab, $title) — 인증 폼 렌더
                        ▶ UX
                          · 인증 안내 박스 (10분 유효 명시)
                          · 인증 실패 시 빨간 에러 메시지
                          · 인증 성공 시 초록 토스트 (?reauth_ok=1)
                        ▶ 토큰 키: esg_reauth_{user_id} (값=md5(session_token))
  v68.13.2 (2026-05-06) - 우측 깎기 기본값 100→200px 상향:
                        ▶ 우측 상단의 위성지도 토글이 일부 남아 보이는 문제 추가 수정
                        ▶ 기존 사용자: 사이트 설정 → 오시는길 → "우측 깎기" 100 → 200 변경
  v68.13.1 (2026-05-06) - 지도 상단 깎기 기본값 70→110px 상향:
                        ▶ 위성/지형 토글이 살짝 남아 보이는 문제 해결
                        ▶ 기존 사용자: 사이트 설정 → 오시는길 → "상단 깎기" 70 → 110 변경
  v68.13 (2026-05-06) - iframe 지도 깎기 픽셀 사용자 조정 가능:
                        ▶ 네이버 지도 임베드의 좌측 패널·상단 토글·우측 줌버튼 등을
                          픽셀 단위로 정확히 가릴 수 있게 4방향 깎기 옵션 추가
                          · 좌측 깎기 (기본 540px) — 장소 정보 카드 영역
                          · 상단 깎기 (기본 70px) — 일반/위성/지형 토글
                          · 우측 깎기 (기본 100px) — 줌 버튼
                          · 하단 깎기 (기본 30px) — NAVER 로고
                          · 지도 표시 높이 (기본 380px) — 보이는 영역
                        ▶ 사용자가 화면을 보면서 픽셀 미세 조정 가능
                        ▶ 기본값으로도 대부분의 네이버 임베드 깔끔하게 표시됨
  v68.12 (2026-05-06) - 좌표 기반 지도 모드 추가 (가장 쉬운 방법):
                        ▶ 새 기본 모드: 좌표 입력 (위도·경도)
                          · OpenStreetMap 정적 임베드 (API 키 불필요, 무료)
                          · 위도/경도만 입력하면 자동으로 깔끔한 지도 생성
                          · 줌 레벨 선택 (14~19)
                          · 컨트롤 거의 없음, 마커만 표시
                          · "큰 지도 보기" 버튼 자동 생성
                        ▶ 3가지 모드 정리
                          · 좌표 모드 (기본, 권장) — 위도/경도 입력
                          · 이미지 모드 — 캡처 사진 업로드
                          · iframe 모드 — 네이버/구글 임베드 (한계 있음)
                        ▶ iframe 모드 한계 안내 강화
                          · https://naver.me/... 같은 짧은 URL 사용 불가 명시
                          · 진짜 임베드 URL이 필요함을 안내
  v68.11.1 (2026-05-06) - 오시는길 지도 이미지 fallback 개선:
                        ▶ 이미지 모드인데 이미지를 안 올린 상태 처리
                          · 링크만 있으면 → 큰 카드형 "🗺️ 네이버 지도에서 위치 보기" 버튼
                          · 둘 다 없으면 → 관리자에게만 노란 안내 박스 (일반 사용자는 안 보임)
                        ▶ 사용자 혼란 방지: 화면이 깨져 보이는 일 없게 항상 의미있는 콘텐츠 표시
  v68.11 (2026-05-06) - 오시는길 지도 이미지 모드 (가장 깔끔):
                        ▶ 근본 원인: 네이버 지도 임베드는 마커/장소 정보 카드,
                          좌측 사이드바, 상단 토글, 줌버튼 등 컨트롤이 너무 많고
                          가변적이라 CSS만으로 100% 가리기 어려움
                        ▶ 새 솔루션: 지도 이미지 모드 추가
                          · 사용자가 네이버/구글 지도에서 원하는 모습으로 캡처
                          · 캡처 이미지를 업로드 → 지도가 깔끔하게 표시됨
                          · "큰 지도 보기" 버튼으로 실제 지도 페이지 링크
                          · 디자인 100% 통제 가능, 컨트롤 0
                        ▶ 두 가지 모드 선택 가능
                          · 이미지 모드 (기본, 권장) — 캡처 이미지 + 링크
                          · iframe 모드 (선택) — 임베드 코드 + 컨트롤 가리기
                        ▶ 페이지 콘텐츠 편집 폼 새 필드 타입 추가
                          · type=image — wp.media 업로더 + 미리보기
                          · type=select — 옵션 드롭다운
                        ▶ 페이지 매니저에서 wp_enqueue_media 호출 보장
  v68.10 (2026-05-06) - 푸터 '회사소개' 외부 링크 옵션:
                        ▶ 푸터의 '회사소개' 링크를 모회사·운영사 외부 사이트로 연결
                          · 헤더의 '기관소개'(/about) — 우리 교육기관 소개 페이지
                          · 푸터의 '회사소개' — 모회사(예: 챌비온) 외부 홈페이지
                          → 두 개념이 분리됨
                        ▶ 사이트 설정 → 사이트설정 → 푸터(🏢) 카드 안에
                          '🔗 푸터 회사소개 링크 설정' 영역 추가:
                          · 외부 URL (예: https://www.challvion.com)
                          · 링크 텍스트 (기본: 회사소개)
                          · 새 탭/현재 탭 선택 (외부는 새 탭 권장)
                        ▶ 동작
                          · URL 미설정 → 기존대로 /about 페이지로
                          · URL 설정 → 외부 사이트로 이동 (rel="noopener" 보안 처리)
                        ▶ 옵션 키:
                          · esg_footer_about_url / _label / _target
  v68.9  (2026-05-06) - 오시는길 지도 깔끔 표시:
                        ▶ 지도 컨트롤 가리기 모드 (map_clean) 옵션 추가
                          · 네이버 지도의 좌측 사이드바 (지도 홈/길찾기/메뉴)
                          · 상단 지도 타입 토글 (일반/위성/지형)
                          · 우측 줌 버튼 (+/-)
                          · NAVER 로고 / 디아이빌 같은 보조 컨트롤
                          → 모두 가려서 순수 지도 영역만 표시
                        ▶ 동작 방식
                          · iframe을 wrapper로 감싸고 overflow:hidden + 높이 320px 고정
                          · iframe 자체는 width 더 넓게(+200px), height 더 높게(430px)
                            만들고 음수 마진으로 가장자리 밀어내기
                          · 가시 영역에는 지도만 남고 컨트롤은 잘림
                        ▶ 옵션 위치
                          · 페이지 → 오시는길 편집 → 지도 깔끔 모드 체크박스
                          · 기본값 ON (대부분의 경우 적합)
                        ▶ iframe 코드 처리 방식 개선
                          · 사용자가 `<iframe ... src="..." ...>` 통째로 붙여넣어도
                            src만 추출해서 우리 스타일로 다시 렌더
                          · src 추출 실패 시 원본 그대로 (안전 fallback)
  v68.8  (2026-05-06) - 페이지 본문 편집 시스템 신설:
                        ▶ 페이지 관리 목록에서 누락된 페이지 추가
                          · location (오시는길) 추가
                          · privacy (개인정보처리방침) 신규
                          · terms (이용약관) 신규
                          · 자동 생성 매핑 / 보호 슬러그 / template_redirect 모두 갱신
                        ▶ 페이지 본문 콘텐츠 옵션 시스템 (esg_page_content_*)
                          · 페이지 관리 → 페이지 편집에 '📝 페이지 본문 편집' 영역 추가
                          · 기관소개: 소개글, 통계 4개(자동/수동), 핵심 가치 카드 3개
                          · 오시는길: 주소, 전화, 이메일, 지도 임베드, 운영시간,
                                     대중교통, 주차 안내
                          · 개인정보/약관: 본문 + 최종 개정일
                        ▶ 4개 템플릿 옵션 기반으로 전면 재작성
                          · page-about.php — 옵션값으로 본문/통계/가치 카드 동적 렌더
                          · page-location.php — iframe 임베드 지원, 교통/주차 카드
                          · page-privacy.php — 신규 생성
                          · page-terms.php — 신규 생성
                        ▶ 헬퍼 함수
                          · esg_get_page_content_options(slug) — 옵션 + 기본값 병합
                          · esg_save_page_content_options(slug, data) — 저장
                          · esg_get_page_content_fields(slug) — 폼 필드 정의
                          · esg_render_page_content_editor(slug) — 편집 폼 출력
                        ▶ 옵션 키 형식: esg_page_content_{slug} (단일 옵션 배열)
  v68.7.1 (2026-05-06) - 디자인 설정 탭 전환 버그 진짜 수정 (v68.6 실패 만회):
                        ▶ v68.6의 정규식 변환 방식 폐기
                          · 원인: /\{[\s\S]*?\}/ non-greedy 매치가 중첩 중괄호의
                                  첫 번째 } 에서 멈춤 → 함수가 깨진 채 변환됨
                          · 결과: 디자인 페이지의 탭 클릭 핸들러 등록 실패 유지
                        ▶ v68.7.1 새 방식: addEventListener 가로채기
                          · esgGroupRunInlineScripts에서 script 실행 직전에
                            document.addEventListener / window.addEventListener
                            / jQuery.fn.ready 를 임시로 가로채기
                          · 'DOMContentLoaded' / 'load' / 'readystatechange'
                            콜백이 등록되면 즉시 실행 (이벤트가 이미 끝났으므로)
                          · 다른 이벤트(click, change 등)는 정상 등록
                          · script 실행 끝나면 원래 함수로 복원
                        ▶ 영향: 디자인 설정의 모든 탭 (헤더버튼/로고SNS/색상테마
                                /하단CTA/페이지편집) 정상 전환
                                다른 페이지의 inline DOMContentLoaded 도 모두 호환
  v68.7  (2026-05-06) - 과정⇄강사 연동 + 강사 사진 표시:
                        ▶ 과정 편집의 '담당 강사' 자유 텍스트 → 드롭다운 선택
                          · 등록된 강사 목록에서 선택 (직책 함께 표시)
                          · '직접 입력' 옵션 유지 (강사 CPT 등록 안 된 경우)
                          · 새 강사 등록 바로가기 링크
                          · 강사가 0명이면 '강사를 먼저 등록하세요' 안내
                          · 기존 텍스트 데이터와 호환 (이름 매칭 시도)
                        ▶ 강사 정보 헬퍼 함수 신설
                          · esg_resolve_course_instructor — 이름/CPT객체/연결여부 반환
                          · esg_course_instructor_name — 강사명만 빠르게
                          · 과정 메타가 ID(숫자)든 텍스트(이름)든 안전하게 처리
                        ▶ 강사 소개 페이지 (page-instructors.php) 사진 우선 표시
                          · 사진이 있으면 사진, 없으면 이모지 (기본 👨‍🏫)
                          · 분야 칩 추가 (택소노미 활용)
                          · 직책 / 한줄소개 / 평점 표시
                          · 새 메타필드 (_instructor_position, _career_summary) 활용
                          · 카드 호버 효과 강화
                        ▶ 과정 상세 (single-course.php) 강사 CPT 연동 카드
                          · 담당 강사가 CPT에 등록된 경우 풍부한 정보 자동 표시
                            - 사진 (120px 원형)
                            - 분야 칩 / 직책 / 평점
                            - 한줄소개 / 경력 (최대 5줄)
                            - 강사 프로필 자세히 보기 링크
                          · 자유 텍스트 강사명도 호환 (fallback 카드)
                          · 과정별 추가 강사 메시지 (course_instructor_bio)는
                            '📌 이 과정에 대한 강사 메시지'로 별도 섹션
                        ▶ 과정 목록 (page-courses.php / page.php) 강사명 정규화
                          · CPT ID 저장 시에도 이름으로 자동 변환 출력
                        ▶ '강사 소개' textarea 안내문 변경
                          · 'CPT가 우선, 추가 메모 용도' 명확화
  v68.6  (2026-05-06) - 4가지 버그/기능 통합 수정:
                        ▶ 디자인 설정 탭 전환 안 되던 버그 수정
                          · 원인: AJAX로 가져온 inline script가 DOMContentLoaded
                                  이벤트를 기다리는데 그 이벤트는 이미 끝남
                          · 해결: esgGroupRunInlineScripts에서 DOMContentLoaded
                                  / jQuery(document).ready / window load 콜백을
                                  즉시 실행으로 자동 변환
                          · 영향: 디자인 설정의 헤더버튼/색상테마/CTA 등 모든 탭 작동,
                                  다른 페이지의 inline script도 자동 호환됨
                        ▶ Footer '회사소개/개인정보처리방침/이용약관' 페이지 이동으로 변경
                          · 모달 → 실제 페이지 (about/privacy/terms)
                          · 페이지가 없으면 fallback으로 모달 유지 (안전)
                        ▶ 🎨 수료증 디자인 설정 시스템 신설
                          · 수강관리 > 수료증 발급 > 🎨 디자인 설정 토글
                          · 텍스트: 제목, 영문 부제, 본문 템플릿, 라벨, 발급기관
                          · 본문 변수: {name} {course} {period} {start} {end}
                                       {hours} {org} 자동 치환
                          · 색상 4종: 메인 / 강조 / 모서리 / 직인 (color picker)
                          · 테두리 스타일: 고전형 / 모던형 / 심플형
                          · 직인 이미지 / 배경 이미지 업로드 (wp.media)
                          · 표시 항목 토글: 교육기간 / 총시간 / 소속
                          · 옵션 키: esg_cert_design (단일 옵션 배열)
                        ▶ 수료증 자동 연동 발급
                          · 디자인 변경 시 모든 발급 수료증에 즉시 반영
                          · 수강생 이름/과정명/기간 enrollment에서 자동 가져옴
                          · esg_render_certificate_html 디자인 옵션 적용 모드로 재작성
                        ▶ 미리보기 라우터 추가 (/?esg_cert_preview=1)
                          · 가짜 데이터로 디자인 즉시 확인 (관리자 전용)
                        ▶ 수강 관리 그룹 진입 시 wp_enqueue_media 호출
                          · 수료증 디자인 설정의 이미지 업로드 보장
  v68.5  (2026-05-06) - 강사 관리 전면 개편 — 분야 탭 + 별도 등록/편집 페이지:
                        ▶ 강사 분야 택소노미 도입 (instructor_category)
                          · 자유롭게 분야 추가/이름변경/삭제 (소속 강사 0명일 때만 삭제)
                          · 한 강사가 여러 분야에 속할 수 있음 (다중 선택)
                          · 사이드바 노출 차단 — esangedu에서 통합 관리
                        ▶ 강사 관리 목록 페이지 (esangedu-instructors)
                          · 분야별 탭 (전체 / AI / 디자인 / ... / 분야 미지정)
                          · 카드 그리드 레이아웃 (사진/이름/직책/분야칩/평점)
                          · ⚙️ 분야 관리 접기 영역 (인라인 추가/이름변경/삭제)
                          · + 새 강사 등록 버튼
                        ▶ 강사 등록/편집 페이지 신설 (esangedu-instructor-edit)
                          · 4개 내부 탭: 기본정보 / 경력 / 연락처 / 노출설정
                          · 사진 업로드 (wp.media), 200×200 정사각 미리보기
                          · 새 메타: position(직책) / career(경력) / career_summary(요약)
                                     intro(자기소개) / email / phone
                          · 기존 메타 호환 유지 (icon / rating)
                          · 분야 다중 선택 칩 UI
                        ▶ 워드프레스 기본 편집 화면(post.php) 메타박스
                          · 자체 편집 페이지로 안내하는 메시지로 교체
                        ▶ 프론트 single-instructor.php 전면 재작성
                          · 새 메타필드 활용 (직책, 경력, 분야 칩)
                          · 평점 배지 / 경력 글머리 / 분야 칩 칩
                          · 잘못된 메타키 (_esg_instructor_*) 모두 정정
  v68.4  (2026-05-06) - 🏆 수료증 발급 시스템:
                        ▶ 관리자: 수강 관리 > 수료증 발급 탭 (esangedu_certificates_admin_page)
                          · 발급 대기 목록 (수강확정 + 교육 종료일 지남)
                          · 일괄/개별 발급 / 취소 / 보기 인쇄
                          · 통계: 대기 N건 / 발급 N건
                        ▶ 수강생: 마이페이지 > 수료증 탭 (?tab=certs)
                          · 발급된 수료증 카드 목록
                          · 보기 / 인쇄 / PDF 다운로드 버튼
                          · 학습현황 통계 카드에도 수료증 수 표시
                          · 학습목록의 수료 항목 옆에 🏆 수료증 버튼
                        ▶ 수료증 인쇄 페이지 (/?esg_cert=CERT-...)
                          · A4 가로 단독 페이지 (헤더/푸터 없음)
                          · 디자인: navy/blue/cyan 토큰 + 4개 모서리 장식
                          · Noto Serif KR (제목) + Noto Sans KR (본문)
                          · 수료번호 / 수료자 / 과정명 / 교육기간 / 발급일 / 직인
                          · 권한: 관리자 또는 본인만
                          · 인쇄: window.print() (PDF 저장은 브라우저 인쇄 메뉴에서)
                        ▶ 데이터 스키마 확장 (enrollment 항목)
                          · cert_issued / cert_no / cert_issued_at / cert_issuer
                          · 수료번호 형식: CERT-YYYYMMDD-NNNN (일별 카운터)
                        ▶ 발급 가능 판정 로직 (esg_is_cert_eligible)
                          · 수강확정/승인완료/입금확인/학습중/수강중 + 종료일 지남
                          · 명시적 수료/완료 상태
                          · 미발급 + 미취소
  v68.3  (2026-05-06) - GET 폼(검색/필터) AJAX 호환성 버그 수정:
                        ▶ 문제: 과정관리/회원관리 페이지의 검색 폼이 동작 안 함
                          · '저장되었습니다' 토스트만 뜨고 결과는 변화 없음
                        ▶ 원인: AJAX 핸들러가 모든 form 데이터를 $_POST로만 전달
                          · 페이지 함수는 $_GET['s'], $_GET['tab_filter']를 보는데
                            우리는 $_POST로만 보내서 검색어가 도달하지 못함
                          · 거기다 모든 응답에 무조건 '저장되었습니다' 토스트 출력
                        ▶ 해결:
                          · JS: form method 판별해서 _method=get/post 메타 추가
                          · PHP: _method=get이면 데이터를 $_GET 으로 복사 + $_POST 비움
                                 (의도치 않은 저장 트리거 방지)
                          · JS: GET 폼은 토스트 표시 안 함 (검색은 저장 동작 아님)
                        ▶ 부수 개선:
                          · POST form도 라우팅 메타키(_group/_tab/_page/_method)를
                            $_POST에서 제거 → 페이지 함수가 깨끗한 폼 데이터만 받음
                          · 단순 탭 로드 시에도 $_POST 비우기 → 의도치 않은 처리 방지
                        ▶ 영향 페이지: 과정관리, 회원관리 (GET form 사용 페이지 자동 적용)
  v68.2  (2026-05-06) - CPT 사이드바 노출 차단 + 강사/후기 관리 페이지 신규 (esangedu 통합):
                        ▶ 사이드바에 따로 떠있던 '과정 관리', '강사 관리', '수강후기' 제거
                          · course / instructor / review 모두 show_in_menu => false
                          · CPT 자체는 그대로 유지 (편집 URL은 살아있음)
                        ▶ 강사 관리 페이지 신규 (esangedu_instructors_admin_page)
                          · 빠른 등록 폼 + 인라인 수정 + 삭제
                          · 메타필드: 전문분야 / 아이콘 / 평점
                        ▶ 후기 관리 페이지 신규 (esangedu_reviews_admin_page)
                          · 빠른 등록 폼 + 인라인 수정 + 삭제
                          · 메타필드: 평점 / 작성자명 / 관련 과정 / 내용
                        ▶ 그룹 탭 정의 수정 — 외부링크 모두 제거
                          · 교육관리 > 강사: edit.php?post_type=instructor → esangedu-instructors
                          · 콘텐츠 > 후기:   edit.php?post_type=review     → esangedu-reviews
                        ▶ 사이드바 동기화 강화
                          · CPT 편집 화면(post.php?post_type=course/instructor/review)에서도
                            esangedu 메인 메뉴 + 해당 그룹 탭 강조
                        ▶ 대시보드 잔재 외부링크 1건 정리
                          · 강사 카드: edit.php → esangedu-instructors
                        ▶ 결과: 모든 메뉴/관리 항목이 'esangedu' 단일 메뉴 안에 통합됨
  v68.1  (2026-05-06) - 4개 그룹 B-2 AJAX 통합 완성:
                        ▶ 수강 관리 그룹 활성화 (수강신청 / 회원관리)
                        ▶ 고객응대 그룹 활성화 (문의답변 / 제안서요청 / 기업교육문의)
                        ▶ 교육 관리 그룹 활성화 (과정관리 / 강사CPT)
                        ▶ 사이트 설정 그룹 활성화 (홈페이지 / 디자인 / 메뉴 / 페이지 / 사이트설정)
                        ▶ 호환성 사전 검증
                          · wp_redirect/exit 호출 0건 (모든 그룹 페이지)
                          · wp_editor (TinyMCE) 미사용 — 큰 위험 회피
                          · wp.media() 호출은 그룹 진입 시 미리 enqueue로 보장
                          · check_admin_referer() 자체 nonce 검사 그대로 유지
                        ▶ 모든 그룹이 한 화면 통합 편집(B-2) 완성 상태
  v68.0  (2026-05-06) - 관리자 메뉴 6그룹 통합 + B-2 AJAX 탭 시스템 (1단계):
                        ▶ 죽은 LMS 링크 6개 제거
                          · 대시보드 'LMS 도구' 접기 영역 (5개 버튼)
                          · 수강신청 페이지 우상단 '수강생 현황(LMS)' 버튼
                        ▶ 메뉴를 16개 평면 → 6개 그룹으로 재편
                          · 📊 대시보드 / 📚 교육관리 / 🎓 수강관리
                          · 📰 콘텐츠 / 💬 고객응대 / ⚙️ 사이트 설정
                          · 기존 페이지 URL 모두 보존(숨김 등록), 즐겨찾기 호환
                        ▶ AJAX 라우터 인프라 구축
                          · esg_load_group_tab / esg_submit_group_tab 핸들러
                          · 공통 JS: 탭 전환 / form 가로채기 / inline script 실행
                          · 공통 CSS: 로딩 스피너 / 토스트 알림
                          · parent_file/submenu_file 필터로 사이드바 동기화
                        ▶ 콘텐츠 그룹 B-2 AJAX 시범 완성
                          · 5개 탭: 공지 / FAQ / 자료실 / 팝업 / 후기(CPT)
                          · 한 화면에서 부드러운 탭 전환 + form 저장
                        ▶ 빈 골격 4개 그룹 (다음 세션에서 채움)
                          · 교육관리 / 수강관리 / 고객응대 / 사이트설정
  v67.0  (2026-05-06) - 🏫 학사행정 / 📚 LMS 메뉴 통째 제거 (정리 1단계):
                        · 사유: 과정 관리와 중복(과정 안에서 차시 입력 가능),
                                LMS-학사행정 미연동, 실데이터 부재
                        · 제거된 파일: /lms.php, /inc/lms-helpers.php,
                                       /inc/lms-main.php, /inc/lms-sub.php
                        · 제거된 메뉴: esg-lms-main(학사행정 9개 서브),
                                       esg-lms-lessons(LMS 5개 서브)
                        · 향후: 필요 시 esangedu 단일 메뉴 안에 통합 재설계
  v66.31 (2026-05-04) - 세로 카드 — 썸네일↔과정명 사이 적당한 간격 (사용자 요청):
                        · 사용자 지적: v66.30에서 완전 0으로 했더니 너무 붙음
                          "강사↔신청기간 사이 여백만큼만 만들어줘"
                        · 측정: cc-meta gap .3rem (강사↔신청기간 사이)
                        · 수정: cc-body padding-top: 0 → .3rem (동일한 간격)
  v66.30 (2026-05-04) - 썸네일↔제목 여백 완전 제거 (너무 붙음 → v66.31에서 .3rem으로)
  v66.29 (2026-05-04) - 카드 위/아래 빈 여백 1차 제거
  v66.28 (2026-05-04) - 세로 카드 과정명 1.4rem 키움
  v66.27 (2026-05-04) - 디자인 설정 페이지 탭 UI 개편
  v66.26 (2026-05-04) - 기관소개 / 오시는길 페이지 분리
  v66.25 (2026-05-04) - 자료실 다운로드 버튼/헤더 정렬
  v66.22 (2026-05-04) - 페이지 강좌 미지정 시 "준비 중" 안내만
  v66.21 (2026-05-04) - 세로 카드 빈 여백 축소 (1차)
  v66.20 (2026-05-04) - cc-spacer로 별점/가격 하단 정렬
  v66.19 (2026-05-04) - 세로 카드 제목 2줄 높이 고정
  v66.18 (2026-05-04) - summary, cat 빈 자리 처리
  v66.17 (2026-05-04) - 세로 카드 모든 요소 위치 정렬
  v66.16 (2026-05-04) - 가로 카드 썸네일 카드 세로 전체 채우기
  v66.15 (2026-05-04) - 글씨 색 통일
  v66.14 (2026-05-04) - 과정명 2줄 여유 (가로 카드)
  v66.13 (2026-05-04) - 가로 카드 4가지 정밀 조정
  v66.12 (2026-05-04) - 가로 카드 강사/신청기간/개폐강확정일 추가
  v66.11 (2026-05-04) - 가로 카드 상단 태그 — 관리자 입력값 적용
  v66.10 (2026-05-04) - PPT2 #19 가로 카드 비율 + 둥근 사각형 썸네일
  v66.7  (2026-05-04) - PPT 미적용 항목 3건
  v66.6  (2026-05-04) - 학습 목표/대상 줄바꿈 보존
  v66.5  (2026-05-04) - SNS 아이콘 위치 미세 조정 작동
  v66.4  (2026-05-04) - 수강생 후기 슬라이더 무한 루프
  v66.1  (2026-05-04) - PPT2 #2 퀵 메뉴 사이즈 2배
*/

@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap');

:root {
  --navy: #0F2040;
  --blue: #1B6FD8;
  --cyan: #00C2D1;
  --lb: #E8F4FD;
  --g50: #F8FAFC;
  --g100: #F1F5F9;
  --g200: #E2E8F0;
  --g300: #CBD5E1;
  --g400: #94A3B8;
  --g500: #64748B;
  --g600: #475569;
  --g700: #334155;
  --g800: #1E293B;
  --ok: #10B981;
  --warn: #F59E0B;
  --err: #EF4444;
  --font: 'Noto Sans KR', sans-serif;
  --r: 12px;
  --rs: 8px;
  --sh0: 0 1px 3px rgba(0,0,0,.08);
  --sh1: 0 4px 16px rgba(0,0,0,.12);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: var(--font); background: var(--g50); color: var(--g800); }
a { text-decoration: none; }
img { max-width: 100%; height: auto; }

/* ── BUTTONS ── */
.btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: .55rem 1.1rem; border-radius: var(--rs);
  font-size: .88rem; font-weight: 600; cursor: pointer;
  border: none; text-decoration: none; transition: all .18s;
  white-space: nowrap; font-family: var(--font);
}
.btn-primary { background: linear-gradient(135deg, var(--blue), var(--cyan)); color: #fff; }
.btn-primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn-outline { background: transparent; border: 1.5px solid rgba(255,255,255,.35); color: #fff; }
.btn-outline:hover { background: rgba(255,255,255,.12); }
.btn-navy { background: var(--navy); color: #fff; }
.btn-danger { background: var(--err); color: #fff; }
.btn-success { background: var(--ok); color: #fff; }
.btn-gray { background: var(--g100); color: var(--g700); border: 1px solid var(--g200); }
.btn-sm { padding: .3rem .7rem; font-size: .78rem; }
.btn-lg { padding: .75rem 1.8rem; font-size: .98rem; border-radius: 10px; }

/* ════════════════════════════════
   3줄 헤더
════════════════════════════════ */
.site-header { position:sticky;top:0;z-index:500;background:#fff;box-shadow:0 2px 10px rgba(0,0,0,.1); }

/* 1줄: 연결사이트 + SNS */
.h-top { background:var(--navy);padding:0 1.5rem; }
.h-top-in { max-width:1280px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;min-height:52px;padding:.35rem 0 }
.h-sites { display:flex;align-items:center;gap:var(--esg-site-gap, .5rem); }

/* 사이트 버튼 — 참고 이미지2 스타일 (로고 중심, 콤팩트) */
.h-site-btn {
  display:flex;align-items:center;gap:.55rem;
  padding: var(--esg-site-pad-y,.45rem) var(--esg-site-pad-x,.9rem);
  border-radius: var(--esg-site-radius, 8px);
  text-decoration:none;
  transition:filter .18s,transform .18s,box-shadow .18s;
  min-height: var(--esg-site-height, 40px);
  box-shadow:0 2px 0 rgba(0,0,0,.12);
}
.h-site-btn:hover { filter:brightness(1.12);transform:translateY(-1px);box-shadow:0 4px 10px rgba(0,0,0,.22); }
.hsb-logo {
  width: var(--esg-site-logo-size, 28px);
  height: var(--esg-site-logo-size, 28px);
  border-radius:5px;
  background:rgba(255,255,255,.18);
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;overflow:hidden;
}
.hsb-logo img{width:100%;height:100%;object-fit:contain}
.hsb-texts { display:flex;flex-direction:column;line-height:1.05; }
.hsb-sub  { font-size: var(--esg-site-sub-size, .58rem); color:rgba(255,255,255,.75);margin-bottom:2px;letter-spacing:.02em;font-weight:500 }
.hsb-name { font-size: var(--esg-site-name-size, .84rem); font-weight:800;color:#fff;white-space:nowrap;letter-spacing:-.01em }

@media (max-width:900px){
  .h-sites{gap:.35rem}
  .h-site-btn{padding:.35rem .6rem;min-height:36px}
  .hsb-sub{display:none}
  .hsb-logo{width:22px;height:22px}
  .hsb-name{font-size:.74rem}
}
.h-sns { display:flex;gap:.45rem;align-items:center; }
.h-sns-btn { color:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;transition:all .18s;text-decoration:none; }
.h-sns-btn:hover { color:#fff;background:rgba(255,255,255,.15); }
/* 🆕 SNS 브랜드 컬러 버튼 (배경 원형) */
.h-sns-color{overflow:hidden;position:relative}
/* 🆕 v66.5 PPT1 #2: 미세 조정 작동 — wrap이 인라인 스타일(width/height/transform)을 지키도록 */
.h-sns-color .h-sns-icon-wrap{
  position:relative;
  transition:transform .18s;
  /* width/height는 header.php의 인라인 스타일에서 설정됨 (62% 등) */
}
/* 🆕 v66.5 PPT1 #2: SVG는 wrap 크기 100% 채움 (wrap 자체가 작아지면 SVG도 작아짐)
   기존 !important는 wrap의 인라인 width/height를 무시하지 않음 — wrap 자식 기준 100% */
.h-sns-color svg{width:100%;height:100%;display:block}
.h-sns-color:hover{box-shadow:0 3px 8px rgba(0,0,0,.25);filter:brightness(1.08)}
/* 🆕 v66.5 PPT1 #2: 호버 시 부모 transform이 자식의 translate를 덮지 않도록
   부모 호버는 box-shadow + filter만 사용 (translateY 제거) */

/* 2줄: 로고 + 액션 */
.h-mid { background:#fff;border-bottom:1px solid var(--g100);padding:0 1.5rem; }
.h-mid-in { max-width:1280px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:68px; }
.site-logo { display:flex;align-items:center;gap:.65rem;text-decoration:none; }
.logo-icon { font-size:1.75rem;line-height:1; }
.logo-texts { display:flex;flex-direction:column;line-height:1; }
.logo-name { font-size:1.1rem;font-weight:900;color:var(--navy); }
.logo-sub { font-size:.62rem;color:var(--g400);letter-spacing:.05em;margin-top:2px; }
.h-mid-acts { display:flex;align-items:center; }
.h-act-lnk {
  background:none;border:none;font-family:var(--font);font-size:.78rem;
  color:var(--g600);padding:.3rem .7rem;cursor:pointer;text-decoration:none;
  transition:color .18s;border-right:1px solid var(--g200);line-height:1;
}
.h-act-lnk:last-child { border-right:none; }
.h-act-lnk:hover { color:var(--blue); }
.h-act-main { color:var(--blue)!important;font-weight:700; }
.h-welcome { font-size:.78rem;color:var(--g400);padding:.3rem .7rem;border-right:1px solid var(--g200); }

/* 3줄: 네비 + 검색/전체메뉴 */
.h-bot { background:#fff;border-bottom:2px solid var(--blue);padding:0 1.5rem; }
.h-bot-in { max-width:1280px;margin:0 auto;display:flex;align-items:stretch;justify-content:space-between;height:44px; }
.h-nav { display:flex;align-items:stretch;flex:1; }
.nav-item { position:relative;display:flex;align-items:center; }
.nav-item > a {
  display:flex;align-items:center;gap:3px;
  color:var(--g700);text-decoration:none;
  padding:.25rem .95rem;font-size:.87rem;font-weight:600;
  height:100%;transition:color .18s;white-space:nowrap;
  border-bottom:2px solid transparent;margin-bottom:-2px;
}
.nav-item > a:hover,.nav-item > a.current { color:var(--blue);border-bottom-color:var(--blue); }
.nav-arrow { font-size:.52rem;opacity:.45;transition:transform .2s;display:inline-block; }
.nav-item:hover > a .nav-arrow { transform:rotate(180deg); }
.nav-dropdown {
  display:none;position:absolute;top:100%;left:0;
  background:#fff;border:1px solid var(--g200);
  border-radius:0 0 8px 8px;padding:.35rem 0;
  min-width:130px;box-shadow:0 6px 18px rgba(0,0,0,.1);z-index:600;
}
.nav-dropdown a {
  display:block;color:var(--g700);text-decoration:none;
  padding:.48rem 1rem;font-size:.82rem;transition:all .15s;
}
.nav-dropdown a:hover { color:var(--blue);background:var(--lb);padding-left:1.25rem; }
.nav-item:hover > .nav-dropdown { display:block; }
.h-bot-right { display:flex;align-items:center;gap:.2rem; }
.h-icon-btn {
  background:none;border:none;cursor:pointer;font-family:var(--font);
  color:var(--g600);font-size:.78rem;display:flex;align-items:center;
  gap:.3rem;padding:.3rem .6rem;border-radius:var(--rs);transition:all .18s;
}
.h-icon-btn:hover { background:var(--g100);color:var(--navy); }

/* ── 검색창 (전체 화면 오버레이) ── */
.search-bar {
  display:none;position:fixed;top:0;left:0;right:0;bottom:0;z-index:600;
  background:rgba(15,32,64,.92);backdrop-filter:blur(8px);
  padding:5.5rem 1.5rem 1.5rem;
  overflow-y:auto;
  animation:sbFadeIn .2s ease;
}
.search-bar.open { display:block; }
@keyframes sbFadeIn { from{opacity:0} to{opacity:1} }
.sb-inner { max-width:720px;margin:0 auto; }
.sb-wrap {
  display:flex;align-items:center;gap:.6rem;
  background:#fff;border:2px solid var(--blue);border-radius:50px;padding:.65rem 1.1rem;
  box-shadow:0 12px 30px rgba(0,0,0,.25);
}
.sb-wrap input { flex:1;border:none;outline:none;font-size:1rem;font-family:var(--font);color:var(--g800); }
.sb-close-x { background:#f1f5f9;border:none;cursor:pointer;color:var(--g600);font-size:.9rem;padding:.3rem .55rem;border-radius:50%;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center }
.sb-close-x:hover { background:#e2e8f0; color:var(--g800); }
.sb-tags { display:flex;align-items:center;gap:.4rem;margin-top:.9rem;flex-wrap:wrap;justify-content:center; }
.sb-tags span { font-size:.82rem;color:rgba(255,255,255,.75);font-weight:600; }
.sb-tags button {
  background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.25);color:#fff;
  border-radius:50px;padding:.32rem .85rem;font-size:.8rem;cursor:pointer;
  font-family:var(--font);transition:all .18s;
}
.sb-tags button:hover { background:var(--cyan);color:#fff;border-color:var(--cyan);transform:translateY(-1px); }
.sb-close-arrow { display:none; }

/* 🆕 v77.23 — 모바일 검색창 밀림 픽스 */
@media (max-width: 768px) {
  .search-bar {
    padding: 4.5rem 1rem 1.5rem !important;
  }
  .sb-inner {
    width: 100% !important;
    max-width: 100% !important;
    box-sizing: border-box;
  }
  .sb-wrap {
    padding: .55rem .9rem !important;
    box-sizing: border-box;
    width: 100% !important;
  }
  .sb-wrap input {
    font-size: 16px !important;       /* iOS 자동확대 방지 */
    min-width: 0 !important;
  }
  .sb-tags {
    margin-top: 1.1rem !important;
    gap: .4rem !important;
    justify-content: center !important;
  }
  .sb-tags > span {
    display: block !important;
    width: 100% !important;
    text-align: center !important;
    margin-bottom: .35rem;
  }
  .sb-tags button {
    padding: .35rem .9rem !important;
    font-size: .82rem !important;
  }
}

/* 🆕 검색 결과 드롭다운 */
.sb-results {
  max-width:880px; margin:1.25rem auto 0;
  background:#fff; border-radius:14px; overflow:hidden;
  box-shadow:0 24px 60px rgba(0,0,0,.35);
  display:none;
}
.sb-results.show { display:block; animation:sbFadeIn .2s ease; }
.sb-results-head { padding:.85rem 1.25rem; background:#f8fafc; font-size:.85rem; color:#64748b; font-weight:600; border-bottom:1px solid #e2e8f0; }
.sb-results-body { max-height:60vh; overflow-y:auto; }
.sb-result-item { display:flex; gap:1rem; padding:.95rem 1.25rem; border-bottom:1px solid #f1f5f9; text-decoration:none; color:inherit; transition:background .15s; align-items:center; }
.sb-result-item:hover { background:#f8fafc; }
.sb-result-img { flex:0 0 72px; height:54px; border-radius:8px; overflow:hidden; background:linear-gradient(135deg,var(--navy),var(--blue)); display:flex;align-items:center;justify-content:center;font-size:1.4rem;color:#fff; }
.sb-result-img img { width:100%; height:100%; object-fit:cover; }
.sb-result-body { flex:1; min-width:0; }
.sb-result-title { font-size:.98rem; font-weight:700; color:var(--navy); margin:0 0 .25rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sb-result-title mark { background:rgba(27,111,216,.15); color:var(--blue); padding:0 3px; border-radius:3px; }
.sb-result-meta { font-size:.8rem; color:#64748b; display:flex; gap:.7rem; flex-wrap:wrap; }
.sb-result-meta strong { color:var(--blue); }
.sb-results-empty { padding:2.5rem 1rem; text-align:center; color:#94a3b8; font-size:.92rem; }
.sb-results-empty div:first-child { font-size:2.5rem; margin-bottom:.5rem; }
.sb-results-more { display:block; padding:.9rem 1rem; text-align:center; background:var(--navy); color:#fff !important; font-weight:700; font-size:.9rem; text-decoration:none !important; }
.sb-results-more:hover { background:var(--blue); }

/* ── 퀵메뉴 (우측 고정) ── */
/* 🆕 v66.1 PPT2 #2: 퀵 메뉴 사이즈 2배 (50px → 100px) */
.quick-menu {
  position:fixed;right:0;bottom:120px;z-index:400;
  display:flex;flex-direction:column;gap:1px;
  opacity:.8;transition:opacity .3s;
}
.quick-menu:hover { opacity:1; }
.qm-btn {
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  width:100px;min-height:100px;background:var(--navy);color:#fff;
  border:none;cursor:pointer;text-decoration:none;transition:background .18s;gap:4px;
}
.qm-btn:first-child { border-radius:12px 0 0 0; }
.qm-btn:last-child { border-radius:0 0 0 12px; }
.qm-btn:hover { background:var(--blue); }
.qm-icon { font-size:1.7rem;line-height:1; }
.qm-txt { font-size:.78rem;line-height:1.3;text-align:center;color:rgba(255,255,255,.85); }

/* ── 전체메뉴 ── */
.fullmenu-overlay { display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:700; }
.fullmenu-overlay.open { display:block; }
.fullmenu {
  position:fixed;top:0;right:-480px;width:480px;height:100vh;
  background:#fff;z-index:800;box-shadow:-4px 0 24px rgba(0,0,0,.15);
  transition:right .3s ease;overflow-y:auto;
}
.fullmenu.open { right:0; }
.fm-inner { padding:1.75rem 2rem; }
.fm-close {
  position:absolute;top:1rem;right:1.1rem;background:none;border:none;
  font-size:1.3rem;cursor:pointer;color:var(--g500);padding:.25rem .5rem;
}
.fm-close:hover { color:var(--navy); }
.fm-title { font-size:1.2rem;font-weight:900;color:var(--navy);margin-bottom:1.5rem;padding-bottom:.75rem;border-bottom:2px solid var(--lb); }
.fm-grid { display:grid;grid-template-columns:1fr 1fr;gap:1.5rem; }
.fm-col { display:flex;flex-direction:column;gap:.3rem; }
.fm-cat { font-size:.83rem;font-weight:700;color:var(--blue);margin-bottom:.3rem;padding-bottom:.3rem;border-bottom:1px solid var(--lb); }
.fm-col a { font-size:.8rem;color:var(--g700);text-decoration:none;padding:.18rem 0;transition:color .15s; }
.fm-col a:hover { color:var(--blue); }

/* 🆕 v77.23 — 전체메뉴 상단 검색창 */
.fm-search{
  display:flex;
  align-items:center;
  gap:.5rem;
  background:#f8fafc;
  border:1.5px solid #e2e8f0;
  border-radius:50px;
  padding:.6rem 1rem;
  margin-bottom:1.5rem;
}
.fm-search:focus-within{border-color:var(--blue, #1B6FD8); background:#fff}
.fm-search-input{
  flex:1;
  border:none;
  background:transparent;
  outline:none;
  font-size:.9rem;
  font-family:inherit;
  color:var(--g800, #1e293b);
  min-width:0;
}
.fm-search-go{
  display:flex;
  align-items:center;
  justify-content:center;
  width:30px;
  height:30px;
  border:none;
  background:var(--blue, #1B6FD8);
  color:#fff;
  border-radius:50%;
  cursor:pointer;
  flex-shrink:0;
  transition:transform .15s;
}
.fm-search-go:hover{transform:scale(1.05)}

/* 🆕 v77.23 — 모바일 전체메뉴 전체화면 + 카테고리 1열 */
@media (max-width: 768px) {
  .fullmenu {
    width: 100% !important;
    right: -100% !important;
  }
  .fullmenu.open { right: 0 !important; }
  .fm-inner {
    padding: 1.5rem 1.25rem 5rem !important;
  }
  .fm-title {
    font-size: 1.15rem !important;
  }
  .fm-search-input {
    font-size: 16px !important;     /* iOS 자동확대 방지 */
  }

  /* 🆕 v77.36 — 모바일 전체메뉴 2분할 레이아웃 (좌측: 카테고리 / 우측: 하위 항목)
                 PPT5 슬라이드 — 멀티캠퍼스식 카테고리 탭 + 콘텐츠 분할
     🆕 v77.37/39 — linear-gradient 회피책 시도했으나 근본 원인 미해결
     🆕 v77.40 — 진짜 원인 발견 및 해결 (DevTools grid 오버레이로 확인):
                  display: contents로 .fm-col 해체 → 자식 .fm-cat / a들이 순서대로 grid 자식이 됨.
                  활성 카테고리의 a들이 grid-column:2로 6행을 만들면서, 그 6행의 좌측 셀(열1)이 비어버림.
                  뒤따라오는 카테고리(.fm-cat)들은 grid-auto-flow:row 기본값 때문에
                  빈 좌측 셀로 안 가고 한참 아래쪽 새 행에 들어감 → 좌측 중간이 휑.
                  → grid-auto-flow: dense 로 변경.
                    dense는 빈 슬롯을 거꾸로 채워서 후속 카테고리들이 빈 좌측 셀에 자동 배치됨. */
  .fm-grid {
    display: grid !important;
    grid-template-columns: 36% 1fr !important;
    grid-auto-flow: dense !important;     /* 🆕 v77.40 — 핵심 픽스 */
    gap: 0 !important;
    border-top: 1px solid #e2e8f0;
    border-bottom: 1px solid #e2e8f0;
    max-height: calc(100vh - 220px);
    overflow: hidden;
    padding: 0 !important;
    margin: 0 -1.25rem !important;     /* fm-inner 패딩 상쇄해서 좌우 풀폭 */
    background: #fff;
  }
  /* 각 .fm-col을 카테고리 탭 + 내용 분리 */
  .fm-col {
    display: contents !important;       /* fm-col 자체 박스 해체, 자식들이 직접 grid 자식이 됨 */
  }
  /* 카테고리 탭 (좌측 컬럼에 누적) */
  .fm-cat {
    grid-column: 1 !important;
    margin: 0 !important;
    padding: 1rem .9rem !important;
    font-size: .92rem !important;
    font-weight: 600 !important;
    color: #475569 !important;
    background: #f8fafc !important;
    border-bottom: 1px solid #e2e8f0 !important;
    border-right: 1px solid #e2e8f0;
    cursor: pointer;
    transition: background .15s, color .15s;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  .fm-cat:hover { background: #f1f5f9 !important; }
  .fm-cat.is-active {
    background: var(--esg-blue, #1B6FD8) !important;
    color: #fff !important;
    font-weight: 800 !important;
    border-radius: 0 50px 50px 0 !important;
    margin-right: -1px !important;       /* 우측 영역과 자연스럽게 연결 */
  }
  /* 하위 링크 — 기본 숨김, 활성 카테고리만 표시 */
  .fm-col a {
    grid-column: 2 !important;
    display: none !important;             /* 기본 숨김 */
    padding: 1rem 1rem 1rem 1.25rem !important;
    font-size: .95rem !important;
    color: #0F2040 !important;
    text-decoration: none !important;
    border-bottom: 1px solid #f1f5f9 !important;
    background: #fff !important;
    font-weight: 600 !important;
  }
  .fm-col a:active { background: #f8fafc !important; }
  /* 활성 컬럼의 링크만 표시 */
  .fm-col.is-active a {
    display: flex !important;
    align-items: center;
    justify-content: space-between;
  }
  /* 화살표 추가 (선택적) */
  .fm-col.is-active a::after {
    content: '›';
    color: #cbd5e1;
    font-size: 1.2rem;
    font-weight: 400;
    margin-left: .5rem;
  }

  /* 사이드바 스크롤 (카테고리가 많을 때) */
  .fm-grid {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ── HERO ── */
.hero {
  background: linear-gradient(135deg, var(--navy) 0%, #1a3558 55%, #0d4fa0 100%);
  min-height: 540px; display: flex; align-items: center;
  position: relative; overflow: hidden;
}
.hero::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse at 70% 50%, rgba(0,194,209,.15), transparent 60%);
}
.hero-in {
  max-width: 1280px; margin: 0 auto; padding: 3.5rem 1.5rem;
  position: relative; z-index: 1; display: flex; gap: 3rem; align-items: center;
}
.hero-txt { flex: 1; }
.hero-badge {
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(0,194,209,.15); border: 1px solid rgba(0,194,209,.4);
  color: var(--cyan); padding: .35rem .9rem; border-radius: 50px;
  font-size: .78rem; font-weight: 600; margin-bottom: 1.25rem;
}
.hero-h { font-size: 2.6rem; font-weight: 900; color: #fff; line-height: 1.2; margin-bottom: .85rem; }
.hero-h span { background: linear-gradient(135deg, var(--cyan), #7EC8E3); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
.hero-p { color: rgba(255,255,255,.65); font-size: 1rem; line-height: 1.75; margin-bottom: 1.75rem; }
.hero-btns { display: flex; gap: .75rem; flex-wrap: wrap; }
.hero-stats { display: flex; gap: 2rem; margin-top: 2rem; }
.hs-num { font-size: 1.7rem; font-weight: 900; color: #fff; }
.hs-lbl { font-size: .75rem; color: rgba(255,255,255,.45); margin-top: 2px; }

/* ── SECTIONS ── */
.sec { padding: 4.5rem 1.5rem; }
.sec-in { max-width: 1280px; margin: 0 auto; }
.sec-hd { text-align: center; margin-bottom: 2.5rem; }
.sec-hd.sec-hd-row { text-align: left; }
.sec-hd.sec-hd-row .sec-badge { margin-bottom: .55rem; }
.sec-badge { display: inline-block; background: var(--lb); color: var(--blue); padding: .28rem .85rem; border-radius: 50px; font-size: .75rem; font-weight: 700; margin-bottom: .6rem; }
.sec-title { font-size: 1.8rem; font-weight: 900; color: var(--navy); margin-bottom: .4rem; }
.sec-desc { color: var(--g500); font-size: .9rem; }

/* ── COURSE CARDS ── */
/* 🆕 v66.17 (사용자 요청): 모든 카드의 요소가 같은 위치에 정렬되도록
   - .cg 그리드의 모든 카드가 같은 높이 (auto-rows: 1fr)
   - .cc 카드 내부도 flex column → cc-body-action(수강신청)을 항상 하단에 고정
   - 강사 정보 등이 비어도 다른 요소가 위로 안 올라옴 */
.cg { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.25rem; grid-auto-rows: 1fr; }
.cc { background: #fff; border-radius: var(--r); overflow: hidden; border: 1px solid var(--g200); transition: all .22s; cursor: pointer; box-shadow: var(--sh0); display: flex; flex-direction: column; }
.cc-link { display: flex; flex-direction: column; flex: 1; }   /* 🆕 v66.17: 본문 영역이 늘어나서 빈 공간 채움 */
.cc-link .cc-body { flex: 1; }                                  /* 🆕 v66.17: 본문이 가용 공간 확장 → 수강신청은 항상 하단 */
/* 🆕 v76.2 (PPT PC) — page-courses.php 카드만 콘텐츠 크기로 (가격 위 빈 공간 제거)
   대상: .course-card (page-courses.php의 cc 카드만 추가로 가진 클래스)
        신규/인기과정의 .cc 카드는 .course-card 클래스가 없으므로 영향 없음
   효과: cc-body가 콘텐츠 크기만큼만 차지 → cc-foot(가격+신청버튼)이 콘텐츠 바로 뒤에 붙음
   카드 그리드 행 정렬은 그대로 유지 (빈 공간은 카드 외부, 카드 자체는 콘텐츠 크기) */
.cc.course-card .cc-link,
.cc.course-card .cc-link .cc-body {
  flex: 0 0 auto;
}
.cc:hover { transform: translateY(-4px); box-shadow: var(--sh1); border-color: var(--blue); }
/* 🆕 v68.44 — 모든 카드 썸네일 동일 비율 강제 (aspect-ratio + 부모 컨테이너 고정) */
.cc-thumb {
    width: 100% !important;
    aspect-ratio: 4 / 3 !important;
    height: auto !important;
    background: linear-gradient(135deg, var(--navy), var(--blue));
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 3.2rem;
    position: relative;
    overflow: hidden;
    flex-shrink: 0;
    flex-grow: 0;
}
.cc-thumb img {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
    object-position: center !important;
    display: block !important;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: crisp-edges;
}
.cc-label { position: absolute; top: .65rem; left: .65rem; background: var(--cyan); color: var(--navy); padding: 1px 7px; border-radius: 50px; font-size: .67rem; font-weight: 700; }
/* 🆕 v66.20 (사용자 요청): cc-body flex column + spacer 사용
   → 콘텐츠 양 무관 별점/가격이 카드 하단 같은 위치에 정렬됨 */
/* 🆕 v66.21 (사용자 요청): 빈 여백 축소 — 카드를 더 컴팩트하게 */
/* 🆕 v66.29 (사용자 요청): 위/아래 빈 여백 완전 제거 (썸네일↔제목, 가격↔수강신청) */
.cc-body { padding: .3rem .9rem .35rem; display: flex; flex-direction: column; }   /* 🆕 v76.1 (PPT PC S8): padding-bottom 0 → .35rem 추가 (가격이 카드 하단에 너무 붙는 문제) */
/* 🆕 v75.19 — cc-spacer 무력화 (별점/가격 위 빈 공간 제거)
   변경 전: flex: 1 1 auto → 가용 공간을 모두 차지해 별점/가격을 카드 하단으로 밀어 빈 공간 발생
   변경 후: flex: 0, 최소 높이만 → 별점/가격이 콘텐츠 바로 뒤에 붙음 (컴팩트한 카드)
   카드 사이 정렬은 그리드의 grid-auto-rows로 자동 통일됨 */
.cc-spacer { flex: 0 0 auto; min-height: .25rem; height: .25rem; }
.cc-cat { font-size: .72rem; color: var(--blue); font-weight: 600; margin-top: .5rem; margin-bottom: .15rem; min-height: .87rem; }   /* 🆕 v68.41 #5-6: 빈 카테고리도 같은 자리 (PPT5 라인 정렬 요구) */
/* 🆕 v75.16 — cc-cat-empty 완전 숨김 (PC + 모바일 공통)
   사유: v75.15에서 cc-meta-top이 추가되어 카드 상단 자리를 항상 채움
         cc-cat이 비어있어도 위쪽 메타가 존재하므로 빈 자리 유지 불필요
         기존 v68.41의 라인 정렬 요구는 cc-meta-top이 대체함 */
.cc-cat-empty { display: none !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; }

/* ── 카드 상단 태그 (대면 | 중급 | 교재제공 | 인재키움) ── */
.cc-tags { display: flex; align-items: center; flex-wrap: wrap; gap: .35rem; margin-bottom: .5rem; font-size: .72rem; font-weight: 600; line-height: 1.3; }
.cc-tag { color: var(--blue); }
.cc-tag-accent { color: #ef4444; }
.cc-tag-sep { color: #cbd5e1; font-weight: 400; }

.cc-name { font-size: 1.4rem; font-weight: 800; color: var(--g800); margin: 0 0 .3rem; line-height: 1.4; letter-spacing: -.02em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 3.92rem; word-break: keep-all; }   /* 🆕 v66.28: 가로 카드와 동일하게 키움 (.95→1.4rem, min-height 2.66→3.92rem) */
.cc-summary { font-size: .78rem; color: var(--g500); margin: 0 0 .5rem; line-height: 1.5; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; min-height: 2.34rem; }   /* 🆕 v68.61: min-height 추가 (PPT 3-1 #1: 글씨 밀림 방지) */

/* ── 카드 메타 (강사/신청기간/교육기간) ── */
.cc-meta { margin: 0 0 .55rem !important; padding: .55rem 0 0 !important; border-top: 1px solid var(--g100); display: flex !important; flex-direction: column !important; gap: .3rem !important; list-style: none; }   /* 🆕 v66.21: padding-top .7→.55, margin-bottom .75→.55, gap .38→.3 */
.cc-meta-row { display: flex !important; align-items: baseline !important; gap: .65rem !important; font-size: .78rem !important; line-height: 1.45 !important; margin: 0 !important; padding: 0 !important; }
.cc-meta-row dt { margin: 0 !important; padding: 0 !important; color: var(--g400) !important; font-weight: 500 !important; flex-shrink: 0 !important; width: 64px !important; font-size: .76rem !important; }
.cc-meta-row dd { margin: 0 !important; padding: 0 !important; color: var(--g700) !important; font-weight: 600 !important; flex: 1 !important; min-width: 0 !important; word-break: keep-all; }
/* 🆕 v66.17: 메타 빈 줄 (강사/신청기간 정보 없을 때 자리만 차지) */
.cc-meta-row-empty { min-height: 1.13rem !important; visibility: hidden; }
/* 🆕 v66.18: summary, cat 빈 자리 (모든 카드 위치 통일) */
/* 🆕 v66.21: 마진 축소 (.5 → .35) */
.cc-summary-empty { min-height: 2.34rem; visibility: hidden; margin-bottom: .35rem; }
/* 🆕 v66.30: cc-cat-empty은 위에서 재정의됨 (height 0) */

.cc-inst { font-size: .77rem; color: var(--g500); margin-bottom: .65rem; }
/* 🆕 v76.2 (PPT PC) — cc-foot 위치 정상화
   기존: margin-top:.2rem만 있어서 cc-body의 flex가 콘텐츠와 cc-foot 사이를 늘려서
         가격 위에 빈 공간이 생김 (캡처의 파란 박스 위쪽).
   변경: cc-foot을 콘텐츠 바로 뒤에 붙이고, 위/아래 padding 균등하게.
         카드 그리드 행 정렬은 별도 처리 (cc-body가 flex 1로 늘어나도 cc-foot은 위로 따라옴) */
.cc-foot { display: flex; align-items: center; justify-content: space-between; padding: .6rem 0 .5rem; border-top: 1px solid var(--g100); margin-top: .65rem; }
.cc-price { font-size: .95rem; font-weight: 700; color: var(--navy); }
.cc-price.free { color: var(--ok); }
.cc-btn { background: var(--lb); color: var(--blue); border: none; padding: .35rem .75rem; border-radius: 6px; font-size: .75rem; font-weight: 600; cursor: pointer; transition: all .18s; }
.cc-btn:hover { background: var(--blue); color: #fff; }

/* 🆕 과정 카드 - 태그 뱃지 (신규 과정 관리 태그) */
.cc .cc-badges { display: flex; flex-wrap: wrap; gap: .3rem; margin: .65rem 0 .5rem; align-items: center; }
/* 🆕 v66.17: 태그 빈 영역 (위치 일관성을 위해 자리 차지) */
.cc .cc-badges-empty { min-height: 1.55rem; visibility: hidden; }
.cc .cc-badge { display: inline-block; padding: 3px 9px; background: #f1f5f9; color: #475569; font-size: .72rem; font-weight: 500; border-radius: 14px; line-height: 1.3; border: 1px solid #e2e8f0; white-space: nowrap; }

/* 🆕 과정 카드 - 별점 + 리뷰수 */
.cc .cc-rating { display: flex; align-items: center; gap: .3rem; margin-bottom: .35rem; font-size: .82rem; }   /* 🆕 v66.21: margin .55 → .35 */
.cc .cc-rating-star { color: #fbbf24; font-size: .95rem; line-height: 1; }
.cc .cc-rating-num { color: #0F2040; font-weight: 700; }
.cc .cc-rating-sep { color: #94a3b8; margin-left: .4rem; font-size: .82rem; }
.cc .cc-rating-count { color: #64748b; font-weight: 500; }

/* 🆕 과정 카드 - 큰 가격 */
.cc .cc-price-big { font-size: 1.15rem; font-weight: 800; color: #0F2040; margin-bottom: .15rem; line-height: 1.2; }

/* 🆕 카드 링크 - 전체 덮개 */
.cc .cc-link { display: block; text-decoration: none; color: inherit; }
.cc .cc-link:hover { text-decoration: none; }

/* 🆕 액션 영역 (하트 + 수강신청) - <a> 바깥 영역 */
.cc .cc-body-action { padding: 0 .9rem .9rem; margin-top: 0; display: block; }   /* 🆕 v66.29: margin-top -.3 → 0, padding-bottom .75 → .9 (수강신청 자체 아래는 유지) */
.cc .cc-action { display: flex; gap: .5rem; align-items: center; padding-top: 0; border-top: none; }   /* 🆕 v66.29: padding-top .5 → 0, border-top 제거 (가격↔버튼 빈 공간 제거) */
.cc .cc-heart { width: 42px; height: 42px; flex-shrink: 0; border: 1px solid #e2e8f0; background: #fff; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all .18s; padding: 0; }
.cc .cc-heart:hover { border-color: #ef4444; background: #fef2f2; }
.cc .cc-heart.on { background: #fef2f2; border-color: #fecaca; }
.cc .cc-btn-apply { flex: 1; background: #0F2040; color: #fff; border: none; padding: .7rem 1rem; border-radius: 8px; font-size: .88rem; font-weight: 700; cursor: pointer; transition: all .18s; height: 42px; line-height: 1; }
.cc .cc-btn-apply:hover { background: #1e3a5f; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(15,32,64,.25); }

/* 🆕 가로형 카드 그리드 (2열, 같은 높이) */
.cg.cg-h { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
@media (max-width: 900px) { .cg.cg-h { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════
   🎨 가로형 카드 (이미지 2 스타일) - .cch
   ═══════════════════════════════════════════ */
.cch {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 1rem;
    background: #fff;
    border-radius: 14px;
    overflow: hidden;
    border: 1px solid #f1f5f9;
    text-decoration: none;
    color: inherit;
    transition: all .22s;
    padding: .85rem;
    align-items: stretch;
    /* 🆕 v68.44 — 카드 높이 = 썸네일(180) + 패딩(.85rem×2 = ~27px) ≈ 207px */
    height: 210px;
}
.cch:hover {
    transform: translateY(-3px);
    box-shadow: 0 12px 30px rgba(15,32,64,.08);
    border-color: #bfdbfe;
}

/* 🆕 v68.44 — 가로 카드 썸네일: 정확히 240×180 (4:3) */
.cch-thumb {
    position: relative;
    width: 240px !important;
    height: 180px !important;
    aspect-ratio: 4 / 3 !important;
    overflow: hidden;
    background: linear-gradient(135deg, #0F2040, #1B6FD8);
    border-radius: 12px;
    flex-shrink: 0;
    align-self: center;            /* 🆕 v68.44: 카드 중앙 정렬 (위/아래 같은 여백) */
}
.cch-thumb img {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
    object-position: center !important;
    display: block !important;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: crisp-edges;
}
.cch-thumb-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}
.cch-thumb-icon span {
    font-size: 3.5rem;
}

/* 본문 영역 - 우측 */
/* 🆕 v66.10 PPT2 #19: 카드에 padding이 추가됨에 따라 본문 padding은 줄임
   - justify-content: space-between → 4행(메타:제목:날짜:하단) 균등 분배
   - 본문 padding은 좌측만 약간 (썸네일과 본문 사이 gap이 카드에서 처리됨) */
.cch-body {
    padding: .25rem .25rem .25rem .5rem;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-width: 0;
    height: 100%;
    overflow: hidden;
}

/* 상단 메타 (대면 | 중급 | 교재제공 | 인재키움) */
.cch-meta-top {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    font-size: .82rem;
    font-weight: 700;
    line-height: 1.3;
    margin-bottom: .15rem;
}
.cch-meta-item { white-space: nowrap; }
.cch-sep { color: #cbd5e1; font-weight: 400; }

/* 🆕 v75.15 — 세로형 카드용 메타 가로 배열 (포스터 ↓ / 제목 ↑)
   사용자 요청: 카드 빈 공간에 "온라인 | 초급 | 교재미제공 | 재밌음" 형태 표시 */
.cc-meta-top {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem;
    font-size: .78rem;
    font-weight: 700;
    line-height: 1.3;
    margin-bottom: .35rem;
    padding-top: .15rem;
}
.cc-meta-top-item { white-space: nowrap; }
.cc-meta-sep { color: #cbd5e1; font-weight: 400; }
.cc-meta-top-empty { min-height: 1rem; }

/* 제목 */
/* 🆕 v66.13 (사용자 요청): 과정명만 크기 키움 (1.1rem → 1.4rem) */
/* 🆕 v66.14 (사용자 요청): 항상 2줄 높이 확보 (짧은 제목도 빈 줄 1줄 여유 둠)
   → min-height = font-size(1.4rem) × line-height(1.4) × 2줄 = 약 3.92rem ≈ 4rem
   → 글씨 비율 1:2:1:1에서 "2" 영역 시각적으로 보장 */
.cch-title {
    font-size: 1.4rem;
    font-weight: 800;
    color: #0F2040;
    line-height: 1.4;
    letter-spacing: -.02em;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: keep-all;
    min-height: 3.92rem;            /* 🆕 v66.14: 짧은 제목도 2줄 높이 확보 */
}

/* 기간 (4/27~4/30, 5/26~5/29) */
.cch-period {
    font-size: .85rem;
    color: #94a3b8;
    font-weight: 500;
    margin-top: -.15rem;
}

/* 🆕 v66.12 PPT2 #19 추가: 정보 영역 (강사 / 신청기간 / 개폐강 확정일)
   - 사용자 요청: "신청기간처럼 글과 숫자로, 배경 필요없음"
   - 라벨(왼쪽 회색) + 값(검정) 형태 */
.cch-info-block {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}
.cch-info-row {
    display: flex;
    align-items: baseline;
    gap: .85rem;
    font-size: .9rem;
    line-height: 1.4;
}
.cch-info-label {
    flex-shrink: 0;
    min-width: 5rem;        /* 🆕 v66.13: 최소 너비만 (라벨 길이에 따라 자동 확장) */
    color: #64748b;
    font-weight: 500;
    font-size: .85rem;
    white-space: nowrap;    /* 🆕 v66.13: "개폐강 확정일" 등 긴 라벨도 한 줄로 (사용자 요청) */
}
.cch-info-value {
    color: #0F2040;
    font-weight: 600;
}
/* 🆕 v68.61 (PPT 3-1 #2): 빈 메타 자리 — 보이지 않지만 카드 높이 통일 위해 자리 차지 */
.cch-info-empty {
    visibility: hidden;
    pointer-events: none;
}
/* 🆕 v66.14 (사용자 요청): 개폐강 확정일만 글씨 색 강조 (다른 정보와 구분)
   - 라벨: 네이비 진하게 (다른 라벨은 회색 #64748b)
   - 값: 시안색 강조 (다른 값은 #0F2040 네이비) */
/* 🆕 v66.15 (사용자 변경): 색 강조 제거 → 모든 정보(강사/신청기간/개폐강) 색 통일
   라벨 모두 같은 회색, 값 모두 같은 검정 */
.cch-info-confirmed .cch-info-label {
    color: #64748b;            /* v66.15: 다른 라벨과 동일 (회색) */
    font-weight: 500;
}
.cch-info-confirmed .cch-info-value {
    color: #0F2040;            /* v66.15: 다른 값과 동일 (검정/네이비) */
    font-weight: 600;
}

/* 하단 영역 (태그 + 별점) */
.cch-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: auto;
    padding-top: .75rem;
}

/* 하단 태그 (알고리즘 / 신경망 / 퍼셉트론) */
.cch-tags {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    min-width: 0;
}
.cch-tag {
    display: inline-block;
    padding: 4px 11px;
    background: #f1f5f9;
    color: #475569;
    font-size: .75rem;
    font-weight: 500;
    border-radius: 14px;
    line-height: 1.3;
    white-space: nowrap;
}

/* 우측 별점 (★ 4.8 💬 241) */
.cch-rating {
    display: flex;
    align-items: center;
    gap: .25rem;
    flex-shrink: 0;
    font-size: .88rem;
}
.cch-star {
    color: #fbbf24;
    font-size: 1rem;
    line-height: 1;
}
.cch-rating-num {
    color: #0F2040;
    font-weight: 700;
    margin-right: .35rem;
}
.cch-comment {
    color: #94a3b8;
    font-size: .85rem;
}
.cch-rating-count {
    color: #64748b;
    font-weight: 500;
}

@media (max-width: 600px) {
    /* 🆕 v18: 가로형 카드를 모바일에서는 세로로 전환 (세로형처럼 보이게) */
    .cch { grid-template-columns: 1fr !important; min-height: auto !important; }
    .cch-thumb {
        width: 100% !important;
        max-width: 100% !important;
        aspect-ratio: 16 / 9 !important;
        border-radius: 14px 14px 0 0 !important;
    }
    .cch-body { padding: 1rem 1.1rem; gap: .4rem; }
    .cch-title { font-size: 1rem; line-height: 1.4; }
    .cch-meta-top { font-size: .72rem; gap: .35rem; flex-wrap: wrap; }
    .cch-tag { font-size: .7rem; padding: 3px 8px; }
    .cch-bottom { padding-top: .4rem; }
}

/* ── 신규 과정 슬라이더 ── */
.sec-hd-row { display:flex;align-items:flex-end;justify-content:space-between;gap:1.5rem;flex-wrap:wrap;margin-bottom:1.5rem }
.nc-ctrl { display:flex;align-items:center;gap:.6rem;flex-shrink:0 }
/* 🆕 v19 PPT3 #9: 더보기/전체보기 버튼 - 원형 pill 스타일 */
.nc-more { 
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .86rem;
    color: var(--g700);
    text-decoration: none;
    font-weight: 600;
    padding: .5rem 1.1rem;
    border: 1.5px solid var(--g300);
    border-radius: 50px;
    background: #fff;
    margin-right: .75rem;
    transition: all .18s;
    line-height: 1;
}
.nc-more:hover { 
    color: var(--blue);
    border-color: var(--blue);
    background: #eff6ff;
    transform: translateY(-1px);
}
.nc-nav { width:48px;height:48px;border-radius:50%;border:none;background:#e2e8f0;color:#94a3b8;cursor:pointer;font-size:1.4rem;font-weight:700;display:inline-flex;align-items:center;justify-content:center;transition:all .2s;line-height:1;padding-bottom:2px }
.nc-nav-on { background:#0F2040;color:#fff;box-shadow:0 4px 12px rgba(15,32,64,.25) }
.nc-nav-on:hover { transform:translateY(-2px);box-shadow:0 6px 16px rgba(15,32,64,.35) }
.nc-nav:disabled,.nc-nav:not(.nc-nav-on){ cursor:not-allowed;opacity:.7 }

.nc-viewport { overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scrollbar-width:none;-ms-overflow-style:none;padding-bottom:.25rem }
.nc-viewport::-webkit-scrollbar { display:none }
.nc-track { display:flex;gap:1.25rem;padding:.25rem .15rem }
.nc-slide { flex:0 0 calc((100% - 3.75rem) / 4);min-width:230px;scroll-snap-align:start;display:flex;flex-direction:column }
.nc-slide .cc { height:100% }

.nc-progress { height:3px;background:#e2e8f0;border-radius:3px;margin-top:1.25rem;overflow:hidden }
.nc-progress-bar { height:100%;background:var(--navy);border-radius:3px;transition:width .2s;width:25% }

@media (max-width:960px){ .nc-slide { flex:0 0 calc((100% - 2.5rem) / 3) } }
@media (max-width:720px){ .nc-slide { flex:0 0 calc((100% - 1.25rem) / 2) } }
@media (max-width:480px){ .nc-slide { flex:0 0 82% } }

/* ── NOTICE ── */
.notice-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
.nb { background: #fff; border-radius: var(--r); padding: 1.25rem; border: 1px solid var(--g200); box-shadow: var(--sh0); }
.nb-title { font-size: .9rem; font-weight: 700; color: var(--navy); margin-bottom: .85rem; display: flex; justify-content: space-between; }
.nb-title a { font-size: .77rem; color: var(--blue); text-decoration: none; font-weight: 400; }
.n-list { list-style: none; }
.n-list li { padding: .55rem 0; border-bottom: 1px solid var(--g100); display: flex; justify-content: space-between; cursor: pointer; }
.n-list li:last-child { border: none; }
.n-list li:hover .n-title { color: var(--blue); }
.n-title { font-size: .84rem; color: var(--g700); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.n-date { font-size: .75rem; color: var(--g400); flex-shrink: 0; margin-left: .5rem; }

/* ── TEACHERS ── */
.tg { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 1.25rem; }
.tc { background: #fff; border-radius: var(--r); padding: 1.25rem .9rem; text-align: center; border: 1px solid var(--g200); transition: all .2s; cursor: pointer; }
.tc:hover { transform: translateY(-3px); box-shadow: var(--sh1); border-color: var(--blue); }
.t-av { width: 70px; height: 70px; border-radius: 50%; background: linear-gradient(135deg, var(--navy), var(--blue)); display: flex; align-items: center; justify-content: center; font-size: 1.6rem; margin: 0 auto .65rem; }
.t-name { font-size: .9rem; font-weight: 700; color: var(--navy); }
.t-desc { font-size: .75rem; color: var(--g500); margin-top: .2rem; }
.t-cnt { font-size: .73rem; color: var(--blue); margin-top: .35rem; font-weight: 600; }

/* ── REVIEWS ── */
.rg { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.25rem; }
.rc { background: #fff; border-radius: var(--r); padding: 1.25rem; border: 1px solid var(--g200); box-shadow: var(--sh0); }
.r-stars { color: #F59E0B; margin-bottom: .6rem; }
.r-txt { font-size: .86rem; color: var(--g700); line-height: 1.6; margin-bottom: .85rem; }
.r-auth { display: flex; align-items: center; gap: .65rem; }
.r-av { width: 34px; height: 34px; border-radius: 50%; background: linear-gradient(135deg, var(--blue), var(--cyan)); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; font-size: .82rem; }
.r-name { font-size: .82rem; font-weight: 600; color: var(--g700); }
.r-course { font-size: .72rem; color: var(--g400); }

/* ── FOOTER ── */
.site-footer { background: var(--navy); color: rgba(255,255,255,.55); font-size: .82rem; padding: 2.5rem 1.5rem 1.25rem; }
.f-in { max-width: 1280px; margin: 0 auto; }
.f-top { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 2rem; margin-bottom: 1.75rem; }
.f-logo { font-size: 1rem; font-weight: 900; color: #fff; margin-bottom: .65rem; }
.f-ct { color: #fff; font-weight: 700; font-size: .85rem; margin-bottom: .6rem; }
.f-links { list-style: none; display: flex; flex-direction: column; gap: .35rem; }
.f-links a { color: rgba(255,255,255,.45); text-decoration: none; font-size: .8rem; }
.f-links a:hover { color: #fff; }
.f-bot { border-top: 1px solid rgba(255,255,255,.1); padding-top: 1.25rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: .5rem; }

/* ── SUBPAGE ── */
.sp-hd { background: linear-gradient(135deg, var(--navy), #1a3558); padding: 2.5rem 1.5rem; text-align: center; }
.sp-title { font-size: 1.65rem; font-weight: 900; color: #fff; margin-bottom: .4rem; }
.sp-desc { color: rgba(255,255,255,.55); font-size: .9rem; }
.breadcrumb { display: flex; gap: .4rem; align-items: center; justify-content: center; margin-top: .6rem; }
.breadcrumb a { color: rgba(255,255,255,.45); text-decoration: none; font-size: .78rem; }
.breadcrumb span { color: rgba(255,255,255,.25); font-size: .78rem; }
.breadcrumb .cur { color: var(--cyan); font-size: .78rem; }
.sp-body { max-width: 1280px; margin: 2rem auto; padding: 0 1.5rem; }

/* ── FORMS ── */
.fg { margin-bottom: 1rem; }
.fl { display: block; font-size: .82rem; font-weight: 600; color: var(--g600); margin-bottom: .35rem; }

/* 🆕 v77.84 — 폼 입력 요소 픽셀 단위 통일 (모든 환경에서 동일 외형)
 *   ▸ input / select / textarea가 native 렌더링 차이로 인해 시각적으로 달라 보이는 문제 해결.
 *   ▸ select에 appearance:none + 커스텀 SVG 화살표로 input과 동일한 박스 렌더링.
 *   ▸ 모바일에서는 font-size 16px로 자동 줌 방지 + 시각 일관성.
 *   ▸ !important로 인라인 style 덮어쓰기 (광범위 영향, 인라인 잔존 스타일 무효화). */
.fi, .fs, .fta,
select.fi, select.fs,
textarea.fi, textarea.fta,
input.fi {
  width: 100% !important;
  box-sizing: border-box !important;
  padding: 0 14px !important;
  height: 44px !important;
  border: 1.5px solid var(--g200) !important;
  border-radius: var(--rs) !important;
  font-size: 14px !important;
  line-height: 1.5 !important;
  font-family: var(--font) !important;
  color: var(--g700) !important;
  background-color: #fff !important;
  transition: border-color .18s, box-shadow .18s !important;
  outline: none !important;
  -webkit-appearance: none !important;
     -moz-appearance: none !important;
          appearance: none !important;
}

/* placeholder도 함께 통일 (font-size 명시로 시각 차이 제거) */
.fi::placeholder, .fs::placeholder, .fta::placeholder,
input.fi::placeholder, textarea.fta::placeholder {
  color: #94a3b8 !important;
  font-weight: 400 !important;
  font-size: 14px !important;
  opacity: 1; /* Firefox 기본 opacity 보정 */
}

/* select 전용 — 커스텀 화살표 (input과 동일한 박스 + 우측 화살표) */
select.fi, select.fs {
  padding-right: 36px !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 12px center !important;
  background-size: 12px 12px !important;
  cursor: pointer;
}

/* textarea 전용 — 높이는 min-height로 (44px은 비현실적) */
textarea.fi, textarea.fta, .fta {
  height: auto !important;
  min-height: 110px !important;
  padding: 12px 14px !important;
  resize: vertical !important;
  line-height: 1.55 !important;
}

/* 포커스 — 통일된 파란색 보더 + 글로우 */
.fi:focus, .fs:focus, .fta:focus,
select.fi:focus, textarea.fi:focus, input.fi:focus {
  border-color: var(--blue) !important;
  box-shadow: 0 0 0 3px rgba(27,111,216,.12) !important;
}

/* disabled / readonly 상태 */
.fi:disabled, .fs:disabled, .fta:disabled,
.fi[readonly], .fs[readonly], .fta[readonly] {
  background-color: #f8fafc !important;
  color: #94a3b8 !important;
  cursor: not-allowed !important;
}

/* 🆕 v77.84 — 모바일 전용 (640px 이하): font-size 16px로 iOS 줌 방지 + 시각 통일 */
@media (max-width: 640px) {
  .fi, .fs, .fta,
  select.fi, select.fs,
  textarea.fi, textarea.fta,
  input.fi {
    font-size: 16px !important;
    height: 46px !important;
    padding: 0 14px !important;
  }
  .fi::placeholder, .fs::placeholder, .fta::placeholder,
  input.fi::placeholder, textarea.fta::placeholder {
    font-size: 16px !important;
  }
  select.fi, select.fs {
    padding-right: 36px !important;
    background-position: right 12px center !important;
  }
  textarea.fi, textarea.fta, .fta {
    height: auto !important;
    min-height: 120px !important;
    padding: 14px !important;
  }
  /* 라벨도 모바일에서 살짝 키움 (가독성) */
  .fl {
    font-size: 13px !important;
    margin-bottom: 6px !important;
  }
}

/* ── TABS ── */
.tab-nav { display: flex; border-bottom: 2px solid var(--g200); margin-bottom: 1.25rem; }
.tab-item { padding: .55rem 1.1rem; cursor: pointer; font-size: .85rem; font-weight: 500; color: var(--g500); border-bottom: 2px solid transparent; margin-bottom: -2px; transition: all .18s; }
.tab-item.on { color: var(--blue); border-bottom-color: var(--blue); font-weight: 700; }
.tab-pane { display: none; }
.tab-pane.on { display: block; }

/* ── CARD ── */
.card { background: #fff; border-radius: var(--r); border: 1px solid var(--g200); box-shadow: var(--sh0); overflow: hidden; }
.card-hd { padding: .9rem 1.1rem; border-bottom: 1px solid var(--g100); display: flex; align-items: center; justify-content: space-between; background: var(--g50); }
.card-title { font-size: .9rem; font-weight: 700; color: var(--navy); }

/* ── TABLE ── */
.tbl { width: 100%; border-collapse: collapse; }
.tbl th { background: var(--g50); padding: .65rem 1rem; text-align: left; font-size: .75rem; color: var(--g500); font-weight: 600; border-bottom: 1px solid var(--g200); white-space: nowrap; }
.tbl td { padding: .72rem 1rem; font-size: .83rem; color: var(--g700); border-bottom: 1px solid var(--g100); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: none; }
.tbl tr:hover td { background: var(--g50); }

/* ── BADGE ── */
.badge { display: inline-block; padding: 2px 8px; border-radius: 50px; font-size: .7rem; font-weight: 700; white-space: nowrap; }
.b-blue { background: #EFF6FF; color: var(--blue); }
.b-green { background: #F0FDF4; color: #059669; }
.b-orange { background: #FFF7ED; color: #EA580C; }
.b-red { background: #FEF2F2; color: var(--err); }
.b-gray { background: var(--g100); color: var(--g500); }

/* ── MYPAGE ── */
.my-grid { display: grid; grid-template-columns: 240px 1fr; gap: 1.5rem; }
.my-side { background: #fff; border-radius: var(--r); padding: 1.25rem; border: 1px solid var(--g200); height: fit-content; }
.my-av { width: 72px; height: 72px; border-radius: 50%; background: linear-gradient(135deg, var(--navy), var(--blue)); display: flex; align-items: center; justify-content: center; font-size: 1.8rem; margin: 0 auto .85rem; }
.my-name { text-align: center; font-size: 1rem; font-weight: 700; color: var(--navy); }
.my-email { text-align: center; font-size: .78rem; color: var(--g400); margin-bottom: 1rem; }
.my-menu { list-style: none; }
.my-menu li { padding: .55rem .7rem; border-radius: var(--rs); cursor: pointer; font-size: .84rem; color: var(--g600); display: flex; align-items: center; gap: .45rem; }
.my-menu li:hover, .my-menu li.on { background: var(--lb); color: var(--blue); font-weight: 600; }
.my-con { background: #fff; border-radius: var(--r); padding: 1.25rem; border: 1px solid var(--g200); }
.my-sec-title { font-size: .95rem; font-weight: 700; color: var(--navy); margin-bottom: 1rem; padding-bottom: .65rem; border-bottom: 2px solid var(--lb); }

/* ── PROGRESS ── */
.prog { background: var(--g100); border-radius: 50px; height: 7px; overflow: hidden; }
.prog-fill { background: linear-gradient(90deg, var(--blue), var(--cyan)); border-radius: 50px; height: 100%; transition: width .4s; }

/* ── MODAL ── */
.mo { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 2000; align-items: center; justify-content: center; }
.mo.open { display: flex; }
.mo-box { background: #fff; border-radius: var(--r); padding: 1.75rem; width: 92%; max-width: 520px; position: relative; max-height: 92vh; overflow-y: auto; box-shadow: var(--sh1); }
.mo-title { font-size: 1.1rem; font-weight: 700; color: var(--navy); margin-bottom: 1.25rem; padding-bottom: .75rem; border-bottom: 2px solid var(--lb); }
.mo-close { position: absolute; top: .9rem; right: .9rem; background: none; border: none; font-size: 1.4rem; cursor: pointer; color: var(--g400); line-height: 1; }
.mo-footer { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--g100); }

/* ── TOAST ── */
.toast-wrap { position: fixed; top: 1.2rem; right: 1.2rem; z-index: 9999; display: flex; flex-direction: column; gap: 6px; }
.toast { background: var(--navy); color: #fff; padding: .7rem 1.1rem; border-radius: var(--rs); font-size: .87rem; animation: tsIn .25s ease; min-width: 200px; box-shadow: var(--sh1); }
.toast.ok { background: var(--ok); }
.toast.err { background: var(--err); }
@keyframes tsIn { from { transform: translateX(80px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ── SCROLL TOP ── */
#scrollTop { position: fixed; bottom: 1.5rem; right: 1.5rem; width: 42px; height: 42px; background: var(--navy); color: #fff; border: none; border-radius: 50%; font-size: 1.1rem; cursor: pointer; opacity: 0; transition: all .3s; z-index: 300; display: flex; align-items: center; justify-content: center; }
#scrollTop.show { opacity: 1; }
#scrollTop:hover { background: var(--blue); transform: translateY(-2px); }

/* ── RESPONSIVE ── */
@media (max-width: 900px) {
  .notice-grid { grid-template-columns: 1fr; }
  .f-top { grid-template-columns: 1fr 1fr; }
  .my-grid { grid-template-columns: 1fr; }
  .hero-h { font-size: 1.9rem; }
  /* 🆕 모바일 히어로 배너 최적화 */
  .hero-in { flex-direction: column; padding: 2rem 1rem !important; }
  .hero-txt { width: 100%; padding-right: 0 !important; }
  .hero-p { font-size: .92rem; line-height: 1.65; }
  .hero-btns { gap: .5rem; }
  .hero-btns .btn-lg { padding: .6rem 1rem !important; font-size: .88rem !important; }
  .hero-stats { flex-wrap: wrap; gap: 1rem; margin-top: 1rem; }
  /* 🆕 섹션 내부 여백 축소 */
  .sec { padding: 2.5rem 0 !important; }
  .sec-in { padding: 0 1rem !important; }
  .sec-title { font-size: 1.6rem !important; }
  .sec-desc { font-size: .88rem !important; }
  /* 🆕 섹션 헤더(더보기/네비) 모바일 레이아웃 */
  .sec-hd-row { flex-direction: column !important; align-items: flex-start !important; gap: .75rem !important; }
  .nc-ctrl { align-self: flex-end; }
}
/* v68.67: 구 600px 블록 제거 — 신 768px 블록(아래)에 모두 흡수됨 */

/* ════════════════════════════════════
   3줄 헤더 스타일
════════════════════════════════════ */
.site-header { position:sticky;top:0;z-index:500;background:#fff;box-shadow:0 2px 12px rgba(0,0,0,.1); }

/* 1줄 - 연결사이트/SNS */


/* v68.67: 구 768px / 구 480px 모바일 블록 제거 — 신 블록(아래)에 모두 흡수됨
 * 흡수된 규칙:
 *   - html/body overflow / img,video,iframe / 일반 h1~h3 폰트
 *   - 한글 줄바꿈 word-break: keep-all
 *   - iOS 자동확대 방지 input font-size: 16px ★
 *   - 폼 필드 width 100% / 버튼 min-height 40px
 *   - 일반 table 가로 스크롤 / 섹션 상하 패딩
 *   - 그리드 1열 셀렉터(.notice-grid, .esg-about-grid 등) 추가
 *   - 탭 셀렉터(.esg-nav-tabs, .my-filter-tabs) 추가
 *   - 푸터 정렬 / 480px 푸터 정책 링크 줄바꿈
 */

/* 🆕 v68.39 — 일반 수강생에게 빵부스러기(breadcrumb) 비표시 */
.breadcrumb,
.esg-breadcrumb,
.esg-page-breadcrumb,
.cp-breadcrumb {
    display: none !important;
}


/* ═══════════════════════════════════════════════════════════════════
 * 🆕 v68.67 — 모바일 반응형 통합 (v68.66 미완성분 흡수)
 *
 * v68.66에서 "v68.62~v68.65 구버전 모바일 CSS 모두 삭제" 했다고 했지만
 * 실제로는 구버전 768px 블록(70줄)과 600px 블록(10줄)이 그대로 남아
 * 같은 미디어 쿼리가 두 번 정의되는 상태였음.
 *
 * v68.67: 구버전 블록 9개 그룹을 신버전 블록에 흡수 후 구버전 완전 제거.
 *   - 일반 h1~h3 폰트 크기 (구 768)
 *   - 한글 줄바꿈 word-break: keep-all (구 768)
 *   - iOS 자동확대 방지 input font-size: 16px (구 768) ★ 필수
 *   - 폼 필드 가로 100% (.fg, .esg-input, .esg-auth-input) (구 768)
 *   - 버튼 터치 영역 min-height: 40px (구 768)
 *   - 일반 table 가로 스크롤 (.tbl 별도) (구 768)
 *   - 섹션 상하 패딩 2.5rem (구 768)
 *   - 그리드 1열 셀렉터 추가 (.notice-grid 등) (구 768)
 *   - 탭 셀렉터 추가 (.esg-nav-tabs, .my-filter-tabs) (구 768)
 *   - 푸터 정렬 강화 (구 768)
 *   - 네비 버튼 크기 (구 600)
 *   - 푸터 정책 링크 줄바꿈 (구 480)
 *
 * 전략:
 *   1. PC 인라인 스타일을 무력화하기 위해 모든 규칙에 !important
 *   2. 768px 이하 = 휴대폰 모드, 단일 미디어 쿼리에 모두 통합
 *   3. 480px = 초소형 추가 보정만
 *
 * ※ 900px 블록(2190~)은 태블릿 영역이라 별도 유지
 * ※ 600px 블록(2007~)은 컴포넌트 지역 미디어쿼리라 유지
 * ═══════════════════════════════════════════════════════════════════ */

/* 🆕 v77.31 — PC용 슬라이드 배너 기본 height (CSS 변수로) */
.esg-sb {
  height: var(--esg-sb-h, 360px);
}

@media (max-width: 768px) {

  /* ── ① 가로 넘침 절대 방지 ─────────────── */
  /* 🆕 v77.12 — overflow-x: hidden은 자손의 position:sticky를 깨뜨리는 부작용이 있음.
                clip 으로 변경하여 sticky 정상 작동 + 가로 넘침 방지.
                (clip 미지원 구버전 브라우저에선 hidden으로 fallback) */
  html, body {
    overflow-x: hidden !important;        /* fallback for old browsers */
    overflow-x: clip !important;          /* modern browsers — sticky 안 깨짐 */
    width: 100% !important;
    max-width: 100vw !important;
    margin: 0 !important;
  }

  /* ── ② 폰트: 화면 너비에 비례 ───────────── */
  html { font-size: calc(11px + 0.6vw) !important; }
  body { font-size: 14px !important; line-height: 1.55 !important; }

  /* ── ③ 모든 콘텐츠가 화면 너비를 안 넘게 ── */
  *, *::before, *::after {
    max-width: 100% !important;
    box-sizing: border-box !important;
  }
  img, video, iframe {
    max-width: 100% !important;
    height: auto !important;
  }
  /* 🆕 v77.21 — iframe은 자체 비율이 없어 height:auto면 0이 됨 (지도/유튜브 등 깨짐).
                  명시적 height(인라인 또는 클래스 지정) 유지하도록 예외 처리. */
  iframe {
    height: revert !important;
  }
  /* 🆕 v77.26 — 슬라이드 배너 이미지도 height:auto 적용되면 컨테이너 가득 못 채워 위쪽만 보임.
                  슬라이드 안 img는 height:100% 유지하도록 예외 처리. */
  .esg-sb img,
  .esg-sb-slide img {
    height: 100% !important;
    object-fit: cover !important;
  }
  /* 🆕 v77.28 — 슬라이드 배너 컨테이너: 16:9 비율로 모바일 자동 크기 + fade 방식 적용
     🆕 v77.31 — height 인라인 제거 후 CSS 변수로 처리. 모바일은 aspect-ratio.
     🆕 v77.33 — 슬라이드 안에 모바일 전용 이미지가 있으면 컨테이너를 4:3으로 (모바일 이미지 권장 비율) */
  .esg-sb {
    aspect-ratio: 16/9 !important;
    height: auto !important;
    max-height: 320px !important;
  }
  /* 슬라이드 중 하나라도 모바일 이미지가 있으면 컨테이너를 4:3 비율로 */
  .esg-sb:has(.esg-sb-img-mobile) {
    aspect-ratio: 4/3 !important;
    max-height: 400px !important;
  }
  /* 🆕 v77.33 — 모바일 이미지가 있는 슬라이드는 PC 이미지 숨기고 모바일 이미지 표시 */
  .esg-sb-img-pc.has-mobile-alt {
    display: none !important;
  }
  .esg-sb-img-mobile {
    display: block !important;
  }
  /* 🆕 v77.28 — 슬라이드 배너 화살표 작게 (44 → 32px) */
  .esg-sb-prev,
  .esg-sb-next {
    width: 32px !important;
    height: 32px !important;
    font-size: 1.1rem !important;
    box-shadow: 0 2px 8px rgba(0,0,0,.15) !important;
    left: .5rem !important;
  }
  .esg-sb-next { left: auto !important; right: .5rem !important; }
  /* 🆕 v77.28 — 슬라이드 배너 도트 (작은 동그라미) */
  .esg-sb-dots {
    bottom: .6rem !important;
    gap: .4rem !important;
  }
  .esg-sb-dot {
    width: 7px !important;
    height: 7px !important;
    border-radius: 50% !important;
    padding: 0 !important;
    border: none !important;
    background: rgba(255,255,255,.4) !important;
    cursor: pointer;
    transition: background .2s, transform .2s;
  }
  .esg-sb-dot.on {
    width: 9px !important;
    height: 9px !important;
    background: rgba(255,255,255,.95) !important;
  }
  /* 🆕 v77.26 — 4단계 프로세스 모바일 2x2 그리드 */
  .esg-process-4step {
    grid-template-columns: 1fr 1fr !important;
    gap: 1.25rem .75rem !important;
  }
  .esg-process-4step-card {
    padding: 1.5rem .85rem !important;
  }
  .esg-process-4step-card h3 {
    font-size: .95rem !important;
  }
  .esg-process-4step-card p {
    font-size: .8rem !important;
  }
  /* 한글 단어 줄바꿈 (긴 영문/URL 처리) */
  body, p, div, span, a, li, td, th, h1, h2, h3, h4 {
    word-break: keep-all !important;
    overflow-wrap: break-word !important;
  }
  /* 일반 h1~h3 (.sec-title/.sp-title 외 본문) */
  h1 { font-size: 1.7rem !important; line-height: 1.3 !important; }
  h2 { font-size: 1.4rem !important; line-height: 1.35 !important; }
  h3 { font-size: 1.15rem !important; line-height: 1.4 !important; }

  /* ── ④ 헤더 컴팩트 ──────────────────────── */
  .h-top, .h-mid, .h-bot { padding-left: .75rem !important; padding-right: .75rem !important; }
  .h-mid-in { height: 56px !important; }
  .h-bot-in { height: 40px !important; justify-content: flex-end !important; }
  .h-nav { display: none !important; }
  .logo-name { font-size: .95rem !important; }
  .h-icon-btn { padding: .35rem .65rem !important; font-size: .78rem !important; }
  /* 🆕 v68.68 — 로고 이미지 모바일 크기 (PPT 슬라이드 1: 너무 큼/잘림)
   *   인라인 style="height:40px;max-width:200px"를 무력화.
   *   Designer 토큰: max-height 32px / max-width 140px */
  .site-logo img,
  .site-logo img[style] {
    max-height: 32px !important;
    max-width: 140px !important;
    height: auto !important;
    object-fit: contain !important;
  }

  /* ── ⑤ 본문 좌우 패딩 통일 ─────────────── */
  .sec, section.sec, .esg-home-section,
  .sp-body, .sp-hd, .sec-in, main, .container {
    padding-left: 1rem !important;
    padding-right: 1rem !important;
  }
  .sec-in { padding-left: 0 !important; padding-right: 0 !important; }
  /* 섹션 상하 패딩 축소 */
  section.sec, .esg-home-section {
    padding-top: 2.5rem !important;
    padding-bottom: 2.5rem !important;
  }
  /* iOS 폼 자동 확대 방지 (필수: 16px 이상) */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="tel"], input[type="number"], input[type="search"],
  input[type="url"], input[type="date"],
  textarea, select { font-size: 16px !important; }
  /* 폼 필드 가로 100% */
  .fg input, .fg select, .fg textarea,
  .esg-input, input.esg-auth-input {
    width: 100% !important;
    box-sizing: border-box !important;
  }
  /* 버튼 터치 영역 확보 (페이지네이션 dot, hero 컨트롤 등 작은 버튼은 예외) */
  button, .btn, a.btn, .esg-btn, .esg-btn-ghost {
    min-height: 40px !important;
  }
  /* 🆕 v68.71 — 작은 컨트롤 버튼은 min-height 예외 (dot이 길쭉이로 나오던 원인)
   *   .hero-dot/.hero-ctrl-btn은 자체 width/height로 사이즈 결정,
   *   일반 button 규칙이 height 40 강제하면 dot이 8×40 타원이 됨
   * 🆕 v77.30 — 슬라이드 배너의 화살표/도트도 예외 (찌그러짐 픽스)
   * 🆕 v77.35 — fm-search-go(전체메뉴 검색 화살표), my-wish-heart(찜 하트) 예외
   * 🆕 v77.50 — esg-popup-close-x(팝업 닫기 X) 예외 추가
   *             (32×32 정사각형이어야 border-radius:50%로 동그라미.
   *              위 button 글로벌 min-height:40이 32×40으로 늘려 타원이 됨) */
  .hero-dot, .hero-ctrl-btn,
  .pop-nav, .pop-tab, .nc-nav,
  .cc-heart,
  .esg-sb-prev, .esg-sb-next, .esg-sb-dot,
  .fm-search-go, .my-wish-heart,
  .esg-popup-close-x {
    min-height: auto !important;
  }
  /* 일반 table 가로 스크롤 (.tbl 카드형 별도 처리) */
  table:not(.tbl) {
    display: block !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }

  /* ── ⑥ 모든 그리드 → 1열 강제 ──────────── */
  .cg, .cg-h, .nc-grid,
  .esg-courses-grid, .esg-inst-grid,
  .esg-about-grid, .esg-community-grid,
  .notice-grid,
  .my-grid {
    display: grid !important;
    grid-template-columns: 1fr !important;
    gap: 1rem !important;
  }
  /* 인라인 grid도 1열로 */
  div[style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }
  /* 통계 카드/2열은 유지 */
  .my-stats {
    grid-template-columns: 1fr 1fr !important;
    gap: .65rem !important;
  }

  /* ── ⑦ 가로카드(.cch) → 세로카드 ────────── */
  .cch {
    display: block !important;
    height: auto !important;
    padding: .75rem !important;
  }
  .cch-thumb {
    width: 100% !important;
    height: auto !important;
    aspect-ratio: 16 / 9 !important;
    margin-bottom: .75rem !important;
  }
  .cch-body {
    padding: 0 !important;
    width: 100% !important;
  }

  /* ── ⑧ 세로카드 1열 ─────────────────────── */
  .cc-link img { height: 200px !important; aspect-ratio: 16/10 !important; }
  .cc-name { font-size: 1.05rem !important; min-height: auto !important; }
  .cc-summary { font-size: .82rem !important; min-height: auto !important; }
  /* 🆕 v68.68 — 신규과정 카드 세로 너무 김/여백 과다 (PPT 슬라이드 3-①,②) */
  /* PC에서는 카드 정렬용 placeholder를 유지하지만, 모바일은 1열이라 불필요 */
  .cc-summary-empty,
  .cc-cat-empty,
  .cc-meta-row-empty,
  .cc .cc-badges-empty {
    display: none !important;
  }
  /* 🆕 v68.72 — data-mobile-hide 속성으로 더 강한 셀렉터 보강 (specificity 0,2,0)
   *   PHP 카드 함수에서 빈 placeholder에 data-mobile-hide="1" 부여
   *   class만 박힌 케이스(이전 버전 캐시 등)도 잡고, 새 케이스도 잡음 */
  [data-mobile-hide="1"] {
    display: none !important;
    height: 0 !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    visibility: hidden !important;
  }
  /* 🆕 v68.69 — v68.68이 안 먹힌 이유: PC 카드 정렬 메커니즘이 더 강함
   *   .cg grid-auto-rows: 1fr / .cc-link flex:1 / .cc-link .cc-body flex:1 / .cc-spacer flex:1
   *   이 4개 규칙이 카드를 강제로 동일 높이로 만들어 빈 공간 발생
   *   모바일에서는 1열이라 불필요 — 모두 풀어줘서 콘텐츠 높이로 자연 조정 */
  .cg { grid-auto-rows: auto !important; }
  .cc-link { flex: 0 1 auto !important; }
  .cc-link .cc-body { flex: 0 1 auto !important; }
  .cc-spacer { display: none !important; }
  /* 카드 내부 패딩 축소 */
  .cc-body { padding: .3rem .75rem 0 !important; }
  .cc-foot { padding-top: .5rem !important; margin-top: .1rem !important; }
  /* 🆕 v68.71 (PPT4 슬라이드 3-②) — 카드 하단 여유공간 / 액션 영역 패딩 축소
   *   사용자 보고: 수강신청 버튼 아래에 빈 영역 ("여유공간 없음" = 그 빈 영역이 거슬림)
   *   해결: cc-body-action의 패딩을 0.9 → 0.5rem로 (절반) */
  .cc .cc-body-action { padding: 0 .75rem .5rem !important; }
  .cc-meta { margin: 0 0 .4rem !important; padding: .4rem 0 0 !important; }
  /* 🆕 v68.68 — 카드 태그 가로 스크롤 (PPT 슬라이드 3-③: 4개 태그 한쪽 쏠림)
   *   Designer 토큰: nowrap + overflow-x auto + 스크롤바 숨김
   *   .cc-tags(상단 태그)와 .cc-badges(하단 뱃지) 둘 다 적용 */
  .cc-tags,
  .cc .cc-badges {
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    scrollbar-width: none !important;            /* Firefox */
    -ms-overflow-style: none !important;         /* IE/Edge */
  }
  .cc-tags::-webkit-scrollbar,
  .cc .cc-badges::-webkit-scrollbar { display: none !important; }  /* Chrome/Safari */
  .cc-tag,
  .cc-tag-sep,
  .cc .cc-badge {
    flex-shrink: 0 !important;                   /* 카드 폭에 눌려 줄지 않게 */
  }

  /* ── ⑨ 인라인 width 무력화 ────────────── */
  [style*="width:240px"],
  [style*="width: 240px"],
  [style*="width:300px"],
  [style*="width: 300px"],
  [style*="min-width:240px"],
  [style*="min-width: 240px"] {
    width: 100% !important;
    min-width: 0 !important;
  }

  /* ── ⑩ 마이페이지 사이드 → 상단 ────────── */
  .my-side {
    position: static !important;
    padding: 1rem !important;
    margin-bottom: 1rem !important;
  }
  .my-main {
    padding: 1rem !important;
    border-radius: 10px !important;
  }
  .my-stat { padding: .85rem .75rem !important; }
  .my-stat-num { font-size: 1.4rem !important; }

  /* ── ⑪ 배너 슬라이드 ──────────────────── */
  /* 🆕 v68.70 (PPT3 슬라이드 1-①) — 빈 영역 제거 + 이미지 가득
   *   사용자 의도: "안쪽 네모만큼만 보여도 OK" = 빈 영역 없이 이미지가 가로폭 가득
   *   v68.69의 contain은 빈 영역(letter-box)이 너무 컸음
   *   해결: background-size: 100% 100% — 컨테이너 가득 채움 (약간 변형되지만 빈 영역 없음)
   *   aspect-ratio 16/8 = 가로 긴 PC 분위기 유지하면서 텍스트 영역도 확보 */
  .hero-slider {
    width: 100% !important;
    min-height: 0 !important;
    overflow: hidden !important;
    position: relative !important;
    background: #0F2040 !important;
  }
  .hero-slide {
    width: 100% !important;
    min-height: 0 !important;
    /* 🆕 v77.23 — 16/8(가로 너무 김) → 16/11로 변경하여 모바일에서 이미지 더 크게 보이도록 */
    aspect-ratio: 16/11 !important;
    overflow: hidden !important;
    position: relative !important;
  }
  .hero-bgimg,
  .hero-slide .hero-bgimg[style] {
    background-size: 100% 100% !important; /* contain → 100% 100%: 빈 영역 제거 */
    background-position: center center !important;
    background-repeat: no-repeat !important;
    width: 100% !important;
    height: 100% !important;
    position: absolute !important;
    inset: 0 !important;
  }
  .hero-in {
    padding: 1rem !important;
    width: 100% !important;
    position: relative !important;
    z-index: 1 !important;
  }
  .hero-h { font-size: 1.5rem !important; line-height: 1.3 !important; }
  .hero-p { font-size: .9rem !important; }
  .hero-btns { flex-wrap: wrap !important; gap: .5rem !important; }
  .hero-btns a {
    font-size: .85rem !important;
    padding: .55rem 1rem !important;
  }
  .hero-stats { gap: 1rem !important; font-size: .8rem !important; }
  .hs-num { font-size: 1.25rem !important; }

  /* ── ⑫ 푸터 정렬 ───────────────────────── */
  .site-footer {
    padding-left: 1.25rem !important;
    padding-right: 1.25rem !important;
  }
  .site-footer > div {
    width: 100% !important;
    max-width: 100% !important;
  }
  .f-grid, .f-cols, .f-col,
  .site-footer .f-top {
    grid-template-columns: 1fr !important;
    flex-direction: column !important;
    gap: 1.25rem !important;
  }
  /* 🆕 v68.68: .f-nav-cols 강제 2열 제거 — footer.php 자체 미디어쿼리가
   *   가로 3열(768px 이상)/3열(480~768px)/2열(480px 이하)을 적절히 처리
   *   사용자 요청(PPT 슬라이드 5): 모바일에서 가로 3열 배열 */
  .site-footer .f-info { font-size: .78rem !important; line-height: 1.7 !important; }
  .site-footer .f-logo img { max-height: 40px !important; }
  .site-footer .f-bot { flex-direction: column !important; gap: .5rem !important; text-align: center !important; }

  /* ── ⑬ 게시판 → 카드형 ────────────────── */
  /* 🆕 v68.78 (PPT9 S7-10) — 커뮤니티 게시판 카드 콤팩트 모바일 */
  .tbl, table.tbl {
    display: block !important;
    width: 100% !important;
  }
  .tbl thead { display: none !important; }
  .tbl tbody, .tbl tr {
    display: block !important;
    width: 100% !important;
  }
  .tbl tr {
    border: none !important;
    border-bottom: 1px solid #f1f5f9 !important;
    border-radius: 0 !important;
    margin: 0 !important;
    /* 🆕 v77.38 — 좌우 .25rem → 1rem (커뮤니티 게시글 번호가 좌측 가장자리에 너무 붙는 문제 픽스) */
    padding: .85rem 1rem !important;
    background: #fff !important;
    /* 한 row 안에서 td들이 가로로 배치되도록 */
    display: flex !important;
    flex-wrap: wrap !important;
    gap: .35rem .75rem !important;
    align-items: center !important;
  }
  .tbl td {
    padding: 0 !important;
    border: none !important;
    text-align: left !important;
    font-size: .88rem !important;
    width: auto !important;  /* block 100% 해제 */
    display: inline-block !important;
  }
  /* 첫 td (제목) — 가로 전체 (1행) */
  .tbl tr > td:first-child {
    width: 100% !important;
    font-weight: 700 !important;
    color: #0F2040 !important;
    font-size: .95rem !important;
    margin-bottom: .15rem !important;
    line-height: 1.4 !important;
  }
  /* 그 외 td (날짜, 작성자, 상태 등) — 작게 회색 */
  .tbl tr > td:not(:first-child) {
    color: #94a3b8 !important;
    font-size: .8rem !important;
  }
  /* 페이지네이션 셀 (colspan)은 가운데 정렬 + 가로 전체 */
  .tbl tr > td[colspan] {
    width: 100% !important;
    text-align: center !important;
    padding: 1rem 0 !important;
    color: #475569 !important;
    font-size: .85rem !important;
  }
  /* 빈 게시판 안내 셀도 적절히 */
  .tbl tr.tbl-empty td,
  .tbl tr td.empty,
  .tbl tr td:only-child {
    width: 100% !important;
    text-align: center !important;
    padding: 2rem 1rem !important;
    color: #64748b !important;
    font-size: .9rem !important;
  }
  /* 자료실 비밀번호 버튼 등 td 안의 버튼은 정상 크기 */
  .tbl td .btn,
  .tbl td button {
    font-size: .8rem !important;
    padding: .4rem .85rem !important;
  }

  /* 🆕 v77.41 — 커뮤니티 게시판 모바일 1줄 레이아웃 (override v68.78~v77.38)
     【배경】 기존 모바일 카드형은 제목을 한 줄 위로, 메타(작성자/날짜)를 두 번째 줄로
              내려서 2줄 카드 형태였음. 사용자가 "1줄 표시 + 좌·우 정렬 + ellipsis"를
              요청 → 행 전체를 한 줄 flex row로 강제, 마지막 td만 우측 정렬.
     【대상】 .sp-body .tbl (커뮤니티 게시판 4개 탭 공용). PC(>768px)는 영향 없음.
     【탭별 컬럼】
        - 공지사항    : 번호 / 제목 / 작성자 / 등록일
        - FAQ        : 번호 / 질문 / 등록일
        - 문의게시판 : 번호 / 제목 / 작성자 / 상태 / 등록일
        - 자료실     : 번호 / 제목 / 파일(다운로드 버튼) / 등록일
                       └ 모바일에서는 "번호 / 제목 / 다운로드"만 노출.
                         등록일 td는 .is-pds-table 한정 :nth-child(4) 숨김.
     【핵심 트릭】
        1. tr { flex-wrap: nowrap } → 줄바꿈 강제 차단
        2. td { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0 }
        3. 제목 td만 flex:1 1 0 (남는 공간 차지, ellipsis 작동 조건)
        4. 마지막 td는 margin-left:auto → 우측 끝 정렬
        5. colspan을 가진 tr(페이지네이션·빈 안내)은 영향 받지 않도록 :has(td[colspan]) 예외 */
  .sp-body .tbl tr:not(:has(td[colspan])) {
    flex-wrap: nowrap !important;
    gap: .6rem !important;
    padding: .65rem 1rem !important;
    align-items: center !important;
  }
  .sp-body .tbl tr:not(:has(td[colspan])) > td {
    white-space: nowrap !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    min-width: 0 !important;
    width: auto !important;
    margin-bottom: 0 !important;
    line-height: 1.4 !important;
  }
  /* 번호 (첫 td) — 좁은 고정 폭, 왼쪽, 메타색 */
  .sp-body .tbl tr:not(:has(td[colspan])) > td:first-child {
    flex: 0 0 auto !important;
    min-width: 22px !important;
    font-size: .78rem !important;
    font-weight: 500 !important;
    color: #94a3b8 !important;
    text-align: left !important;
  }
  /* 제목 (두 번째 td) — 남는 공간 모두, ellipsis 작동 */
  .sp-body .tbl tr:not(:has(td[colspan])) > td:nth-child(2) {
    flex: 1 1 0 !important;
    font-size: .88rem !important;
    font-weight: 700 !important;
    color: #0F2040 !important;
  }
  .sp-body .tbl tr:not(:has(td[colspan])) > td:nth-child(2) a {
    color: #0F2040 !important;
    font-weight: 700 !important;
  }
  /* 제목 td 내부 보조 정보(파일명, 강사/과정 설명 등)는 1줄 모드에서 숨김
     - 자료실의 📎파일명, 강사/과정 정보 div */
  .sp-body .tbl tr:not(:has(td[colspan])) > td:nth-child(2) > div {
    display: none !important;
  }
  /* 가운데 메타 td (작성자/상태/파일 셀) — 좁게, 회색, ellipsis */
  .sp-body .tbl tr:not(:has(td[colspan])) > td:nth-child(n+3):not(:last-child) {
    flex: 0 0 auto !important;
    max-width: 80px !important;
    font-size: .74rem !important;
    color: #94a3b8 !important;
  }
  /* 마지막 td (등록일 또는 자료실의 다운로드) — 우측 끝 */
  .sp-body .tbl tr:not(:has(td[colspan])) > td:last-child {
    flex: 0 0 auto !important;
    margin-left: auto !important;
    font-size: .74rem !important;
    color: #94a3b8 !important;
    text-align: right !important;
  }
  /* 자료실 한정 — 등록일(4번째 td) 숨김, 마지막은 파일 td(다운로드 버튼)가 됨 */
  .sp-body .is-pds-table .tbl tr:not(:has(td[colspan])) > td:nth-child(4) {
    display: none !important;
  }
  /* 자료실 다운로드 버튼 — 1줄 높이에 맞게 콤팩트 */
  .sp-body .is-pds-table .tbl tr td:nth-child(3) .btn,
  .sp-body .is-pds-table .tbl tr td:nth-child(3) a.btn,
  .sp-body .is-pds-table .tbl tr td:nth-child(3) button {
    font-size: .7rem !important;
    padding: .25rem .55rem !important;
    line-height: 1.3 !important;
    gap: .15rem !important;
  }
  /* 페이지네이션 / 빈 안내(colspan tr)는 1줄 규칙 적용 안 되므로 기존 흐름 그대로 */

  /* ── ⑭ 모달 ────────────────────────────── */
  .mo-box, [class*="-modal"] > div {
    width: calc(100% - 1.5rem) !important;
    max-width: calc(100% - 1.5rem) !important;
    margin: 1rem auto !important;
    padding: 1.25rem !important;
  }

  /* ── ⑮ 인기과정/필터 탭 가로 스크롤 ──────── */
  .pop-tabs, .esg-tabs, .esg-nav-tabs, .my-filter-tabs {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    flex-wrap: nowrap !important;
    white-space: nowrap !important;
    padding-bottom: .4rem !important;
  }
  .pop-tabs > *, .esg-tabs > *,
  .esg-nav-tabs > *, .my-filter-tabs > * { flex-shrink: 0 !important; }
  .esg-tabs::-webkit-scrollbar,
  .esg-nav-tabs::-webkit-scrollbar,
  .my-filter-tabs::-webkit-scrollbar { height: 3px !important; }
  /* 네비 버튼 터치 크기 */
  .nc-nav { width: 40px !important; height: 40px !important; font-size: 1.2rem !important; }

  /* ── ⑯ 섹션 타이틀 ──────────────────────── */
  .sp-title { font-size: 1.4rem !important; }
  .sec-title { font-size: 1.4rem !important; }
  .sec-desc { font-size: .85rem !important; }

  /* 🆕 v77.47 — 수강신청 페이지 필터 바 글씨 축소 (모바일만, 사용자 요청)
     【배경】
       page-courses.php의 필터 select 3개(fc-level/fc-price/fc-textbook) + 검색 input(fc-q)이
       인라인 스타일로 font-size:.85rem 박혀 있어서 모바일에선 큰 편.
       사용자 요청으로 .72rem 으로 축소.

     【주의】
       인라인 스타일을 override하려면 specificity와 무관하게 !important 필수.
       ID 셀렉터로 정확히 잡고 !important 부여. */
  #fc-level, #fc-price, #fc-textbook {
    font-size: .72rem !important;
    padding: .32rem .55rem !important;
  }
  #fc-q {
    font-size: .72rem !important;
  }
  /* 카운트 표시 ("14개 과정") */
  #fc-count {
    font-size: .72rem !important;
  }
}

/* ── 작은 모바일 (480px 이하) ── */
@media (max-width: 480px) {
  html { font-size: calc(10.5px + 0.6vw) !important; }
  .sp-title { font-size: 1.25rem !important; }
  .sec-title { font-size: 1.25rem !important; }
  .hero-h { font-size: 1.3rem !important; }
  .h-mid-in { height: 50px !important; }
  /* 더 좁은 본문 패딩 */
  .sec-in, .container, .page-wrap {
    padding-left: 12px !important;
    padding-right: 12px !important;
  }
  /* 초소형 일반 헤딩 추가 축소 */
  h1 { font-size: 1.45rem !important; }
  h2 { font-size: 1.25rem !important; }
  h3 { font-size: 1.05rem !important; }
  /* 🆕 v68.68: .f-nav-cols 강제 1열 제거 — footer.php가 480px에서 2열 처리 */
  .site-footer .f-policy-links { font-size: .76rem !important; flex-wrap: wrap !important; }
}

/* ════════════════════════════════════════════════════════════════════
 * 🎯 v68.78 — S2 인기과정 가로카드 (.cch) 별점/후기 정렬 (주황선)
 *           S2 보라색 영역 (cch 안의 빈 공간) 제거
 * ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* cch-rating 정렬 통일 */
  .cch .cch-rating{
    display: inline-flex !important;
    align-items: center !important;
    gap: .25rem !important;
    line-height: 1 !important;
  }
  .cch .cch-rating-num,
  .cch .cch-rating-count{
    line-height: 1 !important;
    vertical-align: middle !important;
  }

  /* cch 안의 빈 공간 제거 — height auto로 자기 콘텐츠만큼만 */
  .cch{
    height: auto !important;
    min-height: 0 !important;
    align-self: flex-start !important;
  }
  .cch [data-mobile-hide="1"],
  .cch .cch-summary-empty,
  .cch .cch-tag-empty{
    display: none !important;
    height: 0 !important; max-height: 0 !important;
    margin: 0 !important; padding: 0 !important;
  }
}

/* ════════════════════════════════════════════════════════════════════
 * 🆕 v68.78 (PPT9 S12) — 인기과정 "더보기" 중복 제거 + 버튼 우상단
 * ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* 모바일 전용 "더보기" 버튼 숨김 (S12: 같은 역할 더보기 지우기) */
  .pop-mobile-ctrl .pop-more-btn{
    display: none !important;
  }
  /* PC용 "전체 보기 →" (.nc-more)는 그대로 유지하되 모바일에서 우측 정렬 */
  .sec-hd.sec-hd-row{
    flex-direction: row !important;  /* 다시 row로 (column 무력화) */
    align-items: flex-start !important;
    flex-wrap: wrap !important;
    gap: .5rem !important;
  }
  .sec-hd.sec-hd-row > div:first-child{
    flex: 1 1 auto !important;
    min-width: 0;
  }
  .sec-hd.sec-hd-row .nc-more{
    flex: 0 0 auto !important;
    align-self: flex-start !important;
    margin-top: .35rem !important;
    font-size: .82rem !important;
    padding: .4rem .85rem !important;
    border: 1.5px solid #e2e8f0;
    border-radius: 50px;
    background: #fff;
    color: #0F2040;
    text-decoration: none;
    font-weight: 600;
    white-space: nowrap;
  }
  /* pop-mobile-ctrl이 비어있으면(< > 만 남으면) 우측 정렬 */
  .pop-mobile-ctrl{
    justify-content: flex-end !important;
  }
}

/* ════════════════════════════════════════════════════════════════════
 * 🆕 v68.78 — 추가 모바일 처리 (S3, S4, S5)
 * ──────────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  /* S3 (오시는길) — 주소/연락처 grid 1열 */
  .esg-loc-info-grid{
    grid-template-columns: 1fr !important;
    gap: 1rem !important;
  }

  /* S4 (개인정보처리방침/이용약관 모달) — 시행일 select 글씨 축소 */
  .esg-privacy-date-row select,
  #esg-priv-version-select,
  #esg-terms-version-select{
    font-size: .82rem !important;
    padding: .5rem .75rem !important;
    height: auto !important;
    max-width: 100%;
    min-width: 0;
  }
  .esg-privacy-date-row{
    padding: .65rem 1rem !important;
  }
  /* 모달 헤더 제목도 약간 축소 */
  .esg-privacy-title{
    font-size: 1.05rem !important;
    line-height: 1.4 !important;
  }

  /* S5 (푸터) — 이메일 한줄 내림 */
  .f-email-br{
    display: inline !important;  /* 모바일에서 br 적용 */
  }
  /* PC에서는 이메일 br 안 보이게 */
}
@media (min-width: 769px){
  .f-email-br{ display: none !important; }
}


/* 🆕 v75.17 → v75.18 — PC에서도 cc-bottom-row를 flex 한 줄로 (별점 좌 + 가격 우)
   v75.17에서 적용했지만 다른 .cc .cc-rating { margin-bottom: .35rem }, .cc .cc-price-big { margin-bottom: .15rem }
   가 cc-rating 아래에 공간을 만들어 시각적으로 분리되어 보임.
   v75.18: 셀렉터 강도 강화(.cc .cc-bottom-row) + !important 로 확실히 우선 적용 + 자식들의 margin 0 강제 */
.cc .cc-bottom-row {
  display: flex !important;
  flex-direction: row !important;
  justify-content: space-between !important;
  align-items: center !important;
  gap: .6rem !important;
  width: 100% !important;
  margin-top: .25rem !important;
}
.cc .cc-bottom-row .cc-rating {
  margin: 0 !important;
  flex: 0 0 auto !important;
}
.cc .cc-bottom-row .cc-price-big {
  margin: 0 !important;
  flex: 0 0 auto !important;
  white-space: nowrap !important;
  text-align: right !important;
}

/* ════════════════════════════════════════════════════════════════════
 * 🎯 v68.90 (2026-05-07) — 신규과정 모바일 카드 — 깨끗한 재작성
 * ────────────────────────────────────────────────────────────────────
 * 사용자 요구 (확정):
 *   1) 카드 크기 완전 고정 — 모든 카드 같은 폭, 같은 높이
 *   2) 모든 요소 자리 고정 — 강사·신청기간 등 위치 고정
 *   3) 데이터 없는 행은 빈 자리(placeholder)로 — 행이 사라지는 게 아님
 *   4) 별점·후기 좌측 + 가격 우측 (양 끝 분리, space-between)
 *   5) 콘텐츠 길이가 카드 크기에 영향 X (제목 ellipsis로 잘림)
 *
 * 전략:
 *   - 카드 폭: flex 0 0 82% + width 82% + min-width 82% + max-width 82%
 *     (콘텐츠가 길어도 폭 고정)
 *   - 카드 높이: align-items stretch + 모든 카드의 콘텐츠 행 수 동일
 *     (placeholder가 자리 유지 → 같은 높이 자동 통일)
 *   - placeholder: visibility:hidden + height/min-height 유지 (display:none 아님)
 *     기존 모바일 [data-mobile-hide=1] 글로벌 display:none 규칙을 specificity로 무력화
 *   - cc-bottom-row: display:flex + justify-content:space-between (좌우 양 끝)
 *
 * PC는 절대 안 건드림 — 모든 규칙이 @media (max-width: 768px) 안에서만.
 * ════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* ─────────────────────────────────────────────
   * 1) 슬라이더 트랙 / 슬라이드 — 카드 폭/높이 고정
   * ───────────────────────────────────────────── */
  .nc-track{
    align-items: stretch !important;
    gap: .75rem !important;
  }
  .nc-track .nc-slide{
    flex: 0 0 82% !important;
    min-width: 82% !important;
    max-width: 82% !important;
    width: 82% !important;
    align-self: stretch !important;
    height: auto !important;
    min-height: 0 !important;
    overflow: hidden !important;
  }

  /* ─────────────────────────────────────────────
   * 2) 카드 (.cc) — stretch로 모든 카드 같은 높이
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc{
    height: 100% !important;
    min-height: 0 !important;
    min-width: 0 !important;
    width: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    border-radius: 14px !important;
    overflow: hidden !important;
    background: #fff !important;
    border: 1px solid #e2e8f0 !important;
  }

  /* ─────────────────────────────────────────────
   * 3) cc-link — 가용공간 흡수 (PC default 복원)
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc-link{
    display: flex !important;
    flex-direction: column !important;
    flex: 1 1 auto !important;
    height: auto !important;
    min-width: 0 !important;
    padding: 0 !important;
    gap: 0 !important;
    grid-template-columns: none !important;
    grid-template-rows: none !important;
  }

  /* 썸네일 — 풀폭 16:9 */
  .nc-track .nc-slide .cc-thumb{
    width: 100% !important;
    max-width: 100% !important;
    height: auto !important;
    aspect-ratio: 16 / 9 !important;
    border-radius: 14px 14px 0 0 !important;
    overflow: hidden !important;
    padding: 0 !important;
    margin: 0 !important;
    flex: 0 0 auto !important;
    display: block !important;
  }
  .nc-track .nc-slide .cc-thumb img{
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
  }

  /* ─────────────────────────────────────────────
   * 4) cc-body (콘텐츠) — 가용공간 채움
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc-link .cc-body{
    flex: 1 1 auto !important;
    height: auto !important;
    min-width: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    gap: .35rem !important;
    padding: 1rem 1rem .85rem !important;
    text-align: left !important;
  }

  /* 카테고리 */
  .nc-track .nc-slide .cc-cat{
    font-size: .72rem !important;
    color: #1B6FD8 !important;
    font-weight: 700 !important;
    line-height: 1.3 !important;
    margin: 0 !important;
    padding: 0 !important;
    background: none !important;
    min-height: .87rem !important;
  }

  /* 과정명 — 2줄 ellipsis (제목 길어도 카드 크기 영향 X) */
  .nc-track .nc-slide .cc-name{
    font-size: 1rem !important;
    font-weight: 800 !important;
    color: #0F2040 !important;
    line-height: 1.35 !important;
    margin: 0 !important;
    overflow: hidden !important;
    display: -webkit-box !important;
    -webkit-line-clamp: 2 !important;
    -webkit-box-orient: vertical !important;
    word-break: keep-all !important;
    overflow-wrap: anywhere !important;
    min-height: 2.7rem !important;        /* 2줄 자리 항상 확보 → 모든 카드 통일 */
  }

  /* 부제 — 2줄 ellipsis */
  .nc-track .nc-slide .cc-summary{
    display: -webkit-box !important;
    -webkit-line-clamp: 2 !important;
    -webkit-box-orient: vertical !important;
    overflow: hidden !important;
    font-size: .8rem !important;
    line-height: 1.45 !important;
    color: #64748b !important;
    margin: 0 0 .15rem !important;
    padding: 0 !important;
    height: auto !important;
    min-height: 2.32rem !important;       /* 2줄 자리 항상 확보 */
    max-height: none !important;
    word-break: keep-all !important;
  }

  /* 메타 영역 (강사/신청기간) */
  .nc-track .nc-slide .cc-meta{
    display: block !important;
    margin: .15rem 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-meta-row{
    display: grid !important;
    grid-template-columns: 60px 1fr !important;
    gap: .35rem !important;
    font-size: .82rem !important;
    line-height: 1.5 !important;
    margin: 0 !important;
    padding: 0 !important;
    min-height: 1.23rem !important;       /* 1행 자리 항상 확보 */
  }
  .nc-track .nc-slide .cc-meta-row dt{
    display: block !important;
    color: #94a3b8 !important;
    font-size: .82rem !important;
    font-weight: 500 !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-meta-row dd{
    color: #0F2040 !important;
    font-weight: 700 !important;
    margin: 0 !important;
    padding: 0 !important;
    word-break: keep-all !important;
  }
  .nc-track .nc-slide .cc-meta-row dd::before{
    content: "" !important;
  }

  /* 태그 영역 — 1줄 자리 확보 */
  .nc-track .nc-slide .cc-badges{
    display: flex !important;
    flex-wrap: nowrap !important;
    overflow: hidden !important;
    max-height: 1.6rem !important;
    min-height: 1.6rem !important;        /* 빈 태그도 자리 확보 */
    gap: .3rem !important;
    margin: .15rem 0 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-badge{
    flex: 0 0 auto !important;
    font-size: .68rem !important;
    padding: 2px 8px !important;
    line-height: 1.4 !important;
    min-height: 0 !important;
  }

  /* spacer — 가용공간 흡수 (PC 메커니즘 복원, attribute selector로 specificity 보강) */
  .nc-track .nc-slide .cc-spacer[data-mobile-hide="1"],
  .nc-track .nc-slide .cc-spacer{
    display: block !important;
    flex: 1 1 auto !important;
    height: auto !important;
    min-height: .25rem !important;
    max-height: none !important;
    margin: 0 !important;
    padding: 0 !important;
    visibility: hidden !important;
  }

  /* ─────────────────────────────────────────────
   * 5) cc-bottom-row (별점·후기 좌측 + 가격 우측)
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc-bottom-row{
    display: flex !important;
    flex-direction: row !important;
    justify-content: space-between !important;   /* 별점 좌측, 가격 우측 */
    align-items: center !important;
    gap: .6rem !important;
    width: 100% !important;
    margin: .25rem 0 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-bottom-row .cc-rating{
    margin: 0 !important;
    padding: 0 !important;
    font-size: .82rem !important;
    line-height: 1.3 !important;
    display: inline-flex !important;
    align-items: center !important;
    gap: .25rem !important;
    flex: 0 0 auto !important;
    position: static !important;
    left: auto !important; right: auto !important;
    top: auto !important; bottom: auto !important;
  }
  .nc-track .nc-slide .cc-bottom-row .cc-price-big{
    margin: 0 !important;
    padding: 0 !important;
    font-size: 1.05rem !important;
    font-weight: 800 !important;
    color: #0F2040 !important;
    line-height: 1.3 !important;
    white-space: nowrap !important;
    flex: 0 0 auto !important;
    position: static !important;
    left: auto !important; right: auto !important;
    top: auto !important; bottom: auto !important;
  }
  .nc-track .nc-slide .cc-rating-star,
  .nc-track .nc-slide .cc-rating-num,
  .nc-track .nc-slide .cc-rating-sep,
  .nc-track .nc-slide .cc-rating-count{
    line-height: 1 !important;
    vertical-align: middle !important;
  }

  /* ─────────────────────────────────────────────
   * 6) cc-body-action (찜 + 수강신청 버튼) — 카드 하단 고정
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc-body-action{
    flex: 0 0 auto !important;
    padding: .6rem .85rem .85rem !important;
    border-top: 1px solid #f1f5f9;
    background: #fafbfc;
    margin: 0 !important;
  }
  .nc-track .nc-slide .cc-action{
    display: flex !important;
    gap: .55rem !important;
    align-items: stretch !important;
  }
  .nc-track .nc-slide .cc-heart{
    flex: 0 0 auto !important;
    width: 42px !important;
    height: auto !important;
    background: #fff !important;
    border: 1px solid #e2e8f0 !important;
    border-radius: 8px !important;
    cursor: pointer !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  .nc-track .nc-slide .cc-btn-apply{
    flex: 1 1 auto !important;
    padding: .65rem !important;
    font-size: .9rem !important;
    font-weight: 700 !important;
    border-radius: 8px !important;
    background: #0F2040 !important;
    color: #fff !important;
    border: none !important;
    cursor: pointer !important;
  }

  /* ─────────────────────────────────────────────
   * 7) placeholder (빈 자리) — visibility:hidden, 자리 유지
   *    핵심 결정: display:none 안 함, 자리 차지하면서 안 보이게
   *    글로벌 [data-mobile-hide=1] { display:none } 규칙을 specificity로 무력화
   * ───────────────────────────────────────────── */
  .nc-track .nc-slide .cc-cat-empty[data-mobile-hide="1"],
  .nc-track .nc-slide .cc-cat-empty{
    display: block !important;
    visibility: hidden !important;
    min-height: .87rem !important;
    height: .87rem !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-summary-empty[data-mobile-hide="1"],
  .nc-track .nc-slide .cc-summary-empty{
    display: block !important;
    visibility: hidden !important;
    min-height: 2.32rem !important;
    height: 2.32rem !important;
    margin: 0 0 .15rem !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-meta-row-empty[data-mobile-hide="1"],
  .nc-track .nc-slide .cc-meta-row-empty{
    display: block !important;
    visibility: hidden !important;
    min-height: 1.23rem !important;
    height: 1.23rem !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .nc-track .nc-slide .cc-badges-empty[data-mobile-hide="1"],
  .nc-track .nc-slide .cc-badges-empty{
    display: block !important;
    visibility: hidden !important;
    min-height: 1.6rem !important;
    height: 1.6rem !important;
    margin: .15rem 0 0 !important;
    padding: 0 !important;
  }
}

/* ════════════════════════════════════════════════════════════════════
 * 🎯 v68.94 (2026-05-07) — WordPress emoji 자동 변환 이미지 크기 제어
 * ────────────────────────────────────────────────────────────────────
 * 문제: WordPress는 텍스트의 이모지(💬, ★ 등)를 자동으로 <img class="emoji">로
 *       변환. 이 이미지의 모바일 기본 크기가 비정상적으로 큼(약 200px).
 *       cc-rating 안에 들어가면 cc-rating 높이를 200px로 만들어 카드 내부에
 *       빈 공간 발생.
 * 해결: img.emoji에 1em 크기 + inline 강제. 텍스트 줄 높이와 동일.
 *       전역 적용 (모든 곳의 emoji img에 영향, 안전한 기본값).
 * ════════════════════════════════════════════════════════════════════ */
img.emoji,
.emoji {
  display: inline !important;
  width: 1em !important;
  height: 1em !important;
  max-width: 1em !important;
  max-height: 1em !important;
  margin: 0 .07em !important;
  vertical-align: -0.1em !important;
  background: none !important;
  padding: 0 !important;
  border: none !important;
  box-shadow: none !important;
  position: static !important;
}

/* ════════════════════════════════════════════════════════════════════
 * 🆕 v76.1 (PPT PC 슬라이드 2) — 비밀번호 토글 아이콘 위치 수정
 * 사유: 모든 비밀번호 입력 필드(가입/마이페이지/탈퇴 등)에서 👁 아이콘이
 *       input 아래 좌측 하단에 표시되는 버그.
 *       원인: main.js의 attachPwToggle()이 .esg-pw-wrap + .esg-pw-toggle을
 *            생성하지만 CSS 정의가 어디에도 없어 default 인라인 흐름으로 표시.
 * 해결: wrap을 relative + button을 absolute right로 input 내부 우측 끝 배치.
 * 적용 범위: 로그인 페이지 제외 전부 (로그인은 .esg-auth-pw-toggle 별도 정의)
 * ════════════════════════════════════════════════════════════════════ */
.esg-pw-wrap {
  position: relative;
  display: block;
  width: 100%;
}
.esg-pw-wrap input[type="password"],
.esg-pw-wrap input[type="text"] {
  /* 우측에 아이콘 들어갈 자리 확보 (44px) */
  padding-right: 44px !important;
  width: 100%;
  box-sizing: border-box;
}
.esg-pw-toggle {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  cursor: pointer;
  color: #94a3b8;
  padding: 6px;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: color .15s, background .15s;
  z-index: 2;
}
.esg-pw-toggle:hover {
  color: #1B6FD8;
  background: #f1f5f9;
}
.esg-pw-toggle:focus {
  outline: 2px solid #bfdbfe;
  outline-offset: 1px;
}
/* CAPS LOCK 안내는 wrap 아래에 표시 (input 외부 아래) */
.esg-pw-wrap .esg-pw-caps-hint {
  position: static;
  display: none;
}
.esg-pw-wrap .esg-pw-caps-hint.show {
  display: block;
}

/* ═══════════════════════════════════════════════════════════════
 * 🆕 v77.82 — 신청기간 외 카드 클릭 차단 + 토스트
 * ═══════════════════════════════════════════════════════════════ */

/* 카드 자체는 div(role=link)로 변경됨 — 기존 .cc 스타일 그대로 사용 */
.cc.course-card {
  cursor: pointer;
}
.cc.course-card:focus-visible {
  outline: 2px solid #1B6FD8;
  outline-offset: 2px;
}

/* 신청 불가(준비중/마감) 카드 — 미세한 시각 차이 */
.cc.course-card[data-can-enroll="0"] {
  cursor: not-allowed;
}
.cc.course-card[data-can-enroll="0"]:hover {
  /* 호버 효과는 유지하되 살짝 톤 다운 */
  opacity: .96;
}

/* ── 토스트 알림 ─────────────────────────────────────────── */
.esg-toast-container {
  position: fixed;
  top: 80px;
  right: 20px;
  z-index: 999999;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
  max-width: calc(100vw - 40px);
}

.esg-toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 280px;
  max-width: 360px;
  padding: 14px 16px;
  background: #fff;
  border-radius: 10px;
  border-left: 4px solid #1B6FD8;
  box-shadow: 0 8px 24px rgba(15, 32, 64, .12), 0 2px 6px rgba(15, 32, 64, .08);
  color: #0F2040;
  font-size: .92rem;
  line-height: 1.45;
  font-weight: 500;
  pointer-events: auto;
  transform: translateX(120%);
  opacity: 0;
  transition: transform .35s cubic-bezier(.22, 1, .36, 1), opacity .25s ease;
}
.esg-toast.is-show {
  transform: translateX(0);
  opacity: 1;
}
.esg-toast.is-hide {
  transform: translateX(120%);
  opacity: 0;
}

.esg-toast[data-status="before"] { border-left-color: #1B6FD8; }
.esg-toast[data-status="closed"] { border-left-color: #64748b; }
.esg-toast[data-status="info"]   { border-left-color: #00C2D1; }

.esg-toast-icon {
  font-size: 1.25rem;
  line-height: 1;
  flex-shrink: 0;
  margin-top: 1px;
}
.esg-toast-body {
  flex: 1;
  min-width: 0;
}
.esg-toast-title {
  font-weight: 700;
  font-size: .95rem;
  margin-bottom: 2px;
  color: #0F2040;
}
.esg-toast-msg {
  color: #475569;
  font-size: .86rem;
  font-weight: 500;
}
.esg-toast-close {
  background: transparent;
  border: 0;
  cursor: pointer;
  color: #94a3b8;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 2px;
  margin-left: 4px;
  flex-shrink: 0;
}
.esg-toast-close:hover { color: #0F2040; }

/* 모바일: 하단에서 슬라이드업 */
@media (max-width: 640px) {
  .esg-toast-container {
    top: auto;
    bottom: 20px;
    right: 12px;
    left: 12px;
    max-width: none;
    align-items: stretch;
  }
  .esg-toast {
    min-width: 0;
    max-width: none;
    width: 100%;
    transform: translateY(120%);
  }
  .esg-toast.is-show { transform: translateY(0); }
  .esg-toast.is-hide { transform: translateY(120%); }
}
