@charset "utf-8";
/* ==========================================================================
   게시판 스킨 — portfolio
   --------------------------------------------------------------------------
   테마의 base.css / layout.css 가 먼저 로드되므로 여기서는 토큰만 가져다 쓴다.
   (색·간격 값을 직접 적지 않는다 — 메인과 게시판의 톤이 갈라지지 않게 하기 위함)

   클래스 접두어 pf- : 그누보드 기본 스킨들이 쓰는 이름(bo_, tbl_ 등)과
   충돌하지 않도록 분리했다. 기능상 필요한 그누보드 이름(fboardlist,
   bo_sch_wrap, btn_bo_sch 등)만 그대로 유지한다.
   ========================================================================== */


/* ==========================================================================
   1. 분류 내비게이션
   ========================================================================== */

.pf-cate {
  margin-bottom: var(--sp-6);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--color-border);
}

.pf-cate__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  list-style: none;
}

.pf-cate__list a {
  display: inline-block;
  padding-block: var(--sp-1);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-sub);
}

.pf-cate__list a:hover,
.pf-cate__list .bo_cate_on {
  color: var(--color-accent);
  text-decoration: none;
}


/* ==========================================================================
   2. 상단 도구 막대
   ========================================================================== */

.pf-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3) var(--sp-5);
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-6);
  border-bottom: 1px solid var(--color-border);
}

.pf-toolbar__count { color: var(--color-text-sub); }

/* 정렬 링크(.pf-sort)는 두지 않는다 — 이유는 list.skin.php 도구 막대 주석 참고 */

.pf-toolbar__btns {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin-left: auto;   /* 버튼은 항상 오른쪽 끝으로 */
}

/* 버튼 — 메인의 .btn 과 같은 각진 톤, 크기만 작다 */
.pf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--color-border);
  background: none;
  color: var(--color-text);
  font-size: var(--fs-xs);
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition:
    border-color     var(--dur-base) var(--ease-out),
    background-color var(--dur-base) var(--ease-out),
    color            var(--dur-base) var(--ease-out);
}

.pf-btn:hover {
  border-color: var(--c-ink);
  color: var(--color-text);
  text-decoration: none;
}

.pf-btn--solid {
  border-color: var(--color-accent);
  background-color: var(--color-accent);
  color: var(--c-paper);
}

.pf-btn--solid:hover {
  border-color: var(--color-accent-hover);
  background-color: var(--color-accent-hover);
  color: var(--c-paper);
}

/* 칸 안에 들어가는 작은 버튼 — 파일 이름 옆에 붙어도 줄 높이를 밀어내지 않게 */
.pf-btn--sm {
  min-height: 32px;
  padding: var(--sp-1) var(--sp-3);
}


/* ==========================================================================
   3. 카드 그리드
   --------------------------------------------------------------------------
   목록은 모든 항목의 무게가 같으므로 메인의 비대칭 구조를 쓰지 않고
   균등 그리드로 간다. 1 → 2 → 3열.
   auto-fill 대신 열 수를 못박는 이유: 항목 수가 적을 때(1~2개)
   카드가 화면 폭만큼 늘어나 우스꽝스러워지는 것을 막기 위해서다.
   ========================================================================== */

.pf-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  list-style: none;
}

@media (min-width: 768px) {
  .pf-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .pf-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pf-card {
  position: relative;   /* 체크박스를 카드 모서리에 얹기 위한 기준 */
  height: 100%;
}

/* 공지글은 테두리를 강조해 한눈에 구분 */
.pf-card-item.is-notice .pf-card__link {
  border-color: var(--color-accent);
}

.pf-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  color: var(--color-text);
  text-decoration: none;
  transition:
    border-color var(--dur-base) var(--ease-out),
    transform    var(--dur-slow) var(--ease-out),
    box-shadow   var(--dur-slow) var(--ease-out);
}

.pf-card__link:hover,
.pf-card__link:focus-visible {
  border-color: var(--c-ink);
  color: var(--color-text);
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: var(--shadow-card);
}

/* ---- 썸네일 ----
   상자 비율은 3:2 로 고정한다. 카드마다 이미지 비율이 다른데 상자까지
   따라 늘어나면 그리드의 가로줄이 어긋나 목록이 들쭉날쭉해 보인다.

   그 고정된 상자 안을 채우는 방식이 object-fit 인데, 두 선택지의 차이는 이렇다.

   - cover  : 상자를 꽉 채우고 넘치는 쪽을 잘라낸다. 카드가 균일해 보이는 대신
              가로가 긴 스크린샷은 양끝이, 세로가 긴 화면은 위아래가 사라진다.
              잘려 나가는 게 하필 헤더·사이드바처럼 레이아웃을 알아보게 하는
              부분이라, 작업물을 보여 주는 목적과 정면으로 부딪힌다.
   - contain: 전체가 들어오도록 축소하고 남는 자리는 여백으로 둔다.
              어떤 화면을 만들었는지 한눈에 보이는 대신 비율이 제각각이면
              여백 크기가 카드마다 달라 시각적으로 덜 정돈돼 보인다.

   포트폴리오에서는 정보가 남는 쪽이 맞다고 보고 contain 을 쓴다.
   여백이 사고처럼 보이지 않게 흰 매트를 깔아 "액자에 넣은 스크린샷"으로 읽히게 했다.
   (매트 색을 흰색으로 맞춘 이유는 base.css --color-thumb-matte 주석 참고) */
.pf-card__thumb {
  position: relative;
  display: flex;
  align-items: flex-end;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background-color: var(--color-thumb-matte);
}

.pf-card__thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* 이미지가 없을 때의 자리표시자 — 메인 작업 카드와 같은 줄무늬 */
.pf-card__thumb--empty {
  padding: 12px;
  background-color: var(--color-bg-sunken);
  background-image: repeating-linear-gradient(
    135deg,
    var(--c-stripe) 0 8px,
    var(--color-bg-sunken) 8px 16px
  );
}

.pf-card__caption {
  background-color: var(--c-paper);
  padding: 5px 9px;
  color: var(--color-text-sub);
}

/* ---- 본문 ---- */
.pf-card__body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px 20px 22px;
  flex: 1;   /* 카드 높이가 달라도 아래 메타가 바닥에 정렬되도록 */
}

.pf-card__index { color: var(--color-text-sub); }

.pf-card__title {
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
  word-break: keep-all;
}

.pf-card__flags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.pf-flag {
  padding: 3px 7px;
  border: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--fs-label-xs);
  letter-spacing: 0.08em;
  color: var(--color-text-sub);
}

/* NEW 뱃지(.pf-flag--new)는 두지 않는다 — 이유는 list.skin.php 상태 표시 주석 참고 */

/* ---- 카드 아래쪽 묶음 ----
   "바닥에 붙는 일"은 이 묶음 하나가 맡는다.
   예전에는 .pf-card__meta 에 margin-top:auto 를 걸었는데, 그러면 그 줄이
   비는 순간(역할 미입력) 정렬 기준이 통째로 사라졌다. 한 겹 감싸서
   안에 무엇이 들어 있든 — 태그만, 역할만, 둘 다 — 아래에 붙게 했다.

   세 값이 전부 비면 PHP 쪽에서 이 요소를 아예 그리지 않는다.
   그래도 카드 높이는 흐트러지지 않는다. .pf-cards 가 그리드라
   같은 줄의 항목은 기본값(stretch)으로 서로 높이를 맞추기 때문이다. */
.pf-card__foot {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin-top: auto;
}

.pf-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* 역할 줄. 위에 태그가 있을 때만 구분선을 긋는다 —
   묶음의 첫 줄이면 위쪽에 나눌 것이 없어 선이 허공에 뜬다. */
.pf-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  color: var(--color-text-sub);
}

.pf-card__tags + .pf-card__meta {
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-border);
}

/* 항목 사이에 가운뎃점을 찍는다. 첫 항목 앞에는 붙지 않아야 하므로
   ::before 를 두 번째 요소부터(+ 인접 형제 선택자) 건다.
   구분자를 마크업에 넣지 않고 CSS 로 그리는 이유: 스크린리더가
   "·"를 문장으로 읽지 않도록 하기 위해서다. */
.pf-card__meta > span + span::before {
  content: "·";
  margin-right: var(--sp-2);
  color: var(--color-border-strong);
}

/* ---- 관리자 선택 체크박스 ----
   흰 매트 위에 얹히므로 흰 배경에 묻히지 않도록 자체 판을 깔아 준다. */
.pf-card__chk {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: var(--z-base);
  display: flex;
  padding: 6px;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
}

.pf-card__chk input {
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--color-accent);
  cursor: pointer;
}


/* ==========================================================================
   4. 빈 목록 · 일괄 처리 · 페이지
   ========================================================================== */

.pf-empty {
  padding: var(--sp-9) var(--sp-4);
  border: 1px solid var(--color-border);
  text-align: center;
  color: var(--color-text-sub);
}

/* ---- 관리 도구 (선택 관리) ----
   기본값은 "예전 그대로 보이는" 상태다. 감추는 규칙은 전부 .pf-admin-js
   아래에만 적었다 — 이 클래스는 JS 가 붙이므로, 스크립트가 막히면
   체크박스도 버튼도 그대로 남아 관리 기능을 잃지 않는다. */

.pf-admin-js .pf-card__chk,
.pf-admin-js .pf-bulk {
  display: none;   /* visibility 나 opacity 가 아니라 display —
                      감춘 요소가 Tab 순서에 남지 않아야 한다 */
}

.pf-admin-js.is-admin .pf-card__chk { display: flex; }
.pf-admin-js.is-admin .pf-bulk      { display: flex; }

/* 관리 모드가 켜졌음을 카드 쪽에서도 알 수 있게 한다.
   체크박스 하나만 나타나면 지금이 무슨 상태인지 읽히지 않는다. */
.pf-admin-js.is-admin .pf-card__link { border-style: dashed; }

/* 열린 상태 표시. 강조색(버건디)은 쓰지 않는다 —
   같은 도구 막대에 이미 '글쓰기' 버튼이 강조색을 쥐고 있어서,
   여기에 한 번 더 쓰면 화면당 강조는 한 곳이라는 원칙이 깨진다.
   눌린 듯 눌러앉은 면으로만 상태를 나타낸다. */
.pf-admin-toggle[aria-expanded="true"] {
  border-color: var(--color-border-strong);
  background-color: var(--color-bg-sunken);
}

.pf-bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
  padding: var(--sp-4);
  border: 1px solid var(--color-border-strong);
  background-color: var(--color-bg-sunken);
}

.pf-bulk__all {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  cursor: pointer;
}

.pf-bulk__all input { accent-color: var(--color-accent); }

.pf-bulk__btns {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

/* 페이지 번호 — 그누보드가 .pg_wrap / .pg_page 구조로 출력한다 */
.pg_wrap {
  display: flex;
  justify-content: center;
  gap: var(--sp-1);
  margin-top: var(--sp-8);
}

.pg_page,
.pg_current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  min-height: 40px;
  padding-inline: var(--sp-2);
  border: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--color-text-sub);
}

.pg_page:hover {
  border-color: var(--c-ink);
  color: var(--color-text);
  text-decoration: none;
}

.pg_current {
  border-color: var(--color-accent);
  background-color: var(--color-accent);
  color: var(--c-paper);
}


/* ==========================================================================
   5. 검색 패널
   ========================================================================== */

.pf-search[hidden] { display: none; }

.pf-search {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: var(--sp-4);
}

.pf-search__dim {
  position: absolute;
  inset: 0;
  background-color: var(--color-overlay);
}

.pf-search__panel {
  position: relative;
  z-index: 1;
  width: min(30rem, 100%);
  padding: var(--sp-6);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  box-shadow: var(--shadow-lg);
}

.pf-search__panel form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.pf-search__panel h3 {
  width: 100%;
  color: var(--color-text-sub);
}

.pf-search__select,
.pf-search__input {
  min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  font-size: var(--fs-sm);
}

.pf-search__select { flex: 0 0 8rem; }
.pf-search__input  { flex: 1; min-width: 0; }


/* ==========================================================================
   6. 상세 화면
   ========================================================================== */

.pf-view { max-width: 52rem; }   /* 본문은 읽기 좋은 폭으로 제한 (약 832px) */

/* ---- 현재 위치 ---- */
.pf-crumb {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-6);
  color: var(--color-text-muted);
}

.pf-crumb a { color: var(--color-text-sub); }
.pf-crumb a:hover { color: var(--color-accent); text-decoration: none; }
.pf-crumb [aria-current] { color: var(--color-text); }

/* ---- 머리말 ---- */
.pf-view__head {
  padding-bottom: var(--sp-6);
  margin-bottom: var(--sp-7);
  border-bottom: 1px solid var(--color-border);
}

.pf-view__eyebrow { color: var(--color-text-sub); }

.pf-view__title {
  margin-top: var(--sp-3);
  font-size: var(--fs-h2);
  font-weight: var(--fw-regular);
  line-height: 1.24;
  letter-spacing: -0.03em;
  word-break: keep-all;
}

.pf-view__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: var(--sp-5);
}

/* 작업 메타 — 항목 수가 달라져도 무너지지 않게 auto-fit */
.pf-view__meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--color-border);
}

.pf-view__meta dt { margin-bottom: 6px; color: var(--color-text-sub); }

.pf-view__meta dd {
  font-size: var(--fs-h4);
  letter-spacing: var(--ls-heading);
}

.pf-view__meta dd .sv_member,
.pf-view__meta dd .sv_guest { color: inherit; }

/* ---- 첨부 이미지 ----
   올린 순서대로 세로로 쌓는다. 장수 제한이 없으므로 개수와 무관하게
   같은 규칙이 반복되는 형태여야 한다.

   목록·메인 카드와 달리 상자 비율을 고정하지 않는다. 저기는 카드끼리
   가로줄을 맞춰야 해서 3:2 로 묶었지만, 여기는 한 장씩 세로로 놓이므로
   원본 비율 그대로 두는 편이 화면을 있는 그대로 보여 준다. */
.pf-view__shots {
  /*
   * 두 값이 이 아래 규칙 전체를 움직인다. 화면 구간별로 여기만 바꾸면 된다.
   *
   * --shot-w  : 이미지 폭(%). 단위 없는 숫자인 것이 중요하다.
   *             아래 연결선 가로 길이를 구할 때 나눗셈이 필요한데
   *             calc() 는 퍼센트끼리 나누지 못하고 숫자끼리는 나눈다.
   * --shot-gap: 이미지 사이 여백이자 연결선 세로 구간의 높이.
   *             두 값이 같아야 선이 여백에 정확히 들어찬다.
   *             따로 적어 두면 선이 여백보다 길거나 짧아져 붕 뜬다.
   *
   * 기본값은 모바일 — 폭을 다 쓰고 엇갈림도 연결선도 없다.
   */
  --shot-w: 100;
  --shot-gap: var(--sp-5);

  display: flex;
  flex-direction: column;
  gap: var(--shot-gap);
  margin-bottom: var(--sp-7);
}

.pf-view__shot {
  position: relative;   /* 연결선(의사요소)의 기준 상자 */
  width: calc(var(--shot-w) * 1%);
  margin: 0;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-sunken);

  /* 호버 시 바뀌는 것은 색뿐이다. border-width 를 건드리면 이미지가
     1px 씩 밀려 레이아웃을 다시 잡는다. 색은 페인트만 한다. */
  transition: border-color var(--dur-base) var(--ease-out);
}

/*
 * 4면 강조.
 * :focus-within 을 함께 거는 이유 — 이미지가 <a class="view_image"> 로
 * 감싸여 있어 Tab 으로도 닿는다. 마우스에만 반응하면 키보드로 훑는 사람은
 * 지금 어느 이미지에 서 있는지 알 수 없다.
 */
.pf-view__shot:hover,
.pf-view__shot:focus-within { border-color: var(--color-accent); }

@media (prefers-reduced-motion: reduce) {
  .pf-view__shot { transition: none; }
}

/* ---- 첨부 이미지 : 좌우 엇갈림과 연결선 ----
   Chemistry 의 섹션 연결선 방식. 이미지를 좌우로 엇갈려 놓고, 실 한 가닥이
   각 이미지의 안쪽 옆면을 타고 내려가다 여백 한가운데에서 가로로 꺾여
   다음 이미지의 옆면으로 이어진다.
   첫 장은 자기 위 모서리에서 시작하고, 마지막 장은 꺾이지 않고 자기 아래
   모서리에서 끝맺는다 — 실의 양 끝이 허공에 뜨지 않게 하는 예외다.

   선을 테두리에 붙이지 않고 --trail-off 만큼 바깥으로 띄우는 이유:
   붙이면 호버할 때 테두리 전체가 같은 버건디로 바뀌면서 선이 테두리에
   흡수돼 사라진 것처럼 보인다. 연결선("다음으로 이어진다")과
   호버 강조("지금 여기")는 역할이 다른 장치라 화면에서도 구분돼야 한다.

   768px 부터만 켠다. 좁은 화면에서 이미지를 78% 로 줄이면 스크린샷을
   읽을 수 없고, 폭을 100% 로 되돌리면 엇갈림 자체가 성립하지 않는다.

   선의 개수와 모양은 선택자가 알아서 맞춘다.
     :not(:only-child)::before   세로선 — 1장뿐이면 탈 옆면이 없다
     :first-child::before        첫 장만 위로 올리지 않는다
     :last-child::before         마지막 장만 아래로 내리지 않는다
     :not(:last-child)::after    가로선 — 이어질 다음 장이 있을 때만

   길이는 JS 가 스크롤 위치를 보고 정한다(view.skin.php 의 인라인 스크립트).
   여기서는 "다 그려진 상태"의 모양만 잡는다.

   ※ .pf-view__shot 이나 .pf-view__shots 에 overflow:hidden 을 넣으면 안 된다.
     선이 이미지 바깥으로 나가므로 통째로 잘린다. */
@media (min-width: 768px) {

  .pf-view__shots {
    /* JS 가 넣는 SVG 연결선의 기준 상자. 좌표를 묶음 왼쪽 위에서부터
       재기 위해서다. 이 선언이 없으면 SVG 가 화면 전체를 기준으로 잡는다. */
    position: relative;

    --shot-w: 88;              /* 태블릿 : 엇갈림을 살짝만 */
    --shot-gap: var(--sp-9);   /* 80px — 세로로 꺾이는 구간이 보일 만큼 */

    /* 이미지 테두리 바깥면과 선 사이 간격.
       가로선 길이 계산에도 들어간다 — 양끝이 각각 이만큼 밖으로 나가 있다. */
    --trail-off: 12px;

    /* 실이 끝나는 자리에 긋는 마감 획의 길이.
       홀수로 두어야 1px 짜리 선 위에 정확히 가운데가 맞는다
       (선 한 칸 + 좌우 같은 수 = 홀수). */
    --trail-cap: 11px;
  }

  .pf-view__shot:nth-child(odd)  { align-self: flex-start; }
  .pf-view__shot:nth-child(even) { align-self: flex-end; }

  /* 1장뿐이면 엇갈릴 상대가 없다. 한쪽으로 쏠린 채 남지 않게 폭을 되돌린다. */
  .pf-view__shot:only-child {
    width: 100%;
    align-self: stretch;
  }

  /* ---- 이 아래는 "JS 가 없을 때의 정적 화면" 이다 ----

     스크롤에 따라 자라는 선은 CSS 가 아니라 JS 가 SVG 패스 한 줄로 그린다
     (view.skin.php). JS 가 그 선을 얹으면 묶음에 .has-trail 이 붙고, 그
     순간 아래 의사요소들은 사라진다. 둘이 겹쳐 보일 일은 없다.

     왜 움직이는 선을 CSS 로 그리지 않는가 — 재 보고 내린 결론이다.
     이 방식은 세로선과 가로선이 서로 다른 상자다. 브라우저는 상자마다 따로
     픽셀에 맞춰 그리므로 모서리에서 1px 이 어긋날 수 있다. 한 상자에 두
     테두리를 줘서 모서리를 상자 안에 넣어 봤지만(그러면 그 모서리는 확실히
     이어진다), 이번에는 위 이미지의 상자와 아래 이미지의 상자가 가로 구간에서
     만나는 자리가 남았다. 두 줄의 y 차이는

         3 × (1px − 실제로 그려진 테두리 두께)

     이다. 브라우저가 1px 테두리를 화면 배율에 맞춰 줄여 그리기 때문이다.
     크롬에서 잰 값:
         배율 100% : 테두리 1px     → 차이 0
         배율 125% : 테두리 0.8px   → 차이 0.6px
         배율 150% : 테두리 0.667px → 차이 1.0px
     CSS 에는 "실제로 그려진 테두리 두께"를 읽을 방법이 없어서 이 항을 상쇄할
     수 없다. 값을 어떻게 조정해도 어느 한 배율에서만 맞는다.
     그래서 움직이는 선은 꺾이는 자리가 전부 한 도형 안에 있는 SVG 로 옮겼다.

     정적인 이 규칙은 그대로 둔다. 상자를 움직이지 않으면 브라우저가 같은
     기준으로 맞춰 그려서, 배율 100·125·150% 에서 픽셀을 세어 봐도 모서리
     차이가 0 이다. 문제는 움직일 때만 생겼다. */

  .pf-view__shot:not(:only-child)::before,
  .pf-view__shot:not(:last-child)::after {
    content: "";
    position: absolute;

    /*
     * 호버 테두리와 같은 색을 쓴다(--color-accent = 버건디).
     * 선과 테두리는 "이 이미지에서 다음 이미지로 이어진다"는 같은 말을 하는
     * 장치다. 색이 다르면 서로 관계없는 두 장치로 읽힌다.
     * 토큰으로 참조하므로 강조색을 바꾸면 테두리와 선이 함께 따라온다.
     */
    background-color: var(--color-accent);
  }

  /* ---- 꺾이는 자리를 여백 한가운데로 ----
     선이 꺾이는 높이가 곧 다음 이미지의 위 테두리 높이면, 가로선이 그 테두리
     위에 그대로 얹힌다. 그 이미지에 호버하는 순간 테두리 전체가 같은 버건디로
     바뀌면서 가로선이 통째로 흡수된다 — 세로선을 바깥으로 띄워서 피한 것과
     똑같은 문제가 가로선에 남는 셈이다.

     그래서 여백(--shot-gap)의 절반 지점에서 꺾는다. 위 이미지가 끝나고 gap/2
     내려온 자리에서 가로로 건너가, 다음 이미지가 시작하기 gap/2 전에 닿는다.
     꺾이는 지점이 아무것도 없는 여백 한가운데라 어떤 테두리와도 겹치지 않는다.

     대신 세로선이 자기 위 모서리보다 gap/2 위에서 시작해야 앞 칸에서 건너온
     가로선과 만난다. 두 점의 y 가 같아서 선은 그대로 한 가닥으로 이어진다. */

  /*
   * 세로 구간 — 위 모서리 gap/2 위에서 아래 모서리 gap/2 아래까지.
   *
   * 길이를 height 로 주지 않고 top / bottom 두 모서리로 잡는다.
   * height 로 주면 아래 끝이 "top + height" 라는 덧셈의 결과가 된다.
   * 이미지 높이는 width:100% + height:auto 라 대개 소수점(예: 523.33px)이고,
   * 브라우저는 칠할 때 상자의 각 모서리를 따로 픽셀에 맞춘다. 그래서 더해서
   * 나온 아래 끝과, 가로선이 따로 계산한 y 가 1px 어긋날 수 있다.
   * (실제로 꺾이는 자리에서 세로선이 1px 더 내려온 것처럼 보였다)
   *
   * top 과 bottom 을 함께 주면 높이는 두 모서리 사이로 저절로 정해지고,
   * 아래 끝은 "패딩 박스 아래에서 1px + gap/2 만큼 밖" 이라는 위치 하나로
   * 계산된다. 가로선의 top(= 100% + 1px + gap/2) 과 같은 기준·같은 값이라
   * 둘이 어긋날 여지가 없다.
   *
   * -1px 이 붙는 이유:
   * 절대 위치 자식의 기준은 기준 상자의 패딩 박스다. 이미지 상자에는 위아래
   * 1px 씩 테두리가 있어, 패딩 박스의 위·아래 끝은 실제 바깥 모서리보다
   * 1px 안쪽이다. 그만큼 밖으로 밀어 줘야 모서리에서 시작하고 끝난다.
   */
  .pf-view__shot:not(:only-child)::before {
    top:    calc(-1px - var(--shot-gap) / 2);
    bottom: calc(-1px - var(--shot-gap) / 2);
    width: 1px;
  }

  /* 첫 장 위에는 건너온 가로선이 없다. 위로 올리지 않고 자기 위 모서리에서
     시작한다. (올려 두면 첫 이미지 위 허공에 40px 짜리 선 토막이 뜬다) */
  .pf-view__shot:first-child::before {
    top: -1px;
  }

  /* 마지막 장 아래에는 건너갈 곳이 없다. 아래로 내리지 않고 자기 아래
     모서리에서 끝맺는다. (내려 두면 마지막 이미지 아래 허공으로 40px 더
     뻗은 채 끝나 실이 끊긴 것처럼 보인다) */
  .pf-view__shot:last-child::before {
    bottom: -1px;
  }

  /*
   * 가로 구간 — 여백 한가운데에서 옆으로 꺾인다.
   *
   * 길이 계산. 폭이 78 일 때 위 이미지의 안쪽 끝은 x=78%, 아래 이미지의
   * 안쪽 끝은 x=100−78=22% 다. 가로로 가야 할 거리는 컨테이너의 56%,
   * 이미지 폭(78%) 기준으로는 56/78 = 71.8% 다.
   * 일반화하면 (2·shot_w − 100) / shot_w. 좌우가 대칭이라 홀수·짝수가
   * 같은 식을 쓴다.
   *
   * 비율을 100% 가 아니라 (100% + 2px) 에 곱하는 이유 — 이게 핵심이다.
   * 절대 위치 자식의 100% 는 기준 상자의 "패딩 박스" 폭이라 좌우 테두리 2px 이
   * 빠져 있다. 위 계산이 말하는 78% 는 테두리까지 포함한 바깥 폭이므로,
   * 2px 을 되돌린 뒤에 비율을 곱해야 한다. 그냥 100% 에 곱하면 결과가
   * (2·78−100)/78 × 2px = 1.44px 짧아진다.
   * (실제로 크롬에서 재 보니 가로선이 도착점 세로선에 1.437px 못 미쳤다.
   *  화면에서는 모서리가 이어지지 않고 어긋난 것처럼 보인다)
   *
   * 여기에 두 가지를 더한다.
   *   + 2 × --trail-off : 세로선을 양쪽 다 바깥으로 띄웠으므로 출발점은
   *                       바깥으로 off, 도착점도 반대쪽 바깥으로 off 만큼
   *                       더 나가 있다. 빼먹으면 가로선이 24px 못 미쳐 끊긴다.
   *   + 2px            : 양 끝의 세로선이 각각 1px 폭을 차지한다. 두 폭을
   *                       모두 덮어야 모서리가 한 덩어리로 이어진다.
   */
  .pf-view__shot:not(:last-child)::after {
    top: calc(100% + 1px + var(--shot-gap) / 2);
    height: 1px;
    width: calc(
      (2 * var(--shot-w) - 100) / var(--shot-w) * (100% + 2px)
      + 2 * var(--trail-off)
      + 2px
    );
  }

  /*
   * 선이 지나는 옆면.
   * 홀수(왼쪽 이미지)는 오른쪽 바깥, 짝수(오른쪽 이미지)는 왼쪽 바깥.
   *
   * 값이 calc(-2px - off) 인 이유:
   * right 는 패딩 박스 오른쪽 끝에서 선의 오른쪽 끝까지의 거리다.
   * 패딩 박스 끝은 테두리 바깥면보다 1px 안쪽이고 선 자체가 1px 폭이라,
   * -2px 이면 선의 왼쪽 면이 테두리 바깥면에 딱 붙는다(간격 0).
   * 거기서 off 만큼 더 밀어야 테두리와 선 사이가 정확히 off 가 된다.
   */
  .pf-view__shot:nth-child(odd)::before,
  .pf-view__shot:nth-child(odd)::after  { right: calc(-2px - var(--trail-off)); }

  .pf-view__shot:nth-child(even)::before,
  .pf-view__shot:nth-child(even)::after { left: calc(-2px - var(--trail-off)); }

  /* ---- 실의 끝 ----
     마지막 장 아래, 선이 끝나는 자리에 짧은 가로 획을 하나 긋는다.
     선이 그냥 뚝 끊기면 "여기가 끝"인지 "덜 그려진 것"인지 구분되지 않는다.

     점(원)이 아니라 획인 이유:
     이 화면에는 둥근 모서리가 한 군데도 없다. 격자 배경선·모노 라벨·각진
     버튼과 같은 계열로 두려면 제도 도면의 끝맺음 표시처럼 1px 획이 맞다.
     원을 하나 들이면 그것만 다른 계열이 된다.

     마지막 장의 ::after 는 비어 있다 — 가로 연결선은 :not(:last-child) 에만
     붙기 때문이다. 그래서 요소를 새로 만들지 않고 그 자리를 쓴다.
     :only-child 는 제외한다. 1장뿐이면 세로선 자체가 없어서 마감할 선이 없다.

     나타나는 시점은 JS 가 --draw-h 로 정한다. 마지막 장에서는 이어 갈 가로선이
     없으므로 그 값이 비어 있었는데, 이제 "세로선이 끝난 뒤 얼마나 지났는가"를
     담아 이 획을 키운다. 다른 칸의 꺾임과 같은 거리(CROSS)를 쓰므로
     화면을 훑는 리듬이 끝에서도 그대로 유지된다. */
  .pf-view__shot:last-child:not(:only-child)::after {
    content: "";
    position: absolute;
    top: calc(100% + 1px);        /* 세로선이 끝나는 바로 그 자리 */
    height: 1px;
    width: var(--trail-cap);
    background-color: var(--color-accent);
    transform-origin: center;     /* 선을 중심으로 양쪽으로 함께 자란다 */
  }

  /*
   * 획을 선 위에 가운데로 맞춘다.
   * 세로선은 폭이 1px 이라 그 중심은 선의 왼쪽 끝에서 0.5px 이다.
   * 획의 남는 길이 (--trail-cap − 1px) 를 좌우로 반씩 나눠 밀면
   * 두 중심이 맞는다. 11px 이면 좌우로 5px 씩이다.
   */
  .pf-view__shot:last-child:nth-child(odd)::after {
    right: calc(-2px - var(--trail-off) - (var(--trail-cap) - 1px) / 2);
  }

  .pf-view__shot:last-child:nth-child(even)::after {
    left: calc(-2px - var(--trail-off) - (var(--trail-cap) - 1px) / 2);
  }

  /* ---- 움직이는 선은 SVG 가 그린다 ----

     JS 가 묶음 안에 <svg class="pf-view__trail"> 을 만들어 넣고 묶음에
     .has-trail 을 붙인다. 그때부터 위 의사요소 선은 필요 없다 — 같은 자리에
     두 겹으로 그려지므로 지운다. content:none 이면 상자가 아예 안 생긴다.

     되돌리기도 같은 자리에서 된다. 창을 768px 아래로 줄이거나 모션 최소화가
     켜지면 JS 가 SVG 와 이 클래스를 함께 걷어내고, 그 순간 위의 정적 선이
     그대로 돌아온다. 그래서 CSS 쪽에 모션 최소화 예외를 따로 두지 않는다. */
  .pf-view__shots.has-trail .pf-view__shot::before,
  .pf-view__shots.has-trail .pf-view__shot::after {
    content: none;
  }

  /*
   * SVG 는 묶음 전체를 덮는다. 선의 좌표는 JS 가 실제 상자를 재서 넣으므로
   * 여기서는 자리와 색만 정한다.
   *
   * viewBox 를 두지 않는 이유:
   * viewBox 가 있으면 그 안의 좌표가 화면 크기에 맞춰 늘어나고 선 굵기까지
   * 같이 늘어난다. 없으면 좌표 1 = CSS 1px 이라 JS 가 잰 값을 그대로 쓴다.
   *
   * overflow: visible — 마감 획이 묶음 아래 모서리 밖으로 조금 나간다.
   * shape-rendering: crispEdges — 가로·세로뿐인 선이라 안티앨리어싱을 끄면
   *   어느 배율에서나 흐릿하지 않은 1px 로 떨어진다.
   * inset 대신 네 방향을 따로 적는 것은 구형 사파리 때문이다.
   */
  .pf-view__trail {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    overflow: visible;
    pointer-events: none;

    fill: none;
    stroke: var(--color-accent);
    stroke-width: 1;
    stroke-linecap: butt;
    shape-rendering: crispEdges;
  }
}

@media (min-width: 1024px) {
  .pf-view__shots { --shot-w: 78; }   /* 데스크톱 : 스크린샷 판독성을 지키는 선 */
}

/* 이미지를 감싸는 <a class="view_image"> 가 인라인이라, 그대로 두면
   글자 기준선(baseline) 아래 여백이 생겨 테두리와 사이가 벌어진다.
   block 으로 바꿔 그 여백을 없앤다. */
.pf-view__shot a,
.pf-view__shot img {
  display: block;
}

.pf-view__shot img {
  width: 100%;
  height: auto;
}

/* 캡션은 이미지 아래. 시안의 소형 모노 라벨 톤을 따르되 대문자 변환은 하지 않는다
   — 첨부 설명은 한글 문장으로 들어오는 값이라 라벨이 아니라 읽는 글에 가깝다. */
.pf-view__shot-cap {
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  color: var(--color-text-sub);
  text-transform: none;
  letter-spacing: normal;
  font-family: inherit;
  font-size: var(--fs-xs);
  line-height: var(--lh-base);
  word-break: keep-all;
}

/* ---- 본문 ----
   시안 구조: 왼쪽에 모노스페이스 소형 라벨(소제목), 오른쪽에 내용이 오는 2단.
   섹션 머리말(라벨 → 큰 문장 → 콘텐츠)과 같은 리듬을 본문 안에서도 유지한다.

   에디터가 만드는 본문은 h2 와 내용이 형제로 평평하게 들어오므로,
   PHP(pf_prose_sections)가 h2 경계마다 감싸개를 끼워 아래 구조로 바꿔 준다.

     .pf-prose            묶음 전체 (세로 흐름)
       .pf-prose__lead    첫 소제목 앞의 내용 — 있을 때만
       .pf-prose__row     소제목 한 덩어리 = 2단 그리드
         > h2             왼쪽 라벨
         .pf-prose__body  오른쪽 내용
           --qa           트러블슈팅 (h3 가 들어 있는 덩어리)

   소제목이 없거나 잘라 내기 안전하지 않으면 .pf-prose--flat 으로 떨어진다.
   그 경우의 모양은 이 절 끝에 따로 잡아 두었다. */
.pf-view__body { margin-bottom: var(--sp-8); }

/* ---- 본문 : 묶음 ---- */
.pf-prose {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);   /* 모바일에서는 라벨이 내용 위로 접히므로,
                         덩어리 사이가 시안(22px)보다 넉넉해야 경계가 읽힌다 */
}

@media (min-width: 768px) {
  .pf-prose { gap: 22px; }   /* 시안 값 — 2단에서는 왼쪽 라벨 열이 경계를 만들어 준다 */
}

/* ---- 본문 : 한 덩어리(소제목 + 내용) ---- */
.pf-prose__row {
  display: grid;
  grid-template-columns: 1fr;   /* 모바일 : 라벨이 내용 위로 */
  gap: var(--sp-2);
}

@media (min-width: 768px) {
  .pf-prose__row {
    grid-template-columns: 150px 1fr;   /* 시안 값 */
    gap: 30px;                          /* 시안 값 (열 간격) */
  }
}

/* 왼쪽 라벨. 에디터가 만든 h2 라 클래스를 붙일 수 없어 자식 선택자로 잡는다.
   padding-top 6px 은 시안 값 — 라벨 글자가 작아서 그냥 두면 오른쪽 본문
   첫 줄보다 위로 떠 보인다. 두 글줄의 시각적 높이를 맞추는 값이다. */
.pf-prose__row > h2 {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-sub);
}

@media (min-width: 768px) {
  .pf-prose__row > h2 { padding-top: 6px; }
}


/* ---- 본문 : 글이 흐르는 자리 ----
   세 이름을 함께 묶는다. 2단의 오른쪽 칸, 첫 소제목 앞의 도입부,
   그리고 2단으로 나누지 못했을 때의 원본 — 안쪽 글의 모양은 모두 같아야 한다. */
.pf-prose__body,
.pf-prose__lead,
.pf-prose--flat {
  font-size: var(--fs-body);
  line-height: var(--lh-relaxed);
}

.pf-prose__body > * + *,
.pf-prose__lead > * + *,
.pf-prose--flat > * + * { margin-top: var(--sp-4); }

/* ---- 본문 : 트러블슈팅 (문제 → 원인·해결) ----
   여러 건을 나열해도 구조가 유지되도록, 왼쪽 세로선 하나로 묶고
   각 건은 "문제 한 줄(h3) + 설명(p)" 짝으로 반복한다.
   h3 가 있는 덩어리에만 PHP 가 --qa 를 붙여 준다(_fields.php). */
.pf-prose__body--qa {
  padding-left: 22px;                            /* 시안 값 */
  border-left: 1px solid var(--color-border);

  /* 크기·색을 묶음 전체에 걸어 둔다. 설명 문단이 한 건에 몇 개가 오든
     같은 모양이 되고, 아래에서 h3 만 색을 되돌리면 된다.
     (h3 + p 처럼 인접 선택자로 잡으면 둘째 문단부터 어긋난다) */
  font-size: var(--fs-sm);                       /* 시안 값 15px */
  color: var(--color-text-sub);
}

/* 문제 한 줄. 시안은 굵기를 올리지 않고 색(버건디)으로만 세우는데,
   여기서는 실제 h3 라 브라우저 기본 굵기(bold)가 걸린다.
   본문보다 무거워 보이지 않도록 medium(500)까지만 내렸다. */
.pf-prose__body--qa h3 {
  margin: 0 0 var(--sp-1);                       /* 시안 값 4px */
  font-size: inherit;
  font-weight: var(--fw-medium);
  line-height: var(--lh-base);
  color: var(--color-accent);
}

/* 문제 바로 아래 설명은 위 margin 4px 이 이미 만들었으므로 기본 간격을 지운다 */
.pf-prose__body--qa h3 + * { margin-top: 0; }

/* 건과 건 사이. 앞 건의 설명이 끝나고 다음 문제가 시작되는 자리다.
   위 `> * + *` 의 기본 간격(16px)을 시안 값(18px)으로 덮는다. */
.pf-prose__body--qa > * + h3 { margin-top: 18px; }

/* ---- 본문 : 2단으로 나누지 못했을 때 ----
   소제목이 없거나 h2 가 최상위 블록이 아니어서 원본을 그대로 쓰는 경우.
   2단 이전의 모양(가로로 놓인 소제목)을 그대로 남겨 둔다.

   display 를 되돌리는 게 먼저다. 이 요소는 .pf-prose 와 --flat 을 함께 달고
   있어서, 위에서 준 flex + gap 이 그대로 걸리면 문단 사이가 32px 씩 벌어진다. */
.pf-prose--flat {
  display: block;
}

.pf-prose--flat h2 {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-h3);
  font-weight: var(--fw-medium);
}

.pf-prose--flat h3 {
  margin-top: var(--sp-6);
  font-size: var(--fs-h4);
  font-weight: var(--fw-medium);
}

.pf-prose ul,
.pf-prose ol {
  padding-left: 1.2em;
  list-style: revert;   /* base.css 의 목록 초기화를 본문 안에서만 되살린다 */
}

.pf-prose li + li { margin-top: var(--sp-2); }

.pf-prose img { max-width: 100%; height: auto; }

.pf-prose a { text-decoration: underline; text-underline-offset: 3px; }

.pf-prose blockquote {
  padding-left: var(--sp-4);
  border-left: 2px solid var(--color-accent);
  color: var(--color-text-sub);
}

.pf-prose code {
  padding: 2px 5px;
  background-color: var(--color-bg-sunken);
  font-family: var(--font-mono);
  font-size: 0.9em;
}

.pf-view__signature {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-sm);
  color: var(--color-text-sub);
}

/* ---- 링크 · 첨부 · 관련링크 ---- */
.pf-view__links,
.pf-view__files {
  padding-top: var(--sp-5);
  margin-bottom: var(--sp-6);
  border-top: 1px solid var(--color-border);
}

.pf-view__links h2,
.pf-view__files h2 {
  margin-bottom: var(--sp-4);
  color: var(--color-text-sub);
}

.pf-view__links-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.pf-view__files ul { list-style: none; }

.pf-view__files li + li {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-border);
}

.pf-view__files a { color: var(--color-text); }
.pf-view__files a:hover { color: var(--color-accent); text-decoration: none; }

.pf-view__filesize { color: var(--color-text-sub); font-size: var(--fs-xs); }

.pf-view__filemeta {
  display: block;
  margin-top: 4px;
  color: var(--color-text-muted);
}

.pf-view__act {
  display: flex;
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}

/* ---- 이전 / 다음 글 ---- */
.pf-view__nb {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;                                  /* 1px 간격 + 카드 테두리로 구분선 효과 */
  margin-block: var(--sp-8) var(--sp-6);
  background-color: var(--color-border);     /* gap 사이로 비쳐 선이 된다 */
  border: 1px solid var(--color-border);
}

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

  /* 첫 글·마지막 글이라 한쪽만 있을 때는 한 칸으로 둔다.
     2단을 유지하면 빈 칸에 테두리색 배경이 그대로 드러나 덩어리처럼 보인다. */
  .pf-view__nb--single { grid-template-columns: 1fr; }
}

.pf-view__nb-item {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
  background-color: var(--color-bg-raised);
  color: var(--color-text);
  transition: background-color var(--dur-base) var(--ease-out);
}

.pf-view__nb-item:hover {
  background-color: var(--color-bg-sunken);
  color: var(--color-accent);
  text-decoration: none;
}

.pf-view__nb-item--next { text-align: right; }

.pf-view__nb-title {
  font-size: var(--fs-sm);
  line-height: var(--lh-heading);
  word-break: keep-all;
}

/* 한 줄 설명.
   두 줄까지만 보이고 넘치면 말줄임 — 앞뒤 글의 본문 길이가 제각각이라
   그대로 두면 카드 높이가 크게 어긋난다.
   -webkit-box 계열은 표준이 아니지만 줄 수 기준 말줄임을 지원하는 유일한 방법이고,
   지원하지 않는 브라우저에서는 잘리지 않고 전부 보일 뿐이라 안전하게 실패한다. */
.pf-view__nb-desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  font-size: var(--fs-xs);
  line-height: var(--lh-base);
  color: var(--color-text-sub);
  word-break: keep-all;
}

/* 마우스를 올렸을 때 제목만 강조색으로 바뀌고 설명은 보조색을 유지한다.
   설명까지 같이 물들면 두 줄의 위계가 사라진다. */
.pf-view__nb-item:hover .pf-view__nb-desc { color: var(--color-text-sub); }

/* ---- 하단 버튼 ---- */
.pf-view__btns {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--color-border);
}

.pf-view__btns-right {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-left: auto;
}


/* ==========================================================================
   7. 쓰기 화면
   --------------------------------------------------------------------------
   상세(.pf-view)와 같은 폭·같은 리듬을 쓴다.
   "라벨 → 입력칸 → 도움말" 한 덩어리(.pf-field)를 만들어 두고
   묶음(.pf-fieldset)만 쌓는 구조라, 나중에 칸이 늘어도 마크업이 붙기만 한다.

   그누보드 기본 클래스(frm_input · btn_submit · write_div …)는 쓰지 않는다.
   그 클래스들의 CSS 는 css/default.css 에 있는데 이 테마는 그 파일을 싣지 않아
   이름만 남고 모양이 없다. 여기서는 전부 .pf-* 로 다시 정의한다.
   ========================================================================== */

.pf-write { max-width: 52rem; }   /* 상세 화면과 같은 폭 */

.pf-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  margin-top: var(--sp-5);
}

/* ---- 7-1. 입력 묶음 ---- */
.pf-fieldset {
  padding: var(--sp-5);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
}

/* 작업 정보처럼 한 단계 눌러 보여야 하는 묶음 */
.pf-fieldset--sunken { background-color: var(--color-bg-sunken); }

/*
 * legend 는 fieldset 의 직계 자식이지만 그리드에는 넣지 않는다.
 * fieldset 에 display:grid 를 걸면 legend 를 그리드 항목으로 볼지 말지가
 * 브라우저마다 달라 열 배치가 흔들린다. 그래서 안쪽 div 를 그리드로 쓴다.
 */
.pf-fieldset__legend {
  padding: 0;
  margin-bottom: var(--sp-4);
  color: var(--color-text-sub);
}

.pf-fieldset__body {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
}

@media (min-width: 768px) {
  .pf-fieldset { padding: var(--sp-6); }
  .pf-fieldset__body--2col { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* 한 줄을 통째로 쓰는 항목 (제목 · 본문 · 옵션 · 첨부) */
  .pf-field--full { grid-column: 1 / -1; }
}

/* ---- 7-2. 한 칸 ---- */
.pf-field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-width: 0;   /* 그리드 칸 안에서 긴 입력값이 칸을 밀어내지 않게 */
}

.pf-field__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--color-text-sub);
}

/* 필수 표시 — 강조색을 아주 좁은 면적으로만 쓴다 */
.pf-field__req {
  margin-left: var(--sp-1);
  color: var(--color-accent);
  font-size: var(--fs-label-xs);
}

.pf-field__help {
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--color-text-sub);
}

/* ---- 여러 파일 한 번에 선택 ----
   아래 파일 칸들 위에 놓이는 묶음. 개별 칸을 대체하는 게 아니라
   한 번에 채워 주는 도구라, 칸들과 구분되도록 면을 깔아 세운다. */
.pf-file-multi {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--color-border-strong);
  background-color: var(--color-bg-sunken);
}

.pf-file-multi__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}

.pf-file-multi__row .pf-file { flex: 1 1 240px; }

/* 이번에 고른 파일 이름 + 빼기 버튼.
   JS 가 파일을 담을 때 hidden 을 푼다.

   [hidden] 을 따로 적어 주는 이유:
   브라우저 기본 스타일시트의 [hidden] { display: none } 은 우선순위가 가장
   낮아서, 이렇게 display 를 지정한 규칙이 그걸 이겨 버린다. 되돌려 주지 않으면
   hidden 을 걸어도 칸마다 빈 줄이 하나씩 남는다. */
.pf-file-row__picked {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  grid-column: 1 / -1;
}

.pf-file-row__picked[hidden] { display: none; }

.pf-file-row__picked-name {
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: var(--ls-label);
  color: var(--color-accent-alt);
  word-break: break-all;
}

/* 본문 작성 규칙 안내. 여러 줄짜리라 한 줄짜리 도움말과 달리 묶음으로 세운다.
   눌린 톤의 면을 깔아 "입력칸에 딸린 설명"이 아니라 "읽고 넘어갈 안내"로 보이게 한다. */
.pf-write-guide {
  padding: var(--sp-3) var(--sp-4);
  border-left: 2px solid var(--color-border-strong);
  background-color: var(--color-bg-sunken);
}

.pf-write-guide p + p { margin-top: var(--sp-2); }

.pf-write-guide strong {
  font-weight: var(--fw-medium);
  color: var(--color-text);
}

.pf-field__count {
  align-self: flex-end;
  color: var(--color-text-sub);
}

/* ---- 7-3. 입력 요소 공통 ----
   .pf-editor textarea 를 함께 묶는 이유: 그 textarea 는 에디터 플러그인이
   만들어 내서 클래스를 붙일 수 없다. 래퍼 기준 자손 선택자로만 잡힌다. */
.pf-input,
.pf-select,
.pf-file,
.pf-editor textarea {
  width: 100%;
  min-height: 44px;                    /* 터치 목표 최소 크기 */
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  color: var(--color-text);
  font-size: var(--fs-sm);
  transition: border-color var(--dur-base) var(--ease-out);
}

.pf-input:focus-visible,
.pf-select:focus-visible,
.pf-file:focus-visible,
.pf-editor textarea:focus-visible { border-color: var(--color-accent); }

.pf-input::placeholder { color: var(--color-text-muted); }

/* 파일 선택 버튼 — 브라우저 기본 버튼을 카드 톤에 맞춘다.
   ::file-selector-button 은 표준, ::-webkit-file-upload-button 은 구형 사파리용. */
.pf-file { padding: var(--sp-2); cursor: pointer; }

.pf-file::file-selector-button,
.pf-file::-webkit-file-upload-button {
  margin-right: var(--sp-3);
  padding: 6px var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-sunken);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: var(--ls-label);
  cursor: pointer;
}

/*
 * 본문 textarea.
 * 플러그인이 style="width:100%;height:300px" 를 인라인으로 박아 넣는다
 * (editor.lib.php:53). 인라인 스타일이 이깁니다 — 높이는 !important 로만
 * 덮을 수 있는데, 300px 는 쓰기에 무난하므로 그대로 두고 나머지만 맞춘다.
 * base.css 의 textarea { resize: vertical } 덕분에 필요하면 사용자가 늘릴 수 있다.
 */
.pf-editor textarea {
  min-height: 300px;
  line-height: var(--lh-base);
}

/* ---- 7-3-1. 에디터 단축키 안내 (cke_sc 계열) ----
   스마트에디터2 가 에디터 위에 넣는 "단축키 일람" 버튼과, 그 버튼이 눌렸을 때
   불러오는 단축키 표다. 숨김용 텍스트가 아니라 실제로 동작하는 컨트롤이다.
   (editor.lib.php:26 이 document.write 로 버튼을 쓰고, 33~47 의 스크립트가
    클릭 시 shortcut.html 을 .cke_sc_def 안으로 불러온다)

   여기서 규칙을 잡아 주는 이유.
   원래 모양은 그누보드 기본 CSS(css/default.css:247~254)에 있는데 이 테마는
   그 파일을 싣지 않는다. 게다가 base.css 의 button 리셋(padding·border·background
   전부 0)까지 걸려서, 규칙이 없으면 버튼이 아니라 맨 텍스트로 보인다.
   .sound_only 를 base.css 에 정의해 준 것과 같은 성격의 대응이다.

   네 개를 한꺼번에 잡는다. 버튼만 고치면 눌렀을 때 스타일 없는 표와
   "닫기" 텍스트가 다시 튀어나온다.

   .pf-editor 로 스코프를 거는 이유: 이 이름들은 에디터 플러그인 것이라
   우리 이름 규칙(pf-) 밖에 있다. 전역에 두면 나중에 같은 이름이 다른 데서
   나왔을 때 여기 규칙이 조용히 따라간다. */

.pf-editor .cke_sc {
  margin-bottom: var(--sp-2);
  text-align: right;   /* 본문 입력칸 오른쪽 위 모서리 — 기본 CSS 와 같은 자리 */
}

/* 여는 버튼과 닫는 버튼을 한 규칙으로 묶는다. 같은 기능의 짝이라
   생김새가 갈리면 관계가 읽히지 않고, 나중에 한쪽만 고쳐 어긋나기도 쉽다.
   .pf-btn 과 같은 각진 톤이고 보조 컨트롤이라 크기만 한 단계 작다. */
.pf-editor .btn_cke_sc,
.pf-editor .btn_cke_sc_close {
  padding: 5px var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  color: var(--color-text-sub);
  font-family: var(--font-mono);
  font-size: var(--fs-label-xs);
  letter-spacing: var(--ls-label);
  cursor: pointer;
  transition:
    border-color var(--dur-base) var(--ease-out),
    color        var(--dur-base) var(--ease-out);
}

.pf-editor .btn_cke_sc:hover,
.pf-editor .btn_cke_sc_close:hover {
  border-color: var(--c-ink);
  color: var(--color-text);
}

/* 펼쳐지는 단축키 표. 강조색은 쓰지 않는다 —
   같은 화면에서 강조색은 하단 '작성완료' 버튼이 쥐고 있다. */
.pf-editor .cke_sc_def {
  margin-bottom: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-sunken);
}

/* shortcut.html 은 <dl> 안에 dt(단축키)·dd(설명)가 번갈아 온다.
   기본 CSS 는 float 20%/30% 로 4단을 만드는데, 여기서는 Grid 로 짠다.
   모바일은 "키 / 설명" 한 쌍씩 1줄, 태블릿부터 두 쌍씩 1줄.
   max-content 로 키 칸을 잡으면 CTRL+SHIFT+TAB 같은 긴 키도 줄바꿈되지 않는다. */
.pf-editor .cke_sc_def dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--sp-2) var(--sp-4);
  margin: 0 0 var(--sp-4);
  text-align: left;
}

@media (min-width: 768px) {
  .pf-editor .cke_sc_def dl {
    grid-template-columns: repeat(2, max-content 1fr);
  }
}

.pf-editor .cke_sc_def dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label-xs);
  letter-spacing: var(--ls-label);
  color: var(--color-text-sub);
}

.pf-editor .cke_sc_def dd {
  margin: 0;
  font-size: var(--fs-xs);
}

/* ---- 7-4. 체크박스 옵션 ---- */
.pf-checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  list-style: none;
}

.pf-check {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}

.pf-check input[type="checkbox"] {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--color-accent);   /* 기본 체크박스 색만 바꾼다 (커스텀 그리기 안 함) */
  cursor: pointer;
}

.pf-check label {
  font-size: var(--fs-sm);
  line-height: 1.5;
  cursor: pointer;
}

.pf-check__help {
  display: block;
  font-size: var(--fs-xs);
  color: var(--color-text-sub);
}

/* ---- 7-5. 임시 저장 ---- */
.pf-autosave { position: relative; }

.pf-autosave__btn {
  align-self: flex-start;
  margin-top: var(--sp-2);
  min-height: 34px;
  padding: var(--sp-1) var(--sp-3);
}

/*
 * 임시저장 목록 패널.
 * display:none 으로 감춰야 한다 — js/autosave.js 가 .show() / .hide() 와
 * :hidden 으로 여닫기 때문에, hidden 속성이나 visibility 로 바꾸면 토글이 죽는다.
 */
.pf-autosave__pop {
  display: none;
  position: absolute;
  right: 0;
  z-index: var(--z-base);
  width: min(22rem, 100%);
  margin-top: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--color-border-strong);
  background-color: var(--color-bg-raised);
  box-shadow: var(--shadow-card);
}

.pf-autosave__pop ul {
  list-style: none;
  margin-top: var(--sp-3);
  font-size: var(--fs-xs);
}

.pf-autosave__pop li + li {
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--color-border);
}

.pf-autosave__pop-foot {
  margin-top: var(--sp-3);
  text-align: right;
}

/* ---- 7-6. 첨부파일 한 줄 ---- */
.pf-file-row {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px dashed var(--color-border-strong);
}

@media (min-width: 768px) {
  .pf-file-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 이미 올라가 있는 파일 안내 + 삭제 체크 */
.pf-file-row__old {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  grid-column: 1 / -1;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-xs);
}

.pf-file-row__name { color: var(--color-text-sub); word-break: break-all; }

/* ---- 담긴 칸만 남기기 ----
   JS 가 .is-managed 를 붙였을 때만 도는 규칙이다. 붙지 않으면(스크립트가 막힌
   환경) 20칸이 그대로 보이고, 칸을 하나씩 쓰는 예전 방식이 그대로 남는다.

   감추는 수단이 display:none 인 것이 중요하다. 칸은 문서에 그대로 있고 폼과
   함께 전송되므로 bf_file[] · bf_content[] 의 인덱스가 밀리지 않는다.
   (전송에서 빠지려면 disabled 여야 하는데, 걸지 않는다) */
.pf-fieldset.is-managed .pf-file-row { display: none; }

/* 남기는 칸은 두 가지 — 이번에 파일을 담은 칸, 저장된 파일이 있는 칸 */
.pf-fieldset.is-managed .pf-file-row.is-picked,
.pf-fieldset.is-managed .pf-file-row[data-has-old] { display: grid; }

/* 이미 담은 칸에서는 파일 입력칸을 감춘다. 파일은 위에서 한 번에 고르고
   빼기 버튼으로 뺀다. 같은 일을 하는 입력칸이 칸마다 또 있으면
   "어느 쪽으로 골라야 하나"가 생긴다.
   저장된 파일이 있는 칸에서는 남긴다 — bf_no 를 그대로 두고 다른 파일로
   바꾸는(교체) 유일한 수단이라 대체할 방법이 없다. */
.pf-fieldset.is-managed .pf-file-row.is-picked .pf-file-row__pick { display: none; }

@media (min-width: 768px) {
  /* 파일 입력칸이 빠지면 설명 칸이 두 칸 중 왼쪽 하나만 쓴다. 줄을 다 쓰게 넓힌다. */
  .pf-fieldset.is-managed .pf-file-row.is-picked .pf-file-row__desc { grid-column: 1 / -1; }
}

/* ---- 7-7. 자동등록방지 ----
   캡차는 plugin/kcaptcha 가 만든 마크업이라 클래스를 붙일 수 없다.
   (관리자로 로그인해 쓰는 동안에는 아예 나오지 않고, 비회원 글쓰기를 열 때만 보인다)
   래퍼 기준 자손 선택자로 톤만 맞춘다. */
.pf-captcha #captcha {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.pf-captcha legend { color: var(--color-text-sub); font-size: var(--fs-xs); }

.pf-captcha img { max-width: 100%; height: auto; vertical-align: middle; }

.pf-captcha input[type="text"] {
  width: 7rem;
  min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-raised);
  font-size: var(--fs-sm);
}

/*
 * 음성듣기 · 새로고침 버튼.
 * 플러그인이 <button><span></span>텍스트</button> 형태로 내보내는데,
 * 그 빈 span 은 그누보드 기본 CSS 가 아이콘 스프라이트를 넣는 자리다.
 * 이 테마는 그 CSS 를 싣지 않으므로 span 을 접고 글자만 남긴다.
 */
.pf-captcha button {
  min-height: 36px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--color-border);
  background-color: var(--color-bg-sunken);
  font-family: var(--font-mono);
  font-size: var(--fs-label-sm);
  letter-spacing: var(--ls-label);
}

.pf-captcha button > span { display: none; }

.pf-captcha #captcha_info {
  flex-basis: 100%;
  font-size: var(--fs-xs);
  color: var(--color-text-sub);
}

/* ---- 7-8. 하단 버튼 ----
   상세 화면의 .pf-view__btns 와 같은 구성: 취소는 왼쪽, 제출은 오른쪽. */
.pf-form__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--color-border);
}

/* 제출 버튼만 조금 더 크게 — 화면에서 가장 중요한 동작 */
.pf-form__actions .pf-btn--solid {
  min-height: 48px;
  padding-inline: var(--sp-6);
}

/* 전송 중에는 fwrite_submit() 이 disabled 를 건다. 눌린 상태를 눈으로 알 수 있게. */
.pf-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
