/* ═══════════════════════════════════════════════
   /fe — 프론트엔드 직무용 포트폴리오
   인터랙션 스크립트 gzip 2.5KB(외부 라이브러리 0) · 인쇄(A4) 겸용
   ═══════════════════════════════════════════════ */

/* ── 폰트 ───────────────────────────────────── */
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('./fonts/Pretendard-Regular.subset.woff2') format('woff2');
}
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('./fonts/Pretendard-Bold.subset.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0020-007E;
  src: url('./fonts/JetBrainsMono-Regular.subset.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+0020-007E;
  src: url('./fonts/JetBrainsMono-SemiBold.subset.woff2') format('woff2');
}
/* 폴백 메트릭 정렬 — Pretendard 실측 기준 */
@font-face {
  font-family: 'Pretendard Fallback';
  src: local('Apple SD Gothic Neo'), local('Malgun Gothic'), local('Noto Sans KR');
  size-adjust: 100%;
  ascent-override: 95.2%;
  descent-override: 24.1%;
  line-gap-override: 0%;
}

/* ── 토큰 — 코어 6 + 파생 2 ─────────────────── */
:root {
  --bg:#FDFCF8;  --bg-elev:#F8E7C9;  --ink:#0E1512;
  --muted:#6F6752;  --line:#E4D3B0;  --accent:#064E3B;
  --ink-soft:#1F2622;  --line-soft:#F0E4D0;

  --font-ko: 'Pretendard', 'Pretendard Fallback', 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --max-w: 880px;
  --pad-x: clamp(20px, 5vw, 56px);
  --col-meta: 140px;
  --col-gap: 40px;
  --col-body: 580px;

  --t-hero: clamp(30px, 5.6vw, 54px);
  --t-h2: clamp(21px, 2.6vw, 28px);
  --t-h3: 22px;
  --t-h3-lead: 26px;
  --t-h3-min: 19px;
  --t-lead: 17px;
  --t-body: 15px;
  --t-small: 13px;
  --t-meta: 12px;
  --t-label: 11px;
}

/* ── 기본 ───────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ko);
  font-size: var(--t-body);
  line-height: 1.8;
  letter-spacing: -.005em;
  -webkit-font-smoothing: antialiased;
  /* 한글은 기본값에서 음절 단위로 끊긴다. "기계"가 "기 / 계"로 갈라지면
     문단 끝마다 눈이 걸린다. 어절 단위로 묶고, 넘칠 때만 예외를 허용한다. */
  word-break: keep-all;
  overflow-wrap: break-word;
}
.mono, .spec-line, .entry__links a, .footnote, code, td.n, .swatch-labels span {
  overflow-wrap: anywhere;
}
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
p, dl, dd, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
/* Pretendard 서브셋은 400 · 700 두 벌뿐이다. 600 을 요구하면 맞는 얼굴이 없어
   폴백 서체로 떨어진다. 있는 굵기만 쓴다. */
b, strong { font-weight: 700; }
a { color: inherit; text-underline-offset: 3px; }
a:hover { color: var(--accent); }
a { transition: color 120ms ease; }
table { border-collapse: collapse; width: 100%; }

.wrap { max-width: var(--max-w); margin-inline: auto; padding-inline: var(--pad-x); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 99;
  background: var(--accent); color: var(--bg);
  padding: 12px 20px; border-radius: 4px; text-decoration: none;
}
.skip:focus { left: 0; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
#main:focus { outline: none; }

.mono { font-family: var(--font-mono); }
/* 숫자 강조는 굵기로만. mono로 감싸면 한글 조사가 폴백되며 공백 폭이 어긋난다. */
/* ── 맺음 ─────────────────────────────────────
   마지막에 읽은 것이 남는다. 문서를 덮을 때 손에 쥐는 문장을 여기 둔다. */
.closing { margin-top: 56px; padding-top: 28px; border-top: 2px solid var(--accent); }
.closing blockquote { margin: 0; }
.closing blockquote p {
  font-size: var(--t-h3-min); line-height: 1.9; color: var(--ink);
  max-width: none; margin: 0;
}
/* 출처는 한글이라 mono 를 쓰면 안 된다. mono 서브셋이 라틴(U+0020-007E)만
   담고 있어 글자는 Pretendard 로 떨어지는데 공백만 mono 의 넓은 폭으로
   남아, 어절 사이가 들쭉날쭉해진다. */
.closing cite {
  display: block; margin-top: 10px; font-style: normal;
  font-family: var(--font-ko); font-size: var(--t-small); color: var(--muted);
  letter-spacing: .01em;
}
.closing { text-align: center; }
.closing blockquote p { margin-inline: auto; }
.closing blockquote p::before { content: "\201C"; }
.closing blockquote p::after { content: "\201D"; }
/* 인용과 내 말 사이는 넉넉히 띄운다. 붙여 두면 인용이 끝난 자리에서
   바로 다음 목소리가 시작돼 여운이 남지 않는다. */
.closing__note {
  margin: 44px auto 0; max-width: 46ch;
  font-size: var(--t-body); line-height: 1.9; color: var(--muted);
}
.closing > p.closing__end {
  margin: 30px auto 0; font-size: var(--t-body); line-height: 1.9;
  color: var(--ink-soft); max-width: 46ch;
}
.closing__end strong {
  display: inline-block; margin-top: 6px; font-size: var(--t-h3-min);
  color: var(--accent); letter-spacing: -0.01em;
}

/* 본문 도판 — aspect-ratio 로 자리를 먼저 잡아야 로딩 중 CLS 가 생기지 않는다. */
.shot { margin-top: 24px; }
.shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: 3px; background: var(--bg-elev);
}
.shot--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.shot--pair figcaption { grid-column: 1 / -1; }
@media (max-width: 640px) { .shot--pair { grid-template-columns: 1fr; } }
/* 반전 밴드 위에서는 캡션이 밴드 글자색을 따라야 한다. */
/* 투명도를 걸지 않는다 — 이 문서가 스스로 금지한 규칙이다. 위계는 글자
   크기(.875rem)가 이미 만들고 있고, 불투명하게 두면 대비가 9.47:1 로 오른다.
   흐린 색을 새로 만들면 "코어 6개로 닫는다"는 옆 규칙이 깨진다. */
.band .shot figcaption { color: var(--band-fg); }
.band .shot img { border-color: var(--band-line); }

.shot figcaption { margin-top: 8px; font-size: .875rem; line-height: 1.6; color: var(--muted); }

/* 30초 스캔용 앵커 — 문서 전체에서 가장 큰 숫자 셋만 첫 면에 세운다. */
.badges { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; }
.badges > span {
  flex: 1 1 150px; border: 1px solid var(--line); border-radius: 3px;
  padding: 12px 14px; background: var(--bg-elev);
}
.badges b {
  display: block; font-family: var(--font-ko); font-size: 1.5rem; line-height: 1.1; color: var(--accent); }
.badges i { display: block; margin-top: 6px; font-style: normal; font-size: .8125rem; line-height: 1.5; color: var(--ink-soft); }

/* URL 은 줄 중간에서 끊기면 복사했을 때 깨진 주소가 된다. 통째로 넘긴다. */
.url { white-space: nowrap; overflow-wrap: normal; word-break: keep-all; }
.alt { font-weight: 400; color: var(--muted); font-size: .8em; margin-left: 6px; }

.num { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.print-only, .for-application, .print-note, .jd-lead { display: none; }

/* ── 상단 메타 ──────────────────────────────── */
.topmeta {
  font-family: var(--font-mono); font-size: var(--t-meta); color: var(--muted);
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  border-bottom: 1px solid var(--line); padding-bottom: 16px; padding-top: 24px;
}

/* ── HERO ───────────────────────────────────── */
.hero { padding: clamp(56px, 8vw, 96px) 0 clamp(40px, 5vw, 64px); }
.hero h1 {
  font-size: var(--t-hero); line-height: 1.16; letter-spacing: -.035em;
  max-width: 19ch; text-wrap: balance;
}
.hl {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.rule { width: 120px; height: 1px; background: var(--line); margin: 28px 0; border: 0; }
.hero__lead { font-size: var(--t-lead); line-height: 1.8; max-width: 54ch; color: var(--ink-soft); }
/* 화면에서도 리드 두 문단 사이에 쉼을 준다. */
.hero__lead + .hero__lead { margin-top: 18px; }
.hero__links {
  margin-top: 32px; display: flex; flex-wrap: wrap; gap: 24px;
  font-size: var(--t-body);
}
.hero__links a { color: var(--accent); }
.hero__links .role {
  display: block; font-family: var(--font-mono); font-size: var(--t-meta);
  color: var(--muted); margin-top: 4px;
}

/* ── 기술 스택 요약 ─────────────────────────── */
.stack-sum {
  margin-top: 40px; display: grid; gap: 12px;
  border-top: 1px solid var(--line); padding-top: 24px;
}
.stack-sum > div {
  display: grid; grid-template-columns: var(--col-meta) minmax(0, 1fr);
  gap: var(--col-gap); align-items: baseline;
}
.stack-sum dt {
  font-family: var(--font-ko); letter-spacing: .04em;
  font-size: var(--t-label); font-weight: 700;
  letter-spacing: .1em; color: var(--accent); text-transform: uppercase;
}
.stack-sum dd { font-size: 14px; line-height: 1.7; color: var(--ink-soft); }

/* ── 섹션 ───────────────────────────────────── */
.sec { padding: clamp(56px, 8vw, 96px) 0; }
.sec--light { padding: clamp(40px, 5vw, 64px) 0; }
.sec h2 {
  font-size: var(--t-h2); line-height: 1.30; letter-spacing: -.030em;
}
.sec__label {
  font-family: var(--font-mono); font-size: var(--t-label); font-weight: 600;
  letter-spacing: .12em; color: var(--muted); text-transform: uppercase;
  border-bottom: 1px solid var(--line); padding-bottom: 12px; margin-bottom: 12px;
  display: block;
}
.sec__lead { font-size: var(--t-lead); line-height: 1.8; max-width: 54ch; color: var(--ink-soft); margin-top: 16px; }
.sec__body { margin-top: 40px; }

/* ── 저널 2단 ───────────────────────────────── */
.entry {
  display: grid;
  grid-template-columns: var(--col-meta) minmax(0, var(--col-body));
  gap: var(--col-gap);
  align-items: start;
}
.entry + .entry { border-top: 1px solid var(--line); margin-top: 28px; padding-top: 28px; }
.entry__meta {
  font-family: var(--font-mono); font-size: var(--t-meta); line-height: 1.5;
  letter-spacing: .02em; color: var(--muted);
  border-left: 1px solid var(--line); padding-left: 16px;
}
.entry__meta span {
  font-family: var(--font-ko); letter-spacing: .02em; display: block; }
.entry h3 { font-size: var(--t-h3); line-height: 1.35; letter-spacing: -.025em; }
.entry__def { font-size: var(--t-lead); line-height: 1.8; color: var(--ink); margin-top: 8px; }
.entry__note { font-size: var(--t-body); color: var(--ink-soft); margin-top: 8px; }
.hr { border: 0; border-top: 1px solid var(--line); margin: 20px 0 24px; }

.fields { display: grid; gap: 16px; }
.fields > div { display: grid; grid-template-columns: 72px 1fr; gap: 16px; align-items: start; }
.fields dt {
  font-family: var(--font-mono); font-size: var(--t-meta); font-weight: 600;
  letter-spacing: .02em; color: var(--muted); padding-top: 4px;
}
.fields dd { font-size: var(--t-body); line-height: 1.8; color: var(--ink-soft); }
.fields dd ul { display: grid; gap: 8px; }
.fields dd li { position: relative; padding-left: 14px; }
.fields dd li::before {
  content: "·"; position: absolute; left: 2px; top: -.05em;
  color: var(--accent); font-weight: 700;
}
/* 불릿을 가진 필드는 라벨을 블록으로 올려 판단 단위가 눈에 끊기게 한다. */
.f--list dt { display: block; margin-bottom: 8px; }

.spec-line {
  font-family: var(--font-mono); font-size: 12.5px; line-height: 1.7;
  color: var(--ink-soft); margin-top: 24px;
  border-top: 1px solid var(--line); padding-top: 12px;
}
/* 스택 칩 — 주요 프로젝트는 본문이 길어 "무엇으로 만들었나"가 문장 속에 묻힌다.
   제목 바로 아래 한 줄로 빼서 읽지 않고도 스캔되게 한다.
   에메랄드 글자 / 샴페인 바탕은 7.99:1 로 AAA (03 대비비 표에 기재). */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.chips span {
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1;
  color: var(--accent); background: var(--bg-elev);
  border: 1px solid var(--line); border-radius: 3px; padding: 5px 8px;
  white-space: nowrap;
}
.entry__links { margin-top: 12px; font-size: var(--t-body); display: flex; gap: 20px; flex-wrap: wrap; }
.entry__links a { color: var(--accent); }
.footnote {
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1.7; color: var(--ink-soft);
  border-top: 1px dashed var(--line); padding-top: 12px; margin-top: 20px;
}

/* 리드 엔트리 (01) */
.entry--lead .entry__body {
  background: var(--bg-elev);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  padding: 28px 28px 28px 24px;
}
.entry--lead h3 { font-size: var(--t-h3-lead); }
.entry--lead .fields dt { color: var(--ink-soft); }
.entry--lead .entry__meta { border-left-color: var(--line); }

/* 그 외 엔트리 */
.minor h3 { font-size: var(--t-h3-min); }
.minor__prose { font-size: var(--t-body); line-height: 1.8; color: var(--ink-soft); margin-top: 12px; }
.subhead {
  font-family: var(--font-mono); font-size: var(--t-label); font-weight: 600;
  letter-spacing: .12em; color: var(--muted); text-transform: uppercase;
  border-top: 1px solid var(--line); padding-top: 12px; margin-top: 56px; display: block;
}

/* ── SVG 도식 ───────────────────────────────── */
.diagram { margin-top: 24px; }
.diagram svg { width: 100%; height: auto; display: block; }
.diagram figcaption { font-size: var(--t-small); color: var(--ink-soft); margin-top: 12px; }

/* ── 표 ─────────────────────────────────────── */
.tablewrap { overflow-x: auto; }
caption {
  text-align: left; font-size: var(--t-small); color: var(--ink-soft);
  padding-bottom: 12px; line-height: 1.7;
}
th, td {
  text-align: left; vertical-align: top; padding: 12px 12px 12px 0;
  border-bottom: 1px solid var(--line-soft); font-size: var(--t-body);
}
th {
  font-family: var(--font-mono); font-size: var(--t-meta); font-weight: 600;
  letter-spacing: .04em; color: var(--muted); text-transform: uppercase;
  border-bottom: 1px solid var(--line);
}
td.n { font-family: var(--font-mono); font-size: 16px; color: var(--ink); white-space: nowrap; }
.bar {
  display: block; width: calc(var(--v) * 1%);
  border-bottom: 2px solid currentColor; margin-top: 4px;
}
td.n.lo { color: var(--muted); }
td.n.hi { color: var(--accent); }
tr.judge td {
  font-size: var(--t-small); color: var(--ink-soft); line-height: 1.7;
  padding: 0 0 16px 16px; border-bottom: 1px solid var(--line);
}
.t-after { font-size: var(--t-lead); line-height: 1.8; color: var(--ink-soft); max-width: 54ch; margin-top: 32px; }

/* 06 요건표 */
.maptable td:nth-child(1) { font-weight: 700; color: var(--ink); }
.maptable td:nth-child(3) {
  font-family: var(--font-mono); font-size: var(--t-meta); color: var(--muted); white-space: nowrap;
}
.maptable td:nth-child(3).ok { color: var(--accent); }

/* ── 04 규칙 ────────────────────────────────── */
.rules { display: grid; gap: 24px; }
.rules p {
  border-left: 2px solid var(--line); padding-left: 16px;
  font-size: var(--t-body); line-height: 1.8; color: var(--ink-soft); max-width: 54ch;
}
.swatch { display: flex; height: 48px; border: 1px solid var(--muted); margin-top: 40px; }
.swatch span { flex: 1; border-right: 1px solid var(--muted); }
.swatch span:last-child { border-right: 0; }
.swatch-labels {
  display: flex; margin-top: 8px;
  font-family: var(--font-mono); font-size: 10px; line-height: 1.5; color: var(--ink-soft);
}
.swatch-labels span { flex: 1; word-break: break-all; padding-right: 4px; }
.cap { font-size: var(--t-small); color: var(--ink-soft); margin-top: 12px; }

pre {
  margin: 0; background: var(--bg-elev); border: 1px solid var(--line);
  border-radius: 4px; padding: 18px 20px; overflow-x: auto;
}
code { font-family: var(--font-mono); font-size: 13px; line-height: 1.7; color: var(--ink-soft); }

/* ── 05 밴드 ────────────────────────────────── */
.band {
  --band-fg: var(--bg);
  --band-line: var(--line);
  background: var(--accent);
  color: var(--band-fg);
}
.band h2, .band h3, .band p, .band li, .band .sec__label { color: var(--band-fg); }
.band .sec__label { border-bottom-color: var(--band-line); }
.band .item { border-top: 1px solid var(--band-line); padding-top: 20px; margin-top: 32px; }
.band .item h3 { font-size: var(--t-h3-min); line-height: 1.4; }
.band .item p { font-size: var(--t-body); line-height: 1.8; margin-top: 12px; max-width: 60ch; }
.band ul { border-left: 1px solid var(--band-line); padding-left: 16px; margin-top: 12px; }
.band li { font-size: var(--t-body); line-height: 1.8; margin-bottom: 12px; max-width: 60ch; }
.band li:last-child { margin-bottom: 0; }

/* ── 07 / 08 / 09 ───────────────────────────── */
.gap-item { margin-top: 32px; max-width: 60ch; }
.gap-item h3 { font-size: var(--t-lead); }
.gap-item p { font-size: var(--t-body); line-height: 1.8; color: var(--ink-soft); margin-top: 8px; }

.timeline { margin-top: 32px; }
.timeline > div {
  display: grid; grid-template-columns: var(--col-meta) minmax(0, 1fr); gap: var(--col-gap);
  padding: 12px 0; border-bottom: 1px solid var(--line-soft); align-items: start;
}
.timeline dt {
  font-family: var(--font-mono); font-size: var(--t-meta); color: var(--muted); letter-spacing: .02em;
}
.timeline dd { font-size: var(--t-body); line-height: 1.7; color: var(--ink-soft); }
.tl-note {
  font-family: var(--font-mono); font-size: var(--t-meta); color: var(--ink-soft);
  margin-top: 16px; line-height: 1.7;
}

.contact-lines { font-size: 16px; font-weight: 400; line-height: 1.9; margin-top: 32px; }
.contact-lines a { color: var(--accent); }
.preface {
  font-size: var(--t-small); line-height: 1.8; color: var(--ink-soft);
  max-width: 60ch; margin-top: 40px;
}
footer {
  font-family: var(--font-mono); font-size: var(--t-meta); line-height: 1.8; color: var(--ink-soft);
  border-top: 1px solid var(--line); padding: 24px 0 44px; margin-top: 24px;
}
footer a { color: var(--ink-soft); }

/* ── 반응형 ─────────────────────────────────── */
@media (max-width: 1024px) {
  :root { --col-meta: 120px; --col-gap: 28px; }
}
@media (max-width: 720px) {
  :root { --pad-x: 20px; }
  .sec { padding: 48px 0; }
  .sec--light { padding: 40px 0; }
  .entry, .timeline > div { grid-template-columns: 1fr; gap: 16px; }
  .entry__meta { border-left: 0; padding-left: 0; }
  .entry__meta span { display: inline; }
  .entry__meta span + span::before { content: " · "; }
  .fields > div { grid-template-columns: 1fr; gap: 4px; }
  .fields dt { padding-top: 0; }
  .entry--lead .entry__body { padding: 20px 20px 20px 16px; }
  .swatch-labels { font-size: 10px; }
}
@media (max-width: 520px) {
  .hero h1 { font-size: 30px; }
  /* 도식을 display:none 으로 숨기면 안에 든 title · desc 까지 접근성 트리에서 사라진다.
     좁은 화면에서는 가로로 스크롤시킨다. */
  .diagram { overflow-x: auto; }
  .diagram svg { min-width: 460px; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* ── 인쇄 (A4) ──────────────────────────────── */
@media print {
  @page { size: A4; margin: 15mm 20mm 16mm 20mm; }
  html { scroll-behavior: auto; }
  body { background: #fff; font-size: 10.5pt; line-height: 1.62; }
  .skip, .topmeta { display: none !important; }
  .for-application, .jd-lead { display: block; }
  .print-only { display: inline; }
  /* p 에 붙으면 인라인이라 연속 문단이 한 덩어리로 이어붙는다. */
  p.print-only { display: block; }

  /* 서류 단계용 축약본 — 근거의 중심만 남기고 정독용 섹션은 웹으로 보낸다.
     JYP는 면접 제출물에 대해 "충분히 담아 제출"을 명시했고, 서류는 스캔 구간이다. */
  .web-only { display: none !important; }
  .print-note {
    display: block; font-size: 9pt; color: #555;
    border-top: .5pt solid #ccc; padding-top: 6pt; margin-top: 10pt;
  }
  .wrap { max-width: none; padding-inline: 0; }

  /* 행 길이는 max-width 캡이 아니라 페이지 여백으로 잡는다.
     본문만 135mm로 묶고 표·구분선은 전폭이면 우측 끝이 세 개가 되어
     문단마다 45mm짜리 죽은 여백이 생긴다. 캡을 없애 끝을 하나로 만든다. */

  .hero { padding: 0 0 6pt; }
  /* 인쇄에서는 폭이 180mm로 넓어지므로 ch 기준 상한을 풀어야 수동 개행이 지켜진다. */
  .hero h1 { font-size: 17pt; max-width: none; line-height: 1.22; }
  .hero__lead { font-size: 10.5pt; margin-top: 0; }
  .hero__lead + .hero__lead { margin-top: 14pt; }
  /* 리드가 두 문단이므로 둘 사이에는 쉼이 있어야 한다. 위 규칙의 margin-top:0 이
     p + p (0,0,2) 를 이기므로, 인접 선택자를 (0,2,0) 로 올려 되돌린다. */
  /* 문서 머리 — 연락처가 8페이지 끝에만 있으면 30초 스캔에서 못 찾는다. */
  .doc-head {
    /* .print-only 의 display:inline 을 되돌린다 —
       인라인이면 세로 마진이 무시돼 밑줄이 다음 줄 글자에 붙는다. */
    display: block;
    font-family: var(--font-mono); font-size: 8.5pt; line-height: 1.5; color: #555;
    border-bottom: .5pt solid #ccc; padding-bottom: 6pt; margin: 0 0 10pt;
    overflow-wrap: normal; word-break: keep-all;  /* URL이 중간에서 갈리지 않게 */
  }
  /* 기술 스택 요약 — 스택이 프로젝트마다 흩어져 있으면 한눈에 볼 곳이 없다. */
  .stack-sum {
    margin-top: 24pt; padding-top: 11pt; gap: 5pt;
    border-top: .5pt solid #ccc; break-inside: avoid;
  }
  .stack-sum > div { grid-template-columns: 22mm 1fr; gap: 5mm; }
  .stack-sum dt { font-size: 8pt; }
  .stack-sum dd { font-size: 9.5pt; line-height: 1.5; }
  .rule { display: none; }
  .hero h1 + .hero__lead, .rule + .hero__lead { margin-top: 13pt; }
  /* 4개가 한 줄에 안 들어가 3+1 로 접히면, 혼자 떨어진 하나가 이상으로
     보여 시선을 훔친다. 4등분 격자로 고정해 한 줄에 맞춘다. */
  .hero__links {
    margin-top: 20pt; font-size: 9.5pt;
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 6pt;
  }
  .hero__links .role { font-size: 8.5pt; margin-top: 2pt; }
  /* 지원 라벨은 머리에 한 줄로 남기고, 본문은 1면 맨 아래로 내렸다.
     헤드라인 위에 회색 본문 다섯 줄이 깔리면 가장 큰 글자가 진입점이 아니라
     두 번째 소제목으로 읽힌다 — 지면에 시작점이 없어진다. */
  .jd-lead {
    font-size: 9pt; color: #555; margin: 0 0 24pt; letter-spacing: .01em;
  }
  /* 1면은 표지다. 남은 자리에 01 섹션 제목이 딸려 들어오면 제목만 고아로
     걸리고 표지의 끝이 흐려진다. 여기서 지면을 끊는다. */
  .hero { break-after: page; }
  /* 04 표가 두 면에 갈리면 뒷면에 몇 행만 남고 큰 백지가 생긴다. 잘린 표 뒤의
     백지는 실수로 읽히고, 섹션 끝의 백지는 마침으로 읽힌다. 통째로 넘긴다. */
  #map { break-before: page; }
  .for-application {
    font-size: 10pt; line-height: 1.5; color: #333;
    border-left: 2pt solid #064E3B; padding-left: 9pt; margin-top: 14pt;
  }
  .sec { padding: 14pt 0; }
  .sec--light { padding: 10pt 0; }
  /* ── 문서용 제목 ──
     웹의 "01 / 지금 하는 일" + "지금 하는 일" 이중 표기는 인쇄에서 같은 말의 반복이다.
     라벨을 숨기고 번호만 제목 앞에 붙여 한 줄로 합친다. */
  .sec__label { display: none; }
  .sec h2 {
    font-size: 16pt; letter-spacing: -.02em; line-height: 1.35;
    padding-bottom: 5pt; border-bottom: .75pt solid #064E3B; margin-bottom: 13pt;
  }
  .sec h2::before {
    content: attr(data-no) ".  ";
  }
  .sec h2[data-print-no]::before {
    content: attr(data-print-no) ".  ";
    color: #6F6752; font-family: var(--font-mono); font-size: 11pt; font-weight: 600;
  }

  /* ── 문서용 본문 리듬 ──
     문단이 붙어 덩어리로 보이지 않게 문단·소제목 간격을 벌린다. */
  p + p, dd + dd { margin-top: 7pt; }
  .entry h3, .entry--lead h3, .minor h3, .band .item h3, .gap-item h3 { margin-bottom: 6pt; }
  /* 섹션 안내문은 본문보다 작아야 한다. 프로젝트 내용이 주가 되어야 하는데
     지금은 안내문(17px≒12.75pt)이 본문(10.5pt)보다 커서 위계가 뒤집혀 있었다. */
  .sec__lead { font-size: 9.5pt; color: #555; margin-bottom: 3pt; line-height: 1.5; }
  .band .item p + p { margin-top: 7pt; }
  .band .item p { margin-top: 6pt; }
  .gap-item { margin-top: 16pt; }
  .gap-item p { margin-top: 5pt; }
  .rules p { margin-bottom: 0; }
  .t-after + .t-after { margin-top: 9pt; }
  .entry h3, .entry--lead h3 { font-size: 14.5pt; }
  .minor h3, .band .item h3 { font-size: 12.5pt; }
  .entry__def { font-size: 11.5pt; color: var(--ink); }
  .mono, .entry__meta, .spec-line, .footnote, th, .timeline dt, .tl-note, footer, code { font-size: 8.5pt; }

  /* 인쇄는 본문 180mm 전폭을 쓴다. 좌 메타를 한 줄로 접으면 행당 글자수가 늘어
     같은 내용이 더 적은 줄에 들어간다. */
  /* 좌측 기준선을 하나로 — 섹션라벨 · h2 · 메타 · h3 · 본문 · 필드가 모두 같은 x에서 시작한다. */
  .entry, .timeline > div { grid-template-columns: 1fr; gap: 4pt; }
  .entry__meta { border-left: 0; padding-left: 0; }
  .entry__meta span { display: inline; }
  .entry__meta span + span::before { content: " · "; }
  .timeline > div { grid-template-columns: 28mm 1fr; gap: 6mm; }

  /* 필드 라벨을 열이 아니라 런인으로 — 두 번째 기준선을 없앤다.
     행 길이 제한은 인라인 dd가 아니라 이 블록 컨테이너에 걸어야 적용된다. */
  .fields > div { display: block; }
  .fields dt {
    display: block; font-family: var(--font-mono); font-size: 8.5pt; font-weight: 600;
    color: #064E3B; letter-spacing: .1em; padding: 0; margin-bottom: 3pt;
  }
  .fields dt::after { content: none; }
  .fields dd { display: inline; }
  /* 불릿 필드는 런인에서 제외 — 라벨은 블록, 항목은 세로로 끊는다. */
  .f--list dt { display: block; margin-bottom: 3pt; }
  .f--list dt::after { content: none; }
  .f--list dd { display: block; }
  .fields dd ul { display: block; }
  .fields dd li { padding-left: 11pt; margin-bottom: 6pt; }
  .fields dd li:last-child { margin-bottom: 0; }

  /* 01 리드 박스: 음수 마진으로 상쇄해 박스 '내용'이 기준선에 맞게 한다. */
  .entry--lead .entry__body {
    margin-left: -10pt; margin-right: -10pt;
    padding: 9pt 8pt 9pt 8pt;
  }
  #numbers .wrap, #map .wrap, #how .wrap { display: block; }
  .tablewrap { overflow: visible; }

  /* 표 열 너비 고정 — 자동 폭에 맡기면 "데스크 / 톱"처럼 두 글자에서 줄이 바뀐다. */
  table { table-layout: fixed; }
  #numbers td, #numbers th { padding: 6pt 4pt 6pt 0; }
  #numbers th:nth-child(1), #numbers td:nth-child(1) { width: 40mm; }
  #numbers th:nth-child(2), #numbers td:nth-child(2) { width: 15mm; white-space: nowrap; }
  #numbers th:nth-child(3), #numbers td:nth-child(3),
  #numbers th:nth-child(4), #numbers td:nth-child(4),
  #numbers th:nth-child(5), #numbers td:nth-child(5),
  #numbers th:nth-child(6), #numbers td:nth-child(6) { width: 16mm; }
  #numbers th:nth-child(7), #numbers td:nth-child(7) { width: auto; }
  #numbers td.n { white-space: nowrap; font-size: 10pt; }
  tr.judge td { padding: 0 0 8pt 8pt; }
  /* 수치 행과 바로 아래 판단 행은 반드시 같은 페이지에 둔다. */
  tr:has(+ tr.judge) { break-after: avoid; }

  #map th:nth-child(1), #map td:nth-child(1) { width: 38mm; }
  #map td { padding: 3.5pt 4pt 3.5pt 0; line-height: 1.42; }
  #map th:nth-child(1), #map td:nth-child(1) { width: 40mm; }

  /* 34mm 고정폭 + nowrap 조합에서 "REST 실무 · GraphQL 개인"의 끝 두 글자가 잘려
     표가 "GraphQL 실무"로 읽혔다. 줄바꿈만 허용한다 — 열 폭은 건드리지 않는다. */
  .maptable td:nth-child(3) { white-space: normal; line-height: 1.45; }

  .band {
    --band-fg: var(--ink);
    --band-line: var(--line-soft);
    background: none;
    border-block: 2pt solid #064E3B;
    padding-block: 10pt;
  }
  /* 배경을 깐 박스는 페이지 경계에서 잘려 나가 인쇄물에서 사고처럼 보인다.
     좌측 굵은 선만 남기면 페이지를 넘어가도 같은 항목으로 읽힌다. */
  /* 세로선은 페이지 경계에서 텍스트 없이 30mm씩 이어져 잘린 것처럼 보인다.
     제목 옆 라벨로 바꾸면 같은 강조를 지면 낭비 없이 얻는다. */
  .entry--lead .entry__body {
    background: none; border-left: 0; padding: 0;
    margin-left: 0; margin-right: 0; border-radius: 0;
  }
  .entry--lead h3::after {
    content: "재직 중"; font-family: var(--font-mono); font-size: 8.5pt;
    color: #064E3B; margin-left: 8pt; vertical-align: 2pt; letter-spacing: .08em;
    font-weight: 600;
  }
  pre { border: .5pt solid #999; }
  .swatch, .swatch span { border-color: #999; }
  .swatch span { border-right: .5pt solid #999; }

  /* 긴 블록에 break-inside:avoid를 걸면 앞 페이지가 통째로 비므로,
     짧은 단위(표 행 · 코드 · 스와치)에만 적용하고 긴 엔트리는 고아/과부 줄만 막는다. */
  .closing {
    break-before: page; break-inside: avoid;
    margin: 0; padding: 0; border-top: 0;
    min-height: 252mm; display: flex; flex-direction: column;
    align-items: center; justify-content: center;
  }
  .closing blockquote p { font-size: 11pt; line-height: 1.95; max-width: none; }
  .closing cite { font-size: 8.5pt; margin-top: 5pt; }
  .closing__note { font-size: 10pt; margin: 26pt auto 0; max-width: 46ch; line-height: 1.8; color: #6F6752; }
  .closing > p.closing__end { font-size: 10.5pt; margin: 18pt auto 0; max-width: 46ch; line-height: 1.8; }
  .closing__end strong { font-size: 13pt; margin-top: 4pt; }

  .shot { break-inside: avoid; margin-top: 11pt; }
  .shot img {
    border: .5pt solid #aaa; width: 100%; height: auto;
    max-height: 78mm; object-fit: cover; object-position: top;
  }
  .shot--pair { gap: 4mm; }
  /* subhead 는 h 태그가 아니라 break-after 규칙이 안 걸린다. 고아 제목을 막는다.
     Chrome 은 뒤 블록이 크면 break-after: avoid 를 포기하므로, 소제목과 첫 항목을
     한 덩어리로 묶어 통째로 넘어가게 한다. */
  .subhead { margin-top: 20pt; break-after: avoid; }
  /* 소제목이 바로 아래 메타 줄과 같은 회색 모노라 한 덩어리로 읽힌다. 아래를 띄운다. */
  span.subhead.print-only { display: block; margin-bottom: 11pt; }
  .subhead.print-only + .entry { break-before: avoid; }
  thead, thead tr { break-after: avoid; }
  .band .shot figcaption { color: #555; }
  .band .shot img { border-color: #aaa; }
  .shot figcaption { font-size: 8.5pt; margin-top: 5pt; color: #555; }
  .badges { gap: 6pt; margin-top: 24pt; break-inside: avoid; }
  .badges > span { padding: 6pt 8pt; border: .5pt solid #064E3B; background: #F8E7C9; }
  .badges b { font-size: 13pt; }
  .badges i { font-size: 7.5pt; margin-top: 3pt; }

  tr, tr.judge, pre, .swatch, .diagram, .gap-item, .fields > div { break-inside: avoid; }
  .band .item { break-inside: auto; }
  .band .item h3 { break-after: avoid; }
  .entry, .entry--lead { break-inside: auto; orphans: 3; widows: 3; }

  /* ── 고아 제목 방지 ──
     제목만 페이지 끝에 남고 본문이 다음 장으로 넘어가는 것을 막는다.
     제목은 뒤따르는 내용과 반드시 같은 페이지에 있어야 한다. */
  h1, h2, h3, h4 { break-after: avoid; break-inside: avoid; }
  .sec__label { break-after: avoid; }
  .entry__meta { break-after: avoid; }        /* 기간·역할 줄이 h3와 갈라지지 않게 */
  /* 한두 줄짜리 짧은 블록은 쪼개지 않는다 — 문장 중간에서 페이지가 넘어가면 다시 읽어야 한다. */
  .entry__def, .entry__note, .sec__lead, .cap, figcaption {
    break-after: avoid; break-inside: avoid;
  }
  .hr { break-after: avoid; break-before: avoid; }
  .fields dt, .f--list dt { break-after: avoid; }  /* 라벨과 항목 분리 금지 */
  .sec__lead { break-after: avoid; }
  caption { break-after: avoid; }
  thead { display: table-header-group; }      /* 표가 나뉘면 헤더를 다음 장에 반복 */
  tfoot { display: table-footer-group; }
  li { break-inside: avoid; }
  p, dd, li { orphans: 2; widows: 2; }
  .sec { padding: 13pt 0; }
  .sec--light { padding: 8pt 0; }
  .sec__body { margin-top: 13pt; }
  .entry + .entry { margin-top: 19pt; padding-top: 19pt; }
  .subhead { margin-top: 20pt; }
  .rules { gap: 10pt; }
  .item { margin-top: 18pt !important; padding-top: 12pt !important; }
  .t-after { margin-top: 12pt; }
  .swatch { height: 26pt; margin-top: 14pt; }
  .hr { margin: 9pt 0 12pt; }
  .fields { gap: 13pt; }
  .spec-line, .footnote { margin-top: 10pt; padding-top: 6pt; }
  /* 칩은 지면에서 한 줄만 먹어야 한다 — 여백을 줄이고 제목에 붙인다. */
  .chips { gap: 3pt; margin-top: 5pt; break-inside: avoid; break-after: avoid; }
  .chips span { font-size: 7.5pt; padding: 2pt 4pt; border: .5pt solid #E4D3B0; background: #F8E7C9; color: #064E3B; }
  .diagram { margin-top: 10pt; }
  .timeline { margin-top: 14pt; }
  .timeline > div { padding: 6pt 0; }
  .preface { margin-top: 16pt; }
  footer { display: none; }
  /* PDF 빌더도 브라우저라 fe.js 가 돌아간다. 스크립트가 만든 화면 전용
     요소는 지면에 남으면 안 된다 — 표식은 섹션 이름 목록으로 찍힌다. */
  .progress, .dots { display: none !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .a-note::after { content: " (" attr(href) ")"; font-size: 8pt; color: #666; }
}


/* ═══════════════════════════════════════════════════════════════════
   인터랙션 — 화면 전용

   전부 @media screen 안에 둡니다. 인쇄 조판은 이 블록을 보지 않으므로
   PDF 는 지금 상태 그대로 유지됩니다.

   초기 숨김은 `.js` 아래에만 둡니다. fe.js 가 로드되지 않으면 `.js` 가
   붙지 않고 모든 내용이 처음부터 보입니다 — 스크립트가 콘텐츠의 전제가
   되지 않게 하기 위해서입니다.
   ═══════════════════════════════════════════════════════════════════ */
@media screen {
  /* ── 나타내기 ── */
  .js .reveal { opacity: 0; transform: translateY(14px); will-change: opacity, transform; }
  .js .reveal.is-in {
    opacity: 1; transform: none; will-change: auto;
    transition: opacity .6s cubic-bezier(.22, .61, .36, 1),
                transform .6s cubic-bezier(.22, .61, .36, 1);
  }

  /* ── 스크롤 진행 막대 ──
     scaleX 만 바꾸므로 레이아웃이 다시 계산되지 않습니다. */
  .progress {
    position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 100;
    background: var(--accent); transform: scaleX(0); transform-origin: 0 50%;
    pointer-events: none;
  }

  /* ── 섹션 표식 ── */
  .dots {
    position: fixed; right: 20px; top: 50%; translate: 0 -50%; z-index: 90;
    display: flex; flex-direction: column; gap: 2px;
  }
  /* 최소 24×24 — 그보다 작으면 터치 대상 규격에 걸린다. */
  .dots a {
    display: flex; align-items: center; justify-content: flex-end; gap: 8px;
    min-height: 26px; min-width: 26px; padding: 0 2px;
    text-decoration: none; color: var(--muted);
    font-family: var(--font-mono); font-size: 11px; line-height: 1;
  }
  .dots a i {
    width: 18px; height: 2px; background: var(--line); border-radius: 2px;
    transition: width .3s ease, background .3s ease; flex: none;
  }
  .dots a span {
    max-width: 0; overflow: hidden; white-space: nowrap; opacity: 0;
    transition: max-width .3s ease, opacity .3s ease;
  }
  .dots a.is-on i { width: 28px; background: var(--accent); }
  .dots a.is-on { color: var(--accent); }
  .dots:hover a span, .dots a:focus-visible span { max-width: 170px; opacity: 1; }

  /* ── 손대면 반응하는 것들 ── */
  .entry__links a, .hero__links a {
    transition: color .2s ease, border-color .2s ease;
  }
  .hero__links a:hover { border-color: var(--accent); }
  .chips span { transition: border-color .2s ease, transform .2s ease; }
  .entry:hover .chips span { border-color: var(--accent); }
  .shot img { transition: transform .45s cubic-bezier(.22, .61, .36, 1); }
  .shot:hover img { transform: scale(1.015); }
  .badges > span { transition: transform .25s ease, border-color .25s ease; }
  .badges > span:hover { transform: translateY(-2px); border-color: var(--accent); }

  /* 좁은 화면에서는 표식을 접습니다 — 본문 폭을 먹습니다. */
  @media (max-width: 1180px) { .dots { display: none; } }
}

/* 모션을 줄이도록 설정한 사용자에게는 전부 즉시 보이게 합니다. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal, .js .reveal.is-in {
    opacity: 1; transform: none; transition: none; will-change: auto;
  }
  .shot:hover img, .badges > span:hover { transform: none; }
}
