/* ==========================================================================
   웹(공개) 영역 스타일 — 헤더/푸터/홈/카탈로그/상세/인증  (시안: EDUPIC 메인·로그인)
   ========================================================================== */

/* 페이지 셸 — 본문이 짧은 페이지에서도 푸터가 바닥에 붙게 한다(sticky footer) */
.web-shell { display: flex; flex-direction: column; min-height: 100vh; }

/* ── GNB ── */
.web-header {
  position: sticky; top: 0; z-index: 30;
  background: rgba(255, 255, 255, .92); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--ep-border);
}
.web-header-inner {
  max-width: var(--ep-container); margin: 0 auto; padding: 0 24px; height: 64px;
  display: flex; align-items: center; gap: var(--ep-space-6);
}
.web-logo { display: flex; align-items: center; gap: var(--ep-space-2); flex-shrink: 0; }
.web-logo-text { font-size: var(--ep-text-xl); font-weight: 800; color: var(--ep-navy); letter-spacing: .5px; }
.web-nav { display: flex; align-items: center; gap: 2px; font-size: var(--ep-text-md); }
.web-nav a {
  padding: 8px 13px; border-radius: 999px; color: var(--ep-text-sub);
  font-weight: 600; white-space: nowrap;
}
.web-nav a:hover, .web-nav a.is-active { background: var(--ep-border-soft); color: var(--ep-primary); }
.web-header-search {
  display: flex; align-items: center; gap: var(--ep-space-2);
  background: var(--ep-bg); border: 1px solid var(--ep-border); border-radius: 999px;
  padding: 8px 16px; margin-left: auto; flex: 0 1 260px; min-width: 140px;
}
.web-header-search input { flex: 1; border: 0; background: transparent; font: inherit; font-size: var(--ep-text-xs); outline: none; }
/* 인풋의 outline:none 이 전역 :focus-visible 링을 눌러버린다(특이도).
   대체가 없으면 키보드 사용자는 검색창에 왔는지 알 수 없다 — 필 전체를 밝힌다. */
.web-header-search:focus-within { border-color: var(--ep-primary); box-shadow: 0 0 0 3px rgba(40, 87, 224, .12); }
.web-header-search input::placeholder { color: var(--ep-text-faint); }
.web-header-actions { display: flex; align-items: center; gap: var(--ep-space-2); flex-shrink: 0; margin-left: 14px; }
/* 로그인한 사람 이름 — 「홍길동님」.
   이름 첫 글자 동그라미(아바타)는 한글에서는 성씨라 사람을 가리지 못했다.
   김씨끼리는 같은 「김」이 뜨고, 정작 «누구로 로그인했는지» 는 알 수 없었다. */
.web-username {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 var(--ep-space-2);
  font-size: var(--ep-text-xs); font-weight: 700; color: var(--ep-text-sub);
  max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.web-username:hover { color: var(--ep-primary); }

/* ── 컨테이너 ── */
.web-container { max-width: var(--ep-container); margin: 0 auto; padding: 0 24px; }
.web-section { padding-top: 44px; }
/* 마지막 섹션은 아래도 띄운다 — DESIGN_RULES §6(하단 56px). style="padding:44px 24px 56px" 로 흩어져 있던 것 */
.web-section.is-last { padding-bottom: var(--ep-space-14); }
.web-section-title { font-size: var(--ep-text-xl); font-weight: 800; margin: 0 0 16px; }

/* ── 히어로 ── */
.web-hero { background: linear-gradient(115deg, #0B1B3F 0%, #10265E 45%, #2857E0 110%); }
/* ⚠️ 2026-08-28 — 슬라이드는 **화면 폭을 꽉 채우는 껍데기**이고, 안쪽만 본문 폭에 맞춘다.
   전에는 배너 하나가 곧 .web-hero-inner(max-width:1160) 였고 바탕색도 거기 붙었다 —
   그래서 운영자가 색을 고르면 **가운데 1160px 만 그 색이고 좌우에는 기본 남색이 남았다**
   (실측: 초록 배너 좌우에 남색 띠). 껍데기와 안쪽을 나누는 얼개다. */
.web-hero-slide { position: relative; overflow: hidden; }
/* 캐러셀은 hidden 속성으로 슬라이드를 껐다 켠다 — common.css 맨 위의
   «[hidden] { display:none !important }» 가 자리마다 못박을 필요를 없앴다(2026-08-28). */
.web-hero-inner {
  position: relative; z-index: 2;
  max-width: 1160px; margin: 0 auto; padding: 52px 24px 56px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--ep-space-10);
}

/* ── 꽉 채우기(display_mode='cover') ──
   히어로 배경이 네이비로 고정이라 무슨 그림을 올려도 그 색이 깔렸다 — 밝은 그림·따뜻한 색
   그림은 어긋나고 고칠 수단이 없었다(2026-08-28 주인님 지적). 이 방식은 그림이 배너 전체를
   덮으므로 색 충돌이 원천적으로 사라지고, 글자 없이 **그림만으로도** 배너가 성립한다. */
.web-hero-slide.is-cover .web-hero-inner {
  min-height: 320px;
  /* 글은 그림 위에 얹힌다 — 좌우로 벌리지 않고 왼쪽에 모은다 */
  justify-content: flex-start;
}
.web-hero-cover {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  /* 안쪽 여백(52px 24px)을 무시하고 상자 끝까지 덮어야 «꽉 채움» 이다 */
  margin: 0; border-radius: 0; z-index: 0; display: block;
}
/* 글을 얹을 때만 까는 어두운 겹 — 밝은 사진 위의 흰 글씨는 안 읽힌다.
   왼쪽이 짙고 오른쪽으로 갈수록 옅어져, 글이 있는 쪽만 눌러 주고 그림은 최대한 살린다. */
.web-hero-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(90deg, rgba(8, 14, 32, .78) 0%, rgba(8, 14, 32, .55) 45%, rgba(8, 14, 32, .12) 100%);
}
.web-hero-slide.is-cover .web-hero-copy { max-width: 640px; }
@media (max-width: 760px) {
  /* 좁은 화면에서는 가로 그라데이션이 글 아래까지 안 닿는다 — 세로로 바꾼다 */
  .web-hero-scrim {
    background: linear-gradient(180deg, rgba(8, 14, 32, .35) 0%, rgba(8, 14, 32, .78) 100%);
  }
}
.web-hero-eyebrow {
  display: inline-block; background: rgba(255, 255, 255, .14); border-radius: 999px;
  padding: 6px 14px; font-size: var(--ep-text-2xs); font-weight: 700; color: #fff;
}
.web-hero-title { font-size: var(--ep-text-4xl); font-weight: 800; line-height: 1.35; margin-top: 14px; color: #fff; }
.web-hero-desc { font-size: var(--ep-text-md); line-height: 1.75; color: rgba(255, 255, 255, .75); margin: 14px 0 0; }
.web-hero-actions { display: flex; gap: var(--ep-space-3); margin-top: 26px; }
.web-hero-btn-light { background: #fff; color: var(--ep-navy); }
.web-hero-btn-light:hover { background: #EDF1FB; color: var(--ep-navy); }
.web-hero-btn-line { border: 1px solid rgba(255, 255, 255, .4); color: #fff; background: transparent; }
.web-hero-btn-line:hover { background: rgba(255, 255, 255, .1); color: #fff; }
/* 히어로 문구 폭 — 상한이 없으면 넓은 화면에서 한 줄이 너무 길어 다음 줄을 못 찾는다.
   우측에 배너 이미지가 오면 그쪽이 폭을 나눠 가지므로 flex 로 줄어들 수 있어야 한다. */
.web-hero-copy { max-width: 720px; flex: 1 1 auto; min-width: 0; }

/* ── 히어로 배너 (시안: 좌 문구 · 우 400×230 이미지 · 하단 인디케이터) ──
   이 자리는 **관리자가 등록한 배너**가 채운다. 운영자가 이미지를 올리고 문구를 쓰면
   첫 화면의 가장 큰 면적이 그것을 보여준다 — 그게 이 자리의 값이다.
   등록된 것이 없으면 기본 문구로 돌아간다(첫 화면이 비면 안 된다). */
.web-hero { position: relative; }
/* ⚠️ contain 이다 — cover 로 두면 운영자가 올린 그림이 **잘린다**.
   1200×400 배너를 올렸더니 400×230 상자에 맞춰 가운데만 남아 «회원 첫 강의 무» 로 잘렸다
   (2026-08-28 실측). 배너는 «그림 전체» 가 전하려는 말이므로 자르면 안 된다.
   높이만 묶고 폭은 그림 비율에 맡긴다. 꽉 채우고 싶으면 «꽉 채우기» 모드다.
   테두리도 두지 않는다 — contain 은 여백이 생겨 테두리가 그림이 아닌 빈칸을 감싼다. */
.web-hero-image {
  height: 230px; max-width: 44%; flex: 0 1 auto;
  object-fit: contain; object-position: right center; display: block;
}

/* 조작 줄 — 지금 몇 번째인지(숫자·점) + 이전·정지·다음.
   본문과 같은 폭 안에 두어 히어로 글자와 왼쪽 선이 맞는다. */
.web-hero-controls {
  position: absolute; left: 0; right: 0; bottom: 20px;
  max-width: 1160px; margin: 0 auto; padding: 0 24px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--ep-space-4);
}
/* 숫자 카운터 — 점이 여러 개면 세어야 하는데, 숫자는 바로 읽힌다.
   자리가 흔들리지 않게 고정폭 숫자를 쓴다. */
.web-hero-count {
  display: flex; align-items: baseline; gap: var(--ep-space-1);
  font-size: var(--ep-text-xs); color: rgba(255, 255, 255, .6);
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}
.web-hero-count b { font-size: var(--ep-text-md); font-weight: 800; color: #fff; }

/* 이전·정지·다음 */
.web-hero-nav { display: flex; align-items: center; gap: var(--ep-space-2); flex: 0 0 auto; }
.web-hero-btn {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .28); background: rgba(255, 255, 255, .08);
  border-radius: 999px; color: #fff; cursor: pointer; position: relative;
  transition: background-color .18s, border-color .18s;
}
.web-hero-btn:hover { background: rgba(255, 255, 255, .2); border-color: rgba(255, 255, 255, .5); }
.web-hero-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.web-hero-btn .msi { font-size: var(--ep-text-md); }
/* 34px 은 손가락 최소치(40px)에 못 미친다 — 누르는 면만 넓힌다 */
.web-hero-btn::after {
  content: ''; position: absolute; width: 40px; height: 40px;
  left: 50%; top: 50%; translate: -50% -50%;
}

/* 재생·정지 아이콘 — 폰트 서브셋에 pause·play_arrow 가 없다.
   폰트를 다시 만드는 대신 도형으로 그린다(없는 이름을 쓰면 화면에 그 글자가 그대로 찍힌다). */
.web-hero-icon { display: block; width: 10px; height: 11px; position: relative; }
/* 멈춤 상태가 아닐 때 = 지금 돌고 있다 → «정지» 표시(막대 두 개) */
.web-hero-icon::before, .web-hero-icon::after {
  content: ''; position: absolute; top: 0; width: 3px; height: 11px; background: currentColor;
}
.web-hero-icon::before { left: 1px; }
.web-hero-icon::after { right: 1px; }
/* 사람이 멈춘 상태 → «재생» 표시(삼각형). 막대 하나를 삼각형으로 바꾸고 나머지는 숨긴다. */
.web-hero-btn.is-paused .web-hero-icon::before {
  left: 2px; width: 0; height: 0; background: none;
  border-left: 9px solid currentColor;
  border-top: 5.5px solid transparent; border-bottom: 5.5px solid transparent;
}
.web-hero-btn.is-paused .web-hero-icon::after { display: none; }

/* 인디케이터 — 지금 몇 번째인지. 활성만 길게 해서 «현재» 를 형태로 말한다
   (색만 다르게 하면 어두운 배경에서 구분이 약하다). */
.web-hero-dots {
  display: flex; justify-content: center; gap: var(--ep-space-2);
  flex: 1 1 auto; min-width: 0;
}
.web-hero-dot {
  width: 6px; height: 6px; padding: 0; border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, .4); cursor: pointer;
  transition: width .2s cubic-bezier(.2, 0, 0, 1), background-color .2s;
}
.web-hero-dot.is-active { width: 22px; background: #fff; }
/* 점 자체는 6px 이라 손가락으로 못 누른다 — 누르는 면을 40px 로 넓힌다.
   ⚠️ 덧면이 이웃을 덮지 않도록 점 사이 간격(8px)보다 좁게 잡는다(§8 실측 사고). */
.web-hero-dot::after {
  content: ''; position: absolute; width: 40px; height: 40px;
  translate: -50% -50%; left: 50%; top: 50%;
}
.web-hero-dot { position: relative; }
@media (prefers-reduced-motion: reduce) { .web-hero-dot { transition: none; } }

@media (max-width: 960px) {
  /* 좁은 화면에서는 이미지를 문구 아래로 내린다 — 나란히 두면 둘 다 못 읽는다 */
  .web-hero-inner { flex-direction: column; align-items: flex-start; }
  /* contain 이라 aspect-ratio 를 박으면 세로 그림에 빈칸이 크게 남는다 — 높이만 묶는다 */
  .web-hero-image { max-width: 100%; height: 180px; object-position: left center; }
  .web-hero-dots { position: static; margin-top: var(--ep-space-4); justify-content: flex-start; padding: 0 24px 20px; }
}

/* ── 카테고리 그리드 ── */
/* 카드 그리드는 모두 같은 12열 트랙 위에 얹는다 — 트랙·gutter 가 같아야
   위아래 섹션의 열 경계가 맞는다(실측: 예전엔 gap 이 12·16·18 로 갈려 어긋났다). */
.web-cat-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--ep-gutter); }
.web-cat-grid > * { grid-column: span 2; min-width: 0; }
.web-cat-card {
  background: var(--ep-surface); border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-card); padding: 18px 16px;
  display: flex; flex-direction: column; gap: var(--ep-space-3); color: inherit;
}
.web-cat-card:hover { border-color: var(--ep-primary); color: inherit; }
.web-cat-icon {
  width: 38px; height: 38px; border-radius: 12px; background: var(--ep-primary-soft);
  color: var(--ep-primary); display: flex; align-items: center; justify-content: center;
}
.web-cat-name { font-size: var(--ep-text-sm); font-weight: 700; }
.web-cat-count { font-size: var(--ep-text-2xs); color: var(--ep-text-faint); margin-top: 2px; }

/* ── 강의 카드 ── */
.web-course-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--ep-gutter); }
.web-course-grid > * { grid-column: span 3; min-width: 0; }
.web-course-grid.cols-3 > * { grid-column: span 4; }
/* 카드가 배경에서 떠 보여야 "여기부터 여기까지가 한 강의"로 읽힌다.
   예전에는 흰 카드 + 거의 안 보이는 테두리 1px 뿐이고 그림자가 없어서,
   거의 흰 페이지 배경(#F6F8FC) 위에서 경계가 안 잡혔다.
   테두리를 진하게 하는 대신 그림자를 쓴다 — 선을 굵히면 표처럼 보이고,
   그림자는 배경색이 바뀌어도 그대로 통한다. 한 겹이 아니라 두 겹인 이유는
   가까운 그림자(윤곽)와 먼 그림자(부양감)가 하는 일이 다르기 때문이다. */
.web-course-card {
  background: var(--ep-surface); border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-card); overflow: hidden;
  display: flex; flex-direction: column; color: inherit;
  box-shadow: 0 1px 2px rgba(16, 38, 94, .05), 0 6px 16px -6px rgba(16, 38, 94, .10);
  transition-property: box-shadow, translate, border-color;
  transition-duration: .18s;
  transition-timing-function: cubic-bezier(.2, 0, 0, 1);
}
.web-course-card:hover {
  color: inherit;
  border-color: #D3DDF3;
  translate: 0 -3px;
  box-shadow: 0 2px 4px rgba(16, 38, 94, .06), 0 14px 28px -10px rgba(16, 38, 94, .18);
}
/* 호버에서 썸네일이 살짝 당겨지면 "누를 수 있는 것"이라는 신호가 분명해진다 */
.web-course-card:hover .web-course-thumb { scale: 1.04; }
@media (prefers-reduced-motion: reduce) {
  .web-course-card, .web-course-thumb { transition: none; }
  .web-course-card:hover { translate: none; }
  .web-course-card:hover .web-course-thumb { scale: none; }
}
.web-course-thumb {
  height: 132px; position: relative; background-size: cover; background-position: center;
  background-color: #DDE6FA; display: flex; align-items: center; justify-content: center;
  transition: scale .28s cubic-bezier(.2, 0, 0, 1);
}
/* 이미지 없는 강의 — 빗금 대신 브랜드형 플레이스홀더(부드러운 그라디언트 + 아이콘).
   분야별로 색을 돌린다. 전부 같은 색이면 격자가 벽지처럼 보여서
   스크롤하다 "아까 그 카드"를 다시 찾지 못한다. 채도는 낮게 유지해
   제목·가격보다 시선을 먼저 가져가지 않게 한다. */
.web-course-thumb.is-empty { background-image: linear-gradient(135deg, #EAF0FE 0%, #D8E4FC 55%, #C4D4F7 100%); }
/* 브랜드(파랑) 계열을 축으로 명도·색상만 조금씩 돌린다.
   따뜻한 색은 하나만 둔다 — 목록 전체가 주황·분홍이 되면 브랜드가 흐려진다. */
.web-course-thumb.is-empty.tint-1 { background-image: linear-gradient(135deg, #EAF0FE 0%, #D8E4FC 55%, #C4D4F7 100%); }
.web-course-thumb.is-empty.tint-2 { background-image: linear-gradient(135deg, #E7F2FA 0%, #D2E6F5 55%, #B9D6ED 100%); }
.web-course-thumb.is-empty.tint-3 { background-image: linear-gradient(135deg, #E9F4F6 0%, #D3E9ED 55%, #BADCE3 100%); }
.web-course-thumb.is-empty.tint-4 { background-image: linear-gradient(135deg, #EDEFFB 0%, #DCE0F6 55%, #C7CEF0 100%); }
.web-course-thumb.is-empty.tint-5 { background-image: linear-gradient(135deg, #F1EDFA 0%, #E2DAF4 55%, #CFC4EC 100%); }
.web-course-thumb.is-empty.tint-6 { background-image: linear-gradient(135deg, #F7F1E8 0%, #EEE3D2 55%, #E2D2B9 100%); }
.web-course-thumb.is-empty::after {
  content: 'school'; font-family: 'Material Symbols Outlined';
  font-size: 38px; font-weight: 300; color: rgba(40, 87, 224, .30);
}
.web-course-body { padding: 14px 16px 16px; display: flex; flex-direction: column; gap: var(--ep-space-2); flex: 1; }
.web-course-cat { font-size: var(--ep-text-2xs); font-weight: 700; color: var(--ep-primary); }
/* 제목이 카드의 주인공이다. 예전에는 가격과 같은 14px 이라 위계가 평평했고,
   한 줄 말줄임이라 긴 제목이 앞부분만 남아 무슨 강의인지 알 수 없었다.
   두 줄까지 보여주고 높이도 두 줄로 고정한다 — 격자는 카드 높이를 맞추므로,
   제목만 한 줄인 카드는 아래가 그만큼 비어 카드마다 속이 달라 보인다. */
.web-course-title {
  font-size: var(--ep-text-lg); font-weight: 700; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.8em;
}
.web-course-meta {
  font-size: var(--ep-text-2xs); color: var(--ep-text-faint);
  /* 요약 2줄 고정 — 텍스트 길이가 달라도 카드 높이가 균일하게 유지된다 (좁은 화면 정렬 문제 해결) */
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.9em; line-height: 1.45;
}
.web-course-price { font-size: var(--ep-text-md); font-weight: 800; margin-top: auto; font-variant-numeric: tabular-nums; }
/* 「무료」 는 어느 화면에서나 같은 초록이다(2026-08-27 통일).
   전에는 목록·홈이 파랑(--ep-primary), 강좌 상세가 초록이라 같은 강좌의 가격이
   화면을 옮길 때마다 달라 보였다. 파랑을 버린 이유는 두 가지다 —
   이 시스템에서 파랑은 «누를 수 있다» 는 뜻인데 여기는 가격 자리라 누를 것이 없고,
   초록은 이미 «수료·성공» 으로 쓰여 «값을 안 내도 된다» 와 결이 맞는다. */
.web-course-price.is-free { color: var(--ep-green); }
/* 지금 살 수 없는 기수는 값 대신 «언제쯤인지» 를 **같은 자리에** 적는다 (2026-09-03).
   요약과 같은 작은 글씨로 흘리면 카드마다 마지막 줄의 크기가 달라져 격자가 들쭉날쭉해지고,
   무엇보다 손님이 «값이 왜 없지» 하고 그냥 지나친다. 자리와 크기는 가격에 맞추고
   무게만 낮춰(700) «이건 값이 아니다» 를 알린다. 색 막대나 배지는 두지 않는다. */
.web-course-price.is-pending { font-weight: 700; color: var(--ep-text-muted); }

/* ── 검색·필터 칩 ── */
.web-search {
  display: flex; align-items: center; gap: var(--ep-space-2);
  background: var(--ep-surface); border: 1.5px solid var(--ep-primary);
  border-radius: 999px; padding: 4px 8px 4px 20px; max-width: 560px;
}
.web-search input { flex: 1; border: 0; font: inherit; font-size: var(--ep-text-sm); outline: none; background: transparent; }
.web-search:focus-within { box-shadow: 0 0 0 3px rgba(40, 87, 224, .12); }
.web-chips { display: flex; gap: var(--ep-space-2); flex-wrap: wrap; }
.web-chip {
  font-size: var(--ep-text-xs); font-weight: 600; border-radius: 999px; padding: 9px 18px;
  background: var(--ep-surface); color: var(--ep-text-muted);
  border: 1px solid var(--ep-border); white-space: nowrap;
}
.web-chip:hover { color: var(--ep-primary); border-color: var(--ep-primary); }
.web-chip.is-active { background: var(--ep-navy); color: #fff; border-color: var(--ep-navy); font-weight: 700; }
.web-chip.is-active:hover { color: #fff; }

/* 필터 묶음 — 여러 줄이 한 덩어리임을 보이게 (DESIGN_RULES §3 "줄을 나누고 각 줄 앞에 라벨").
   예전엔 세 줄이 페이지 배경 위에 그냥 떠 있고 줄 간격이 16/8/16px 로 섞여 있어서,
   가격·분야는 붙어 보이고 정렬만 멀어져 어디까지가 필터인지 읽히지 않았다(실측).
   여백은 패널이 한 값으로 정하고, 각 줄은 자기 margin 을 갖지 않는다. */
/* 필터 묶음 — 라벨과 항목을 **같은 칸**에 세우고 줄마다 선으로 나눈다.
   예전엔 flex 라 라벨 다음 칸의 시작점이 줄마다 달랐다: 칩은 안쪽 여백 18px 만큼
   글자가 밀리고 링크는 0 이라, 같은 라벨 폭인데도 항목이 들쭉날쭉했다(지적받은 자리).
   grid 로 라벨 열을 고정하면 어느 줄이든 항목이 한 세로선에서 시작한다. */
.web-filter-panel {
  background: var(--ep-surface);
  border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-card);
  padding: 0 var(--ep-space-5);
}
.web-filter-row {
  display: grid;
  grid-template-columns: 48px 1fr;
  align-items: center;
  gap: var(--ep-space-4);
  padding: var(--ep-space-3) 0;
  border-top: 1px solid var(--ep-border-soft);
}
.web-filter-row:first-child { border-top: 0; }
.web-filter-row .ep-filter-label { margin: 0; }
/* 항목 열 — 칩이든 셀렉트든 이 안에서만 흐른다 */
.web-filter-items { display: flex; align-items: center; flex-wrap: wrap; gap: var(--ep-space-2); min-width: 0; }

@media (max-width: 560px) {
  /* 좁은 화면에서는 라벨을 항목 위로 — 48px 열을 유지하면 칩이 두 글자 폭으로 찌그러진다 */
  .web-filter-row { grid-template-columns: 1fr; gap: var(--ep-space-2); }
}

/* ── 강의 상세 ── */
/* 강의 상세: 본문 8 + 구매 패널 4 (고정 320px 대신 같은 트랙을 쓴다) */
.web-detail-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--ep-gutter); align-items: start; }
.web-detail-grid > *:first-child { grid-column: span 8; min-width: 0; }
.web-detail-grid > *:last-child { grid-column: span 4; min-width: 0; }
.web-detail-aside {
  position: sticky; top: 88px;
  background: var(--ep-surface); border: 1px solid var(--ep-border);
  border-radius: var(--ep-radius-panel); padding: 22px;
  display: flex; flex-direction: column; gap: var(--ep-space-4);
}
.web-detail-price { font-size: var(--ep-text-3xl); font-weight: 800; color: var(--ep-primary); font-variant-numeric: tabular-nums; }
.web-curriculum-row { display: flex; align-items: center; gap: var(--ep-space-3); padding: 13px 4px; border-bottom: 1px solid var(--ep-border-soft); font-size: var(--ep-text-sm); }
.web-curriculum-row:last-child { border-bottom: 0; }

/* ── 푸터 ── */
.web-footer { background: var(--ep-navy-dark); color: rgba(255, 255, 255, .6); margin-top: 56px; }
.web-footer-inner { max-width: var(--ep-container); margin: 0 auto; padding: 40px 24px 32px; }
.web-footer-cols { display: flex; justify-content: space-between; gap: var(--ep-space-8); flex-wrap: wrap; }
/* 태그가 아니라 클래스로 잡는다. 예전엔 h4 였는데, 페이지 제목(h1)·섹션(h2) 다음에
   h4 가 오면서 전 페이지에서 제목 계층이 한 단계씩 건너뛰었다 —
   스크린리더는 이 계층으로 문서 구조를 읽는다. 태그를 h3 으로 올리고
   선택자는 클래스로 바꿔, 나중에 태그가 또 바뀌어도 스타일이 안 떨어지게 한다. */
.web-footer-head { font-size: var(--ep-text-xs); font-weight: 800; color: #fff; margin: 0 0 var(--ep-space-3); }
.web-footer-links { display: flex; flex-direction: column; gap: var(--ep-space-2); font-size: var(--ep-text-xs); }
.web-footer-links a { color: rgba(255, 255, 255, .6); }
.web-footer-links a:hover { color: #fff; }
.web-footer-legal { border-top: 1px solid rgba(255,255,255,.12); margin-top: 28px; padding-top: 20px; font-size: var(--ep-text-2xs); line-height: 1.8; }

/* ── 인증(로그인/가입) 스플릿 ── */
.auth-stats { display: flex; gap: var(--ep-space-7); margin-top: 34px; }
.auth-stats b { display: block; font-size: var(--ep-text-3xl); font-weight: 800; }
.auth-stats span { font-size: var(--ep-text-xs); color: rgba(255,255,255,.65); }
.auth-form-panel { display: flex; align-items: center; justify-content: center; padding: 40px 24px; }
.auth-title { font-size: var(--ep-text-2xl); font-weight: 800; margin: 0 0 6px; }
.auth-sub { font-size: var(--ep-text-sm); color: var(--ep-text-muted); margin-bottom: 26px; }
.auth-tabs { display: flex; background: #EEF1F7; border-radius: 999px; padding: 5px; margin-bottom: 28px; }
.auth-tab { flex: 1; text-align: center; font-size: var(--ep-text-sm); font-weight: 600; border-radius: 999px; padding: 10px 0; color: var(--ep-text-faint); }
.auth-tab.is-active { background: #fff; color: var(--ep-primary); font-weight: 700; box-shadow: var(--ep-shadow-card); }
/* 카드 하단 전환 안내 — 로그인↔가입 */
.auth-alt { text-align: center; font-size: var(--ep-text-sm); color: var(--ep-text-muted); margin-top: 20px; }
.auth-alt a { color: var(--ep-primary); font-weight: 700; }

/* 모바일 메뉴 드로어 — 기본은 숨김(넓은 화면은 가로 메뉴가 보인다).
   ⚠️ 이 기본 규칙은 아래 @media 보다 **앞**에 와야 한다. 뒤에 두면 같은 특이도라
      좁은 화면에서도 display:none 이 이겨 버튼이 영영 안 보인다(실측으로 겪음). */
.web-navdrawer, .web-navdrawer-toggle, .web-navdrawer-backdrop { display: none; }
.web-navdrawer a {
  padding: var(--ep-space-3); border-radius: var(--ep-radius-input);
  color: var(--ep-text-sub); font-weight: 700; font-size: var(--ep-text-md);
}
.web-navdrawer a:hover, .web-navdrawer a.is-active { background: var(--ep-border-soft); color: var(--ep-primary); }
.web-navdrawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--ep-space-2); }
@media (prefers-reduced-motion: reduce) { .web-navdrawer { transition: none; } }

@media (max-width: 960px) {
  .web-cat-grid > * { grid-column: span 4; }
  .web-course-grid > *, .web-course-grid.cols-3 > * { grid-column: span 6; }
  .web-detail-grid > *:first-child, .web-detail-grid > *:last-child { grid-column: 1 / -1; }
  .web-detail-aside { position: static; }
  /* 좁은 화면에서는 상단 가로 메뉴·검색을 숨기고 **드로어로 옮긴다**.
     예전에는 숨기기만 해서 모바일 방문자가 헤더에서 어디로도 갈 수 없었다
     (로고 말고는 링크가 0개). 숨기는 것과 대체 수단을 두는 것은 다른 일이다. */
  .web-nav { display: none; }
  .web-header-search { display: none; }
  .web-navdrawer-toggle { display: inline-flex; }

  .web-navdrawer {
    position: fixed; top: 0; right: 0; bottom: 0; height: 100%;
    width: min(300px, 84vw); z-index: 60; background: var(--ep-surface);
    padding: var(--ep-space-5); overflow-y: auto;
    display: flex; flex-direction: column; gap: var(--ep-space-2);
    transform: translateX(100%);
    transition: transform .22s cubic-bezier(.2, 0, 0, 1);
    box-shadow: 0 12px 32px rgba(16, 38, 94, .18);
  }
  .web-navdrawer.is-open { transform: translateX(0); }
  .web-navdrawer-backdrop.is-open {
    display: block; position: fixed; inset: 0; z-index: 50;
    background: rgba(11, 27, 63, .45);
  }
}

@media (max-width: 560px) {
  .web-cat-grid > *, .web-course-grid > *, .web-course-grid.cols-3 > * { grid-column: span 6; }
  /* 헤더 압축 — 좁은 폭 가로 오버플로 방지 (로고 마크만, 여백·갭 축소) */
  .web-header-inner { gap: var(--ep-space-3); padding-left: 16px; padding-right: 16px; }
  .web-logo-text { display: none; }
  .web-header-actions { margin-left: auto; gap: var(--ep-space-2); }
}

/* 소형 화면 헤더 — 로그인 상태에서 액션이 3개(강의실·아바타·로그아웃)가 되면
   560px 규칙만으로는 부족해 헤더가 뷰포트를 5~6px 밀었다(마이페이지 375px 실측).
   비로그인 화면에서는 액션이 2개라 드러나지 않던 문제다.
   버튼 라벨을 줄이고 아바타를 감춰 폭을 확보한다. */
@media (max-width: 420px) {
  .web-header-inner { gap: var(--ep-space-2); padding-left: 12px; padding-right: 12px; }
  .web-header-actions { gap: var(--ep-space-1); min-width: 0; }
  .web-header-actions .ep-btn { padding-left: 11px; padding-right: 11px; font-size: var(--ep-text-2xs); }
  .web-header-actions .web-username { display: none; }   /* 마이페이지 진입은 좌측 내비로 가능 */
}

/* 소형 단말(320~380px: iPhone SE 1세대·구형 안드로이드).
   flex/grid 아이템의 기본 min-width:auto 때문에 히어로 문단이 뷰포트 아래로 안 줄어들어
   320px 에서 가로 스크롤이 생겼다(홈 44px·소개 11px 초과 실측). 최소폭 해제 + 여백·타이포 축소. */
@media (max-width: 380px) {
  .web-hero-inner { padding-left: 16px; padding-right: 16px; gap: var(--ep-space-6); }
  .web-hero-copy, .web-hero-inner > * { min-width: 0; }
  .web-hero-title { font-size: var(--ep-text-3xl); }
  .web-hero-desc { font-size: var(--ep-text-sm); }
  .web-hero-actions { flex-wrap: wrap; }
  .web-hero-actions > .ep-btn { flex: 1 1 auto; min-width: 0; }
  .web-cat-grid > *, .web-course-grid > *, .web-course-grid.cols-3 > * { grid-column: 1 / -1; }
  .web-prose, .web-section-inner { padding-left: 16px; padding-right: 16px; }
}

/* ==========================================================================
   홈 6섹션 (시안 EDUPIC 메인) — 카드 배지·B2B·하단 2:1
   ========================================================================== */
/* 강의 카드 순위/NEW 배지 */
.web-course-thumb { position: relative; }
.web-course-rank {
  position: absolute; top: 0; left: 0; width: 26px; height: 26px;
  background: var(--ep-navy); color: #fff; font-size: var(--ep-text-xs); font-weight: 800;
  display: flex; align-items: center; justify-content: center; border-radius: 0 0 8px 0;
  font-variant-numeric: tabular-nums;
}
.web-course-new {
  position: absolute; top: 10px; left: 10px;
  background: var(--ep-cyan); color: #fff; font-size: 10px; font-weight: 800;
  border-radius: 999px; padding: 4px 10px; letter-spacing: .5px;
}

/* 기업 교육 CTA 배너 */
.web-b2b {
  background: var(--ep-navy); border-radius: 20px; padding: 30px 36px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--ep-space-6);
}
.web-b2b-title { font-size: var(--ep-text-xl); font-weight: 800; color: #fff; }
.web-b2b-desc { font-size: var(--ep-text-sm); color: rgba(255, 255, 255, .7); margin-top: 6px; }
.web-b2b-btn { background: #fff; color: var(--ep-navy); font-weight: 800; flex-shrink: 0; }
.web-b2b-btn:hover { background: #EDF1FB; color: var(--ep-navy); }


/* ── 메인 «중단» 프로모션 타일 (position = main_middle) ──
   partial: resources/views/web/partials/banner-strip.blade.php

   ⚠️ 2026-08-31 재설계(2차). 8/28 판은 «풀블리드 밴드» 였고 주인님께
      「붙어있어서 별로」 를 들었다. 참고 화면을 **나란히 놓고** 다시 보니 기억이 틀렸다 —
      인프런은 밴드가 아니라 **본문 폭 안의 카드 한 장**이고, 휴넷은 **가로 3열 타일**이며,
      경기도지식·패스트캠퍼스의 풀블리드 밴드는 **한 개뿐**이었다.
      **풀블리드를 여러 장 쌓는 곳은 하나도 없다.** 8/28 판이 정확히 그것을 했다.

   그래서 그리드 하나로 통일한다 — 한 장이면 폭을 채워 넓은 카드(인프런 얼개),
   두세 장이면 나란히(휴넷 얼개). 풀블리드는 하단 얇은 띠에만 남긴다. */
/* 열 수는 화면(Blade)이 «등록 개수» 로 정해서 클래스로 알려준다 — auto-fit 에 맡기면
   넉 장일 때 3+1 이 되어 마지막 한 장이 혼자 남는다. 참고처는 그런 줄을 남기지 않는다. */
.web-promo-grid { display: grid; gap: var(--ep-gutter); }
.web-promo-grid.is-cols-1 { grid-template-columns: 1fr; }
.web-promo-grid.is-cols-2 { grid-template-columns: repeat(2, 1fr); }
.web-promo-grid.is-cols-3 { grid-template-columns: repeat(3, 1fr); }
/* 좁아지면 열을 줄인다 — 320px 아래로 눌리면 글과 그림이 둘 다 못 읽힌다 */
@media (max-width: 1080px) {
  .web-promo-grid.is-cols-3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .web-promo-grid { grid-template-columns: 1fr !important; }
}
/* 한 장뿐이면 폭이 본문 전체다 — 그림이 작으면 가운데가 휑하게 빈다(실측).
   그림을 키우고 타일도 조금 높인다(인프런의 단일 프로모션 카드와 같은 비례). */
.web-promo-grid.is-cols-1 .web-promo { min-height: 172px; }
.web-promo-grid.is-cols-1 .web-promo-image { height: 132px; }
/* 열이 셋이면 타일이 좁다 — 좌우로 벌리면 글이 두세 글자씩 끊긴다(실측).
   그때는 그림을 글 위로 올려 세로로 쌓는다. */
.web-promo-grid.is-cols-3 .web-promo { flex-direction: column; align-items: flex-start; }
.web-promo-grid.is-cols-3 .web-promo-image {
  order: -1; max-width: 100%; height: 76px; object-position: left center;
  margin-bottom: var(--ep-space-3);
}
.web-promo {
  position: relative; overflow: hidden;
  min-height: 148px; padding: 24px 26px;
  border-radius: var(--ep-radius-panel);
  background: var(--ep-surface);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--ep-space-6);
  color: inherit; text-decoration: none;
}
/* 운영자가 색을 안 고른 타일. 페이지 배경(#F6F8FC)과 흰 면은 거의 같은 색이라
   경계가 없으면 타일인 줄 모른다 — 없는 색을 지어내는 대신 실선으로 자리를 긋는다. */
.web-promo.is-plain { border: 1px solid var(--ep-border); }
/* 링크 타일만 반응한다 — 안 눌리는 타일이 눌릴 것처럼 보이면 안 된다 */
.web-promo.is-link { cursor: pointer; transition: box-shadow .18s, transform .18s; }
.web-promo.is-link:hover { box-shadow: var(--ep-shadow-card); }
.web-promo.is-link:focus-visible { outline: 2px solid var(--ep-primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .web-promo.is-link { transition: none; } }

.web-promo-copy { position: relative; z-index: 2; min-width: 0; flex: 1 1 auto; }
.web-promo-title {
  display: block; font-size: var(--ep-text-lg); font-weight: 800;
  color: var(--ep-text); text-wrap: balance; line-height: 1.4;
}
.web-promo-body {
  margin: 8px 0 0; font-size: var(--ep-text-sm); color: var(--ep-text-muted);
  text-wrap: pretty;
}
.web-promo-more {
  display: inline-flex; align-items: center; gap: var(--ep-space-1); margin-top: 14px;
  font-size: var(--ep-text-xs); font-weight: 800; color: var(--ep-primary);
}
.web-promo-more .msi { font-size: var(--ep-text-md); }
/* ⚠️ contain 이다 — cover 로 두면 가로로 긴 띠 이미지가 가운데 한 글자만 남는다
   (2026-08-28 실측: 「아주 넓은 띠 이미지」 배너가 «지» 한 글자로 잘렸다).
   높이만 묶고 폭은 그림 비율에 맡긴다. 꽉 채우고 싶으면 «꽉 채우기» 모드다. */
.web-promo-image {
  position: relative; z-index: 2;
  height: 104px; max-width: 44%; flex: 0 1 auto;
  object-fit: contain; object-position: right center; display: block;
}

/* 꽉 채우기 — 그림이 타일 전체를 덮는다. 바탕색과 그림이 어긋날 일이 없어진다. */
.web-promo.is-cover { justify-content: flex-start; }
.web-promo-cover {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center; z-index: 0; display: block;
}
/* 글을 얹을 때만 까는 어두운 겹 — 밝은 사진 위의 흰 글씨는 안 읽힌다.
   히어로와 같은 방향(왼쪽이 짙고 오른쪽으로 옅어짐)이라 두 자리가 같은 규칙으로 읽힌다. */
.web-promo-scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(90deg, rgba(8, 14, 32, .78) 0%, rgba(8, 14, 32, .5) 55%, rgba(8, 14, 32, .12) 100%);
}
/* 어두운 바탕(운영자가 고른 짙은 색 또는 사진) 위에서는 글자를 뒤집는다 */
.web-promo.is-on-dark .web-promo-title { color: #fff; }
.web-promo.is-on-dark .web-promo-body { color: rgba(255, 255, 255, .8); }
.web-promo.is-on-dark .web-promo-more { color: #fff; }
.web-promo.is-on-dark.is-link:focus-visible { outline-color: #fff; }
@media (max-width: 760px) {
  .web-promo { flex-direction: column; align-items: flex-start; gap: var(--ep-space-5); padding: 22px; }
  .web-promo-image { max-width: 100%; height: 88px; object-position: left center; }
  /* 좁은 화면에서는 가로 그라데이션이 글 아래까지 안 닿는다 — 세로로 바꾼다 */
  .web-promo-scrim {
    background: linear-gradient(180deg, rgba(8, 14, 32, .35) 0%, rgba(8, 14, 32, .78) 100%);
  }
}


/* ── 메인 «하단» 알림 띠 (position = main_bottom) ──
   partial: resources/views/web/partials/banner-ticker.blade.php

   참고한 넷 다 이 얇은 띠를 따로 가지고 있었다(높이 30~55px, 한 줄, 작은 CTA, 닫기 X).
   중단 밴드와 **다른 모양**이어야 «위치를 고르는 것» 이 뜻을 갖는다.
   그림은 쓰지 않는다 — 이 자리가 담는 것은 프로모션이 아니라 공지다. */
.web-ticker { background: var(--ep-navy, #10265E); }
/* 닫기는 hidden 으로 끈다 — common.css 맨 위의 «[hidden]» 규칙이 처리한다 */
.web-ticker-inner {
  max-width: 1160px; margin: 0 auto; padding: 0 24px;
  min-height: 48px;
  display: flex; align-items: center; gap: var(--ep-space-3);
}
.web-ticker-icon { font-size: var(--ep-text-lg); flex: 0 0 auto; opacity: .85; }
/* 한 줄을 넘기면 자른다 — 띠가 두 줄이 되면 더 이상 띠가 아니다 */
.web-ticker-text {
  min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--ep-text-sm);
}
.web-ticker-text b { font-weight: 800; }
.web-ticker-sub { margin-left: var(--ep-space-2); opacity: .75; }
.web-ticker-cta {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--ep-space-1);
  font-size: var(--ep-text-xs); font-weight: 800; text-decoration: none;
  border: 1px solid currentColor; border-radius: 999px; padding: 5px 10px 5px 12px;
  /* 44×44 를 못 채우는 작은 알약이라 눌리는 넓이를 가상요소로 넓힌다 */
  position: relative;
}
.web-ticker-cta::after { content: ''; position: absolute; inset: -10px -6px; }
.web-ticker-cta .msi { font-size: var(--ep-text-md); }
.web-ticker-x {
  flex: 0 0 auto; width: 32px; height: 32px; display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%; cursor: pointer; color: inherit;
  position: relative;
}
.web-ticker-x::after { content: ''; position: absolute; inset: -6px; }
.web-ticker-x:hover { background: rgba(127, 127, 127, .18); }
.web-ticker-x .msi { font-size: var(--ep-text-lg); }
.web-ticker.is-on-dark { color: #fff; }
.web-ticker.is-on-dark .web-ticker-cta:hover { background: rgba(255, 255, 255, .14); }
.web-ticker:not(.is-on-dark) { color: var(--ep-text); }
.web-ticker:not(.is-on-dark) .web-ticker-cta:hover { background: rgba(0, 0, 0, .06); }
@media (max-width: 760px) {
  .web-ticker-inner { padding: 8px 16px; flex-wrap: wrap; }
  .web-ticker-text { flex: 1 1 100%; order: 2; white-space: normal; }
}


/* ── 고객센터 카드 (공개단 공통) ──
   home·about·support 가 공유한다. 호출부는 버튼 목록만 넘기고 폭·색을 정하지 않는다.
   partial: resources/views/web/partials/support-card.blade.php */
/* 짝(.ep-row.is-paired) 안에서 옆 박스만큼 늘어났을 때, 남는 세로를 버리지 않고
   버튼을 바닥으로 내려 두 박스의 아래 기준선을 맞춘다(.web-support-actions 의 margin-top:auto).
   늘어나지 않는 곳에서는 auto 가 0 이 되어 아무 영향이 없다. */
/* 이 카드는 옆 카드와 밑변을 맞추느라(.ep-row.is-paired) 내용보다 훨씬 커질 때가 있다.
   공지·고객센터 화면이 그렇다 — 내용은 200px 인데 카드가 480px 로 늘어난다.
   그때 내용을 위에 붙여 두면 아래가 통째로 빈다. 세로 가운데로 모아 위아래로 나눈다.
   카드가 내용 높이일 때(home·about)는 아무 효과가 없다. */
.web-support-card { background: var(--ep-primary-soft); border-radius: var(--ep-radius-panel); padding: var(--ep-space-6); display: flex; flex-direction: column; justify-content: center; }
.web-support-head { display: flex; align-items: center; gap: var(--ep-space-2); color: var(--ep-navy); font-size: var(--ep-text-md); font-weight: 800; }
.web-support-head .msi { font-size: var(--ep-text-2xl); color: var(--ep-primary); }
.web-support-phone { font-size: var(--ep-text-2xl); font-weight: 800; color: var(--ep-navy); margin-top: var(--ep-space-2); letter-spacing: -.01em; }
.web-support-hours { font-size: var(--ep-text-xs); color: var(--ep-text-muted); margin-top: var(--ep-space-1); }
/* 버튼은 개수와 무관하게 균등 분할 — 1개면 자연히 가로 꽉 찬다(호출부 인라인 width 불필요) */
/* margin-top:auto 로 버튼을 바닥에 못박지 않는다 — 그게 카드 중간을 비우던 원인이다.
   버튼은 안내 문구 바로 아래 붙어 한 덩어리로 읽혀야 한다. */
.web-support-actions { display: flex; gap: var(--ep-space-2); padding-top: var(--ep-space-4); }
.web-support-actions .ep-btn { flex: 1; min-width: 0; }

@media (max-width: 960px) {
  .web-b2b { flex-direction: column; align-items: flex-start; }
}

/* ==========================================================================
   표준 콘텐츠 폭·제목 (DESIGN_RULES §1·§2) — 개별 뷰의 인라인 max-width/font-size 대체
   ========================================================================== */
.web-prose  { max-width: var(--ep-prose); margin-left: auto; margin-right: auto; padding-left: 24px; padding-right: 24px; }   /* 정책·회사소개 등 가독 본문 */
.web-narrow { max-width: var(--ep-form); margin-left: auto; margin-right: auto; padding-left: 24px; padding-right: 24px; }   /* 결제 등 단일 컬럼 폼 */
.auth-narrow { max-width: var(--ep-auth); margin-left: auto; margin-right: auto; padding-left: 24px; padding-right: 24px; }  /* 로그인·가입 */
/* 교강사 소개 — 저장 시 정화기를 거친 값(줄바꿈은 <br>, 주소는 링크).
   ⚠️ 무엇이 들어올지 우리가 정하지 않는다. 그래서 둘을 화면이 감당한다:
     · 띄어쓰기 없이 길게 쓴 글(주소 등)이 상자를 뚫지 않게 끊는다(anywhere)
     · 한 줄이 너무 길어지지 않게 읽기 폭을 준다 — 패널이 넓어도 글줄은 좁게
   pre-line 은 쓰지 않는다 — 줄바꿈이 이미 <br> 로 들어 있어 두 번 벌어진다. */
.web-tutor-bio {
  margin: var(--ep-space-3) 0 0;
  max-width: var(--ep-prose);
  overflow-wrap: anywhere;
  font-size: var(--ep-text-sm);
  line-height: 1.75;
  color: var(--ep-text-sub);
}
.web-tutor-bio a { color: var(--ep-primary); text-decoration: underline; }

/* 공개(web) 표준 페이지 제목 — 인라인 font-size 반복 대체 */
.web-page-title { font-size: var(--ep-text-2xl); font-weight: 800; margin: 0 0 var(--ep-space-4); letter-spacing: -.01em; line-height: 1.3; }
.web-page-title.is-flush { margin: 0; }   /* 그리드·패널 안에서 여백을 부모가 담당할 때 */

/* ==========================================================================
   회사소개(about) — 구성 있는 마케팅 레이아웃 (슬롭 탈피: 위계·여백리듬·채움)
   ========================================================================== */
.web-hero-copy { max-width: 560px; }
.about-hero-stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ep-gutter); flex-shrink: 0; }
.about-stat {
  background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.16);
  border-radius: 16px; padding: 18px 22px; min-width: 0;
}
.about-hero-stats { min-width: 300px; }
.about-stat b { display: block; font-size: var(--ep-text-3xl); font-weight: 800; color: #fff; letter-spacing: -.01em; }
.about-stat span { font-size: var(--ep-text-xs); color: rgba(255,255,255,.72); }

.about-section-head { text-align: center; max-width: 640px; margin: 0 auto var(--ep-space-8); }
.about-h2 { font-size: var(--ep-text-3xl); font-weight: 800; color: var(--ep-navy); margin: 0; letter-spacing: -.02em; }
.about-sub { font-size: var(--ep-text-md); color: var(--ep-text-muted); margin: 10px 0 0; line-height: 1.6; }

.about-value { background: var(--ep-surface); border: 1px solid var(--ep-border); border-radius: var(--ep-radius-card); padding: 26px 24px; box-shadow: var(--ep-shadow-card); }
.about-value-icon { display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 13px; background: var(--ep-primary-soft); color: var(--ep-primary); margin-bottom: 16px; }
.about-value-icon .msi { font-size: var(--ep-text-2xl); }
.about-value h3 { font-size: var(--ep-text-lg); font-weight: 800; margin: 0 0 8px; color: var(--ep-text); }
.about-value p { font-size: var(--ep-text-sm); color: var(--ep-text-muted); line-height: 1.7; margin: 0; }

/* 사업자 정보 — 항목 4개를 2×2 로 눕힌다.
   세로 1열로 쌓으면 한 줄짜리 데이터가 행당 69px 을 먹어(실측) 옆 카드보다
   173px 이나 길어졌다. 참조용 정보에 세로를 그만큼 쓸 이유가 없다. */
.about-info-list { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ep-space-1) var(--ep-space-6); }
.about-contact-row { display: flex; align-items: center; gap: var(--ep-space-3); padding: var(--ep-space-3) 0; border-bottom: 1px solid var(--ep-border-soft); }
/* 마지막 '행'은 2열이라 아래 두 개다 — 3·4번째의 밑줄을 지운다 */
.about-info-list > .about-contact-row:nth-last-child(-n+2) { border-bottom: 0; }
.about-contact-row > .msi { font-size: var(--ep-text-xl); color: var(--ep-primary); width: 26px; text-align: center; flex-shrink: 0; }
.about-contact-row .t { display: flex; flex-direction: column; min-width: 0; }
.about-contact-row .t span { font-size: var(--ep-text-2xs); color: var(--ep-text-faint); }
.about-contact-row .t b { font-size: var(--ep-text-md); color: var(--ep-text); font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 640px) { .about-info-list { grid-template-columns: 1fr; } }

/* .about-contact-card 는 .web-support-card 로 통합됐다(2026-08-05).
   같은 고객센터 카드가 화면마다 다른 모습이었고, 네이비 그라디언트가
   히어로·CTA 밴드와 함께 한 페이지에 3번 반복되고 있었다. */

.web-cta-band { background: linear-gradient(115deg, #0B1B3F 0%, #10265E 40%, #2857E0 120%); border-radius: 22px; padding: var(--ep-space-8) var(--ep-space-10); display: flex; align-items: center; justify-content: space-between; gap: var(--ep-space-7); }
.web-cta-title { font-size: var(--ep-text-2xl); font-weight: 800; color: #fff; letter-spacing: -.01em; }
.web-cta-desc { font-size: var(--ep-text-md); color: rgba(255,255,255,.78); margin-top: 9px; line-height: 1.6; }
.web-cta-btn { background: #fff; color: var(--ep-navy); }
.web-cta-btn:hover { background: #EDF1FB; color: var(--ep-navy); }

@media (max-width: 960px) {
  .web-hero-inner { flex-direction: column; align-items: flex-start; }
  .about-hero-stats { width: 100%; min-width: 0; }
}
@media (max-width: 768px) {
  .web-cta-band { flex-direction: column; text-align: center; align-items: stretch; }
}

/* ==========================================================================
   강의 상세 — 시안 수준(브랜드 히어로·메타·커리큘럼·sticky 수강신청)
   ========================================================================== */
/* inline-flex 였을 때는 줄상자의 내림(descender) 만큼 아래가 더 벌어져
   다음 블록과의 간격이 16px 이 아니라 21px 이 됐다. 블록으로 두되
   fit-content 로 폭을 내용에 맞춰 클릭 영역이 한 줄을 통째로 먹지 않게 한다. */
.web-detail-side { position: sticky; top: 88px; align-self: start; display: flex; flex-direction: column; gap: var(--ep-gutter); }
@media (max-width: 960px) { .web-detail-side { position: static; } }

.web-detail-hero { height: 300px; border-radius: 20px; background-size: cover; background-position: center; background-color: #DDE6FA; position: relative; display: flex; align-items: center; justify-content: center; }
.web-detail-hero.is-empty { background-image: linear-gradient(135deg, #EAF0FE 0%, #D8E4FC 55%, #C4D4F7 100%); }
.web-detail-hero.is-empty::after { content: 'school'; font-family: 'Material Symbols Outlined'; font-size: 64px; font-weight: 300; color: rgba(40, 87, 224, .26); }
.web-detail-cat { position: absolute; top: 16px; left: 16px; background: rgba(16, 38, 94, .85); color: #fff; font-size: var(--ep-text-2xs); font-weight: 700; border-radius: 999px; padding: 6px 13px; }
.web-detail-title { font-size: var(--ep-text-3xl); font-weight: 800; letter-spacing: -.01em; line-height: 1.35; margin: 0; color: var(--ep-text); }
.web-detail-summary { font-size: var(--ep-text-md); color: var(--ep-text-muted); margin: 10px 0 0; line-height: 1.6; }
.web-detail-meta { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 16px; }
.web-detail-meta span { display: inline-flex; align-items: center; gap: var(--ep-space-1); font-size: var(--ep-text-xs); color: var(--ep-text-sub); font-weight: 600; }
.web-detail-meta .msi { font-size: var(--ep-text-lg); color: var(--ep-primary); }
/* ⚠️ .web-detail-desc 는 2026-08-28 에 제거했다. 강좌 상세 설명이 이제 HTML 이라
   .ep-prose 를 쓴다. 이 클래스에는 `white-space: pre-line` 이 있었는데(plain text 시절
   줄바꿈을 살리는 용도) HTML 에 그대로 걸면 **소스의 개행이 전부 빈 줄로 렌더된다** —
   태그 사이를 줄바꿈해 적은 강사 코드가 위아래로 한참 벌어진다(실측). 되살리지 말 것. */

.web-cur-lesson { border: 1px solid var(--ep-border); border-radius: 14px; margin-bottom: 10px; overflow: hidden; }
.web-cur-lesson-head { display: flex; align-items: center; gap: var(--ep-space-3); padding: 14px 16px; background: var(--ep-primary-softer); }
.web-cur-no { width: 24px; height: 24px; border-radius: 8px; background: var(--ep-navy); color: #fff; font-size: var(--ep-text-xs); font-weight: 800; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.web-cur-item { display: flex; align-items: center; gap: var(--ep-space-3); padding: 11px 16px 11px 20px; font-size: var(--ep-text-sm); border-top: 1px solid var(--ep-border-soft); color: var(--ep-text-sub); }
.web-cur-item .msi { font-size: var(--ep-text-xl); color: var(--ep-icon-faint); flex-shrink: 0; }

.web-enroll-card { background: var(--ep-surface); border: 1px solid var(--ep-border); border-radius: 18px; padding: 20px; box-shadow: var(--ep-shadow-card); }
.web-enroll-head { font-size: var(--ep-text-md); font-weight: 800; margin-bottom: 14px; }
.web-enroll-class { display: block; border: 1px solid var(--ep-border); border-radius: 12px; padding: 13px 14px; color: inherit; margin-bottom: 10px; transition-property: border-color, background-color; transition-duration: .12s; }
.web-enroll-class:last-child { margin-bottom: 0; }
/* ⚠️ hover 를 걷어냈다 (2026-09-03).
   이 카드는 예전에 통째로 <a> 였다 — 어디를 눌러도 수업 상세로 갔으니 hover 가 맞았다.
   지금은 <div> 안에 신청 <form> 이 들어 있어 **카드 자체는 눌러도 아무 일이 없다.**
   그런데 hover 만 남아 있어서 «눌리는 것처럼» 보였다(거짓 어포던스).
   눌리는 것은 카드 안의 버튼과 링크뿐이고, 그것들은 각자 hover 를 갖고 있다. */
.web-enroll-price { font-size: var(--ep-text-lg); font-weight: 800; color: var(--ep-primary); margin-top: 8px; font-variant-numeric: tabular-nums; }
.web-enroll-price.is-free { color: var(--ep-green); }
.web-enroll-facts { background: var(--ep-surface); border: 1px solid var(--ep-border); border-radius: 18px; padding: 8px 20px; margin-top: 14px; }
.web-enroll-facts > div { display: flex; justify-content: space-between; align-items: center; padding: 11px 0; border-bottom: 1px solid var(--ep-border-soft); font-size: var(--ep-text-xs); }
.web-enroll-facts > div:last-child { border-bottom: 0; }
.web-enroll-facts span { color: var(--ep-text-faint); }
.web-enroll-facts b { color: var(--ep-text); font-weight: 700; text-align: right; }

/* 강의 카드 강사·수강생 메타 (시안 카드 정보밀도 — 요약 대체) */
.web-course-by { display: flex; align-items: center; gap: var(--ep-space-1); font-size: var(--ep-text-2xs); color: var(--ep-text-faint); min-height: 1.5em; overflow: hidden; white-space: nowrap; }
.web-course-by .msi { font-size: var(--ep-text-md); color: var(--ep-icon-faint); }
.web-course-by-name { max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ep-text-muted); font-weight: 600; }
.web-course-by-dot { margin: 0 3px; color: var(--ep-icon-faint); }

/* 인증 전용 레이아웃 보강 — 브랜드 패널 로고/foot, 데코 위 z-index, 반응형 */
@media (max-width: 860px) {
}

/* ── 메인 팝업 (M8) — 관리자 등록 팝업 노출 ──
   ⚠️ 모달이 아니다(2026-08-28 전환). 화면 전체를 덮던 어두운 오버레이를 걷어냈다 —
      뒤 내용을 못 읽게 만들고, 여러 개를 등록하면 전부 한가운데서 겹쳐
      뒤엣것을 닫을 수조차 없었다(실측). 지금은 페이지 위에 «떠 있는 창» 이다. */

/* 층(layer)은 자리만 잡고 클릭은 통과시킨다 — 뒤 페이지를 계속 쓸 수 있어야 한다.
   창 자신은 pointer-events 를 되살린다. */
.web-popup-layer { position: fixed; inset: 0; z-index: 900; pointer-events: none; }

.web-popup {
  position: absolute; pointer-events: auto;
  /* 좌표가 없을 때의 기본 자리 — 화면 «왼쪽 위» 에서 시작해 JS 가 계단식으로 어긋나게 놓는다.
     가운데에 두면 본문(카테고리·강의 카드)을 정면으로 가린다 — 안내를 보여주려다
     정작 보러 온 것을 못 보게 만든다(2026-08-28 실측). */
  top: 88px; left: 32px;
  width: min(400px, calc(100vw - 32px));
  max-height: calc(100vh - 140px);
  display: flex; flex-direction: column;
}
/* 팝업의 [hidden] 은 common.css 맨 위의 «[hidden]» 규칙이 처리한다 */

.web-popup-inner {
  background: #fff; border-radius: var(--ep-radius-card);
  border: 1px solid var(--ep-border);
  box-shadow: 0 18px 48px rgba(15, 23, 42, .28);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}

/* 제목 줄이 곧 손잡이다 */
.web-popup-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--ep-space-3);
  padding: 12px 12px 12px 16px; background: var(--ep-bg);
  border-bottom: 1px solid var(--ep-border-soft);
  cursor: grab; user-select: none; flex: 0 0 auto;
}
.web-popup-head:active { cursor: grabbing; }
.web-popup-title {
  font-size: var(--ep-text-xs); font-weight: 700; color: var(--ep-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 닫기 X — 40×40 최소 타격 면적(DESIGN_RULES §8) */
.web-popup-x {
  flex: 0 0 auto; width: 32px; height: 32px; display: inline-flex;
  align-items: center; justify-content: center;
  border: 0; background: none; border-radius: 8px; cursor: pointer;
  color: var(--ep-text-muted); position: relative;
}
.web-popup-x:hover { background: var(--ep-border-soft); color: var(--ep-text); }
.web-popup-x::after { content: ''; position: absolute; width: 40px; height: 40px;
  left: 50%; top: 50%; translate: -50% -50%; }

/* 본문은 넘치면 안에서 스크롤한다 — 세로로 긴 그림 하나가 창을 화면 밖으로 밀지 않게 */
.web-popup-body { overflow-y: auto; min-height: 0; flex: 1 1 auto; }
.web-popup-body img { width: 100%; height: auto; display: block; }
.web-popup-text { padding: 18px 20px; font-size: var(--ep-text-sm); line-height: 1.75; color: var(--ep-text-sub); white-space: pre-wrap; }
.web-popup-foot { display: flex; align-items: center; justify-content: space-between;
  gap: var(--ep-space-3); padding: 12px 16px; border-top: 1px solid var(--ep-border-soft);
  flex: 0 0 auto; background: #fff; }
.web-popup-foot .ep-check { font-size: var(--ep-text-xs); margin: 0; }

/* 좁은 화면에서는 좌표를 무시하고 아래에 붙인다 — 작은 화면에 절대 좌표를 그대로 쓰면
   창이 화면 밖으로 나가 열 수도 닫을 수도 없다. 드래그도 끈다(스크롤과 싸운다). */
@media (max-width: 640px) {
  .web-popup {
    top: auto !important; bottom: 12px; left: 12px !important; right: 12px;
    width: auto !important; max-height: 70vh;
  }
  .web-popup-head { cursor: default; }
}

/* 공지 한 줄 — 제목이 상세로 가는 링크다(§9.4). 링크 기본색을 끄고 행 전체를 누르게 */
.web-notice-row {
  display: flex; align-items: center; gap: var(--ep-space-2);
  padding: var(--ep-space-3) 0; border-bottom: 1px solid var(--ep-border-soft);
  color: inherit;
}
.web-notice-row.is-roomy { gap: var(--ep-space-3); padding: var(--ep-space-4) 0; }
.web-notice-row:hover { color: inherit; }
/* 제목만 색이 바뀐다 — 공용 클래스(.ep-*)를 여기서 선택자로 쓰면 CssScope 검사가
   'web 전용 정의' 로 오인하므로 구조 선택자로 짚는다(제목은 두 번째 span) */
.web-notice-row:hover > span:nth-child(2) { color: var(--ep-primary); }

/* ⚠️ 홈 상단 배너용 `.web-banner-*` 는 걷어냈다(2026-08-28).
   관리자 등록 배너는 **히어로 자리**가 담당한다(`.web-hero-image` 근처 주석 참조).
   히어로 위에 한 줄짜리 카드를 따로 두면, 운영자가 배너를 등록해도 큰 자리는 늘 같은 문구이고
   등록한 것은 위쪽에 작게 스쳐 지나간다 — 시안에 없는 자리이기도 했다.
   팝업(`.web-popup-*`)은 별개 기능이라 그대로 둔다. */

/* 공개 페이지 **바깥 래퍼**의 세로 리듬 — DESIGN_RULES §6.
   (.web-section 은 페이지 *안* 섹션 사이 간격 44px 이다. 이름을 섞지 말 것)
   상단 32 / 하단 56 이 기본이다. 화면마다 인라인으로 16·24·28·32·52·72 여섯 값이
   흩어져 있어, 메뉴를 옮길 때마다 콘텐츠가 시작하는 높이가 미묘하게 달라졌다.
   («정렬이 안 맞는 느낌» 의 원인 중 하나 — 한 화면 안이 아니라 화면 사이의 어긋남) */
.web-page { padding-top: var(--ep-space-8); padding-bottom: var(--ep-space-14); }
/* 폼 하나만 중앙에 서는 화면(로그인·가입·수료증 확인·에러) — 위아래를 같이 넉넉히 */
.web-page.is-roomy { padding-top: var(--ep-space-14); padding-bottom: var(--ep-space-14); }
/* 홈 배너처럼 헤더 바로 아래 붙는 자리 */
.web-page.is-tight { padding-top: var(--ep-space-4); padding-bottom: 0; }

/* 오류 화면의 큰 아이콘 — «빈 상태(.ep-empty)» 가 아니라 상태 표식이다(둘을 같은 클래스로 쓰면
   «다음에 할 일을 함께 쓴다» 는 빈 상태 규칙에 잘못 걸린다) */
.web-error-icon { padding: 8px 0 4px; }
.web-error-icon .msi { font-size: 44px; color: var(--ep-icon-faint); }

/* 커리큘럼 차시 — 접었다 펴는 형태(강좌를 «고르는» 화면이라 전부 펼치면 판단 재료가 묻힌다) */
.web-cur-lesson > summary { cursor: pointer; list-style: none; }
.web-cur-lesson > summary::-webkit-details-marker { display: none; }
.web-cur-lesson > summary::after {
  content: 'chevron_right'; font-family: 'Material Symbols Outlined';
  font-size: var(--ep-text-xl); color: var(--ep-icon-faint);
  transition: transform .15s; transform: rotate(90deg);
}
.web-cur-lesson[open] > summary::after { transform: rotate(-90deg); }
@media (prefers-reduced-motion: reduce) { .web-cur-lesson > summary::after { transition: none; } }
.web-cur-lesson > summary:focus-visible { outline: 2px solid var(--ep-primary); outline-offset: 2px; }

/* 이전·다음 글 — 좌우로 나눠 «어느 방향» 인지 위치로 말한다 */
.web-post-nav {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--ep-gutter);
  margin-top: var(--ep-space-4);
}
.web-post-nav > a, .web-post-nav > .is-empty {
  display: flex; flex-direction: column; gap: var(--ep-space-1); min-width: 0;
  padding: var(--ep-space-3) var(--ep-space-4);
  border: 1px solid var(--ep-border); border-radius: var(--ep-radius-input);
  background: var(--ep-surface);
}
.web-post-nav > a:hover { border-color: var(--ep-primary); }
.web-post-nav .is-next { text-align: right; align-items: flex-end; }
.web-post-nav > a > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
@media (max-width: 640px) { .web-post-nav { grid-template-columns: 1fr; } .web-post-nav .is-next { text-align: left; align-items: flex-start; } }
