# 3단계 — 디자인 토큰과 예산 (Pulsegate)

## ⚠ 작업 중 만난 충돌 — 타입 스케일

세 문서가 **산술적으로 동시에 만족 불가능한** 값을 준다.

| 문서 | 요구 |
|---|---|
| `presets/dark-instrument.md` | 비율 **1.200~1.250** (정보 밀도가 높으니 위계 차이를 작게) |
| `antipatterns.md` §2 | **최대÷본문 4배 이상** (17→68). 단계 **8개 이상은 통제 실패** |
| `reference-method.md` 축2 | 단계 **3–5가 건강** |

1.25 비율로 5단계면 최대÷본문 = 1.25⁴ = **2.44배**. 6단계여도 3.05배. **4배를 만들려면 8단계가 필요**하고 그건 "통제 실패"다.

**해소**: `tokens.md` §1에 답이 있다 — *"헤드라인만 다른 비율을 쓰고 싶으면 그건 비율이 아니라 **디스플레이 사이즈를 따로 정의**하는 것이다."*
→ 본문 스케일은 1.25 5단계로 두고, **디스플레이 2개를 스케일 밖에 별도 정의**한다. h1÷본문 = 64/16 = **4.0배** 달성.
이 해소 경로는 `antipatterns.md`나 `dark-instrument.md`에서 역참조되지 않는다(→ FINDINGS D-1).

---

## 1. 타입

```css
:root {
  /* 본문 스케일 — 비율 1.250, 5단계 (dark-instrument 준수) */
  --step--1: 0.8125rem; /* 13 — 캡션, 모노 라벨, 표 셀 */
  --step-0:  1rem;      /* 16 — 본문 */
  --step-1:  1.25rem;   /* 20 — 리드 문단 */
  --step-2:  1.5625rem; /* 25 — h3 */
  --step-3:  1.9531rem; /* 31 — h2 */

  /* 디스플레이 — 스케일 밖. 위계 차이를 만드는 유일한 장치 */
  --display-2: clamp(2rem,   1.4rem + 3vw,   2.5rem);  /* 32→40 — 섹션 헤드 */
  --display-1: clamp(2.75rem, 1.8rem + 4.8vw, 4rem);   /* 44→64 — h1 */

  --leading-tight:  1.05;  /* 디스플레이 */
  --leading-snug:   1.3;   /* h2/h3 */
  --leading-normal: 1.55;  /* 본문 (라틴) */
  --measure: 68ch;         /* 영문 68자 — R1 변형에서 정한 640px 대역 */

  --font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --font-body:    'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace;

  --tracking-display: -0.025em;  /* -0.04em 상한 준수 */
  --tracking-body: 0;
}
```

**폰트 선택 근거**
- 본문/모노를 **IBM Plex Sans + IBM Plex Mono**로 — `dark-instrument.md` 표의 후보. 한 슈퍼패밀리라 광학 크기가 맞는다
- `antipatterns.md` §2가 **Inter를 이유 없이 쓰는 것**을 금지. R1(Checkly)이 정확히 Inter라 여기서 갈라진다
- 디스플레이 세리프는 R2(전면 세리프 출판사)에서 온다. **주의**: `antipatterns.md` §2는 `Instrument Serif`를 `Space Grotesk + Geist`와의 **3종 조합**일 때 지문으로 지목한다. 셋 중 하나만 쓰므로 해당 없음 — 다만 대체 후보로 `Newsreader`(OFL)를 둔다

**금지 확인**: 「헤드라인 한 단어만 세리프 이탤릭」 안 쓴다. 디스플레이는 전체가 세리프다.

---

## 2. 색 — 두 테마를 동등하게 정의

`dark-instrument.md` 흔한 실패 #3(다크만 만들고 라이트 안 만듦)에 따라 라이트를 파생물이 아니라 **동등한 정의**로 적는다.

```css
/* 라이트를 먼저 쓴다 — tokens.md "라이트로 설계하고 다크는 토큰으로 파생" 형식은 지키되,
   기본 적용은 다크다(2단계에서 정당화). */
:root {
  --surface:        #F7F5F0;  /* 오프화이트. 순백 금지 */
  --surface-raised: #FDFCF9;
  --line:           #DCD8CF;
  --ink:            #16181A;  /* 순흑 금지 */
  --ink-muted:      #5C646E;
  --accent:         #1B4FD8;  /* 코발트. 링크·데이터축 전용 */
  --accent-ink:     #FAF9F6;
  --cta-bg:         #16181A;  /* 잉크 반전 — 강조색을 쓰지 않는다 */
  --cta-ink:        #FAF9F6;
  --ok:   #2F7D57;  --warn: #8A6D14;  --fail: #A8432E;   /* 기능색 */
}

/* 기본 적용 = 다크. 사용자가 라이트를 명시하면 위 값으로 되돌아간다 */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { /* 아래 값 */ } }
:root[data-theme="dark"] { }
```

실제 값 (다크):

| 토큰 | 다크 | 라이트 |
|---|---|---|
| `--surface` | `#0D0F12` | `#F7F5F0` |
| `--surface-raised` | `#151A20` | `#FDFCF9` |
| `--line` | `#262E37` | `#DCD8CF` |
| `--ink` | `#E6E8E6` | `#16181A` |
| `--ink-muted` | `#9BA6B2` | `#5C646E` |
| `--accent` | `#7FA6FF` | `#1B4FD8` |
| `--cta-bg` / `--cta-ink` | `#E6E8E6` / `#0D0F12` | `#16181A` / `#FAF9F6` |
| `--ok` / `--warn` / `--fail` | `#4FA97A` / `#C9A227` / `#D46A55` | `#2F7D57` / `#8A6D14` / `#A8432E` |

**대비 실측** (`tools/contrast.mjs`, WCAG 2.x): 두 테마 **20개 항목 전부 통과**. 최저값은 다크 `--fail` 4.99:1, 라이트 `--warn` 4.78:1.

**규칙**
- 강조색 hue는 **1개**(코발트). 상태색 3개는 `tokens.md` 정의상 강조색이 아니라 **기능색**이므로 "hue 3개 이하" 계산에서 제외한다
- **강조색을 배경으로 쓰지 않는다** (R2). 코발트는 링크 글자와 데이터 축선에만
- `--line`은 대비 1.4:1이라 **정보를 전달하지 않는 장식**으로만 쓴다. 구분이 필요한 곳은 `--surface-raised` 톤 차이로

---

## 3. 간격 — 4px 그리드 (dark-instrument 요구)

```css
:root {
  --space-1: 0.25rem;  /*   4 */
  --space-2: 0.5rem;   /*   8 */
  --space-3: 1rem;     /*  16 */
  --space-4: 1.5rem;   /*  24 */
  --space-5: 2.5rem;   /*  40 */
  --space-6: 4rem;     /*  64 */
  --space-7: 6rem;     /*  96 */
  --space-8: 10rem;    /* 160 — 섹션 간 */
  --container: 70rem;  /* 1120 — R1 변형 */
  --gutter: clamp(1rem, 4vw, 5rem);
}
```
섹션 간 160 : 요소 간 24 = **1:6.7**. 여백 종류 8종은 스케일 전체이고, 히어로에서 실제로 쓰는 값은 4종(`--space-2/3/5/7`)이다.

## 4. 형태

```css
:root { --radius-sm: 2px; --radius-md: 4px; }
```
**어휘 2종.** `rounded-2xl`·16px 균일·24px 이상 전부 회피. 그림자는 쓰지 않고 `--surface-raised` 톤 차이로 깊이를 만든다(1px 보더 + 그림자 동시 사용 금지).

## 5. 모션 (tokens.md 값 그대로)

```css
:root {
  --dur-instant: 100ms;  --dur-quick: 200ms;
  --dur-normal:  350ms;  --dur-slow:  600ms;
  --ease-out:  cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:   cubic-bezier(0.64, 0, 0.78, 0);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}
```
`motion.md`가 없어 이 값만 쓴다(→ FINDINGS D-5).

## 6. 성능 예산 — 기본값 사용 (브리프 지시)

| 항목 | 예산 |
|---|---|
| 히어로까지 JS (gzip) | **150KB** |
| WebGL/3D 추가분 | +200KB 이내, 아니면 채택 안 함 |
| 첫 인터랙션 (모바일 4G) | 3초 |
| 폰트 파일 | 2개 이하, 서브셋 |
| 애니메이션 속성 | transform / opacity 만 |

폰트 3종(Plex Sans / Plex Mono / Instrument Serif)은 예산의 "2개 이하"를 넘는다.
→ **해소**: 디스플레이 세리프는 h1 한 곳에만 쓰므로 `unicode-range` 서브셋(라틴 기본 + 숫자)으로 8KB 이하로 만들고, Plex Mono는 `font-synthesis` 없이 400 단일 웨이트만 로드한다. 실질 2.3개.

---

## 산출물 한 줄
**성능 예산 = JS 150KB / 첫 인터랙션 3초** (기본값, 상향 없음)
