diff --git a/FIGMA-DESIGN-LANGUAGE.md b/FIGMA-DESIGN-LANGUAGE.md
new file mode 100644
index 0000000..b714ea9
--- /dev/null
+++ b/FIGMA-DESIGN-LANGUAGE.md
@@ -0,0 +1,112 @@
+# Figma Design Language Analysis
+
+> Phase 1 결과 문서 (2026-04-07)
+> Figma Source: `9S6LsQyO6zlRxtiqZccOUM` / Page 1
+
+## 1. 스코프
+
+| 프레임 | 역할 | 판정 |
+|--------|------|------|
+| Frame 1 (1:3) | 3D 수렴 화살표 | 서브 컴포넌트 (장식 이미지) |
+| Frame 2 (1:5) | Solution 제작 목표 | **블록화** → hero-icon-cards |
+| Frame 3 (1:35) | 정책 달성 (Engn.Solution vs DfMA) | **블록화** → compare-2col-badge |
+| Frame 4 (1:49) | 과정 vs 결과의 혁신 | **블록화** → compare-detail-gradient |
+| Frame 5 (1:74) | 상세보기 버튼 | 서브 컴포넌트 (CTA) |
+| Frame 6 (1:80) | 정책방향 (세로 문서) | **제외** (1280×720 부적합) |
+
+## 2. 스케일 변환
+
+Figma 캔버스 → 슬라이드(1280px) 변환 비율:
+- Frame 2, 3: ×0.71 (1808px → 1280px)
+- Frame 4: ×0.33 (3848px, 양쪽 합쳐서 2패널)
+
+| Figma | 슬라이드 환산 | 역할 |
+|-------|-------------|------|
+| 70px | 28-35px | 대섹션 헤더 |
+| 60px | 24-28px | Hero 메시지 |
+| 50px | 22-26px | 섹션 제목, 배지 |
+| 45px | 20-22px | 카드 타이틀 (EN) |
+| 40px | 16-20px | 본문 |
+| 35px | 14-18px | 부제, 한국어 서브 |
+| 32px | 12-14px | 버튼 |
+
+## 3. 색상 팔레트 (Warm Theme)
+
+기존 블루/슬레이트 테마와 **병존**하는 새 팔레트:
+
+| 토큰 | Hex | Figma 원본 | 용도 |
+|------|-----|-----------|------|
+| `--color-warm-brown` | `#5C3714` | rgba(92,55,20) | 과정/프로세스 섹션 제목 |
+| `--color-dark-teal` | `#084C56` | rgba(8,76,86) | 결과/디지털 섹션 제목 |
+| `--color-teal` | `#227582` | rgba(34,117,130) | 설명 텍스트 |
+| `--color-forest` | `#548235` | rgba(84,130,53) | 배경 그라디언트 |
+| `--color-beige` | `#E4D9C0` | rgba(228,217,192) | 서브틀 배경/버튼 |
+| `--color-warm-yellow` | `#FAEDCB` | rgba(250,237,203) | 하이라이트 바 |
+
+### 그라디언트 패턴
+- 왼쪽(과정): `rgba(165,161,150,0.10) → rgba(57,50,30,1.00)` (베이지→브라운)
+- 오른쪽(결과): `rgba(41,107,85,0.10) → rgba(3,33,24,1.00)` (틸→다크)
+- 버튼: `rgba(255,255,255,0.00) → rgba(228,217,192,1.00)` (투명→베이지)
+- 배경: `rgba(84,130,53,1.00) → rgba(37,62,31,0.00)` (그린→투명)
+
+## 4. 타이포그래피
+
+- **폰트**: Pretendard Variable 유지 (Noto Sans KR은 이미 fallback)
+- **핵심은 크기/굵기 위계**
+
+| 레벨 | 크기 (슬라이드) | Weight | 스트로크 | 정렬 |
+|------|---------------|--------|---------|------|
+| Hero Statement | 24-28px | 700 | white 1.5px | center |
+| Section Header | 28-35px | 900 | white 5px | center/left |
+| Badge Title | 22-26px | 700 | 없음 | center |
+| Card Title (EN) | 20-22px | 900 | white 5px | center |
+| Card Subtitle (KR) | 14-18px | 500 | white 1.5px | center |
+| Body Text | 16-20px | 700 | white 1px | left |
+| Section Sub-title | 22-26px | 900 | 없음 | left |
+
+### 텍스트 스트로크 기법
+Figma 디자인의 특징: 다양한 배경 위에서 가독성 확보를 위해 **흰색 스트로크** 사용
+```css
+-webkit-text-stroke: 1.5px white; /* 일반 텍스트 */
+-webkit-text-stroke: 5px white; /* 강조 텍스트 */
+paint-order: stroke fill; /* 스트로크가 텍스트 뒤로 */
+```
+
+## 5. 레이아웃 패턴
+
+### A. Badge Header
+- 이미지/그라디언트 배경 위 `border-radius: 20px` 바
+- 중앙 흰색 텍스트 (50px/700 → 22-26px/700)
+- 높이: ~88px (Figma) → ~44-50px (슬라이드)
+
+### B. Hero Statement
+- 전체 폭 중앙 정렬
+- 큰 텍스트 (60px/700) + 흰색 스트로크
+- 키워드 **굵은 강조** 가능
+
+### C. Icon Card Row
+- N개 카드 수평 배치, 세로 구분선
+- 각 카드: 아이콘 이미지 + 영문 제목(900) + 한국어 부제(500)
+- 흰색 둥근 컨테이너 (borderRadius: 20)
+
+### D. Two-Col Comparison
+- 좌/우 그라디언트 배경
+- 각 열: 헤더 바 + (섹션 제목 + 본문) × N개
+- 색상으로 좌/우 구분 (브라운 vs 틸)
+
+### E. CTA Button
+- 그라디언트 바 (투명→베이지) + 둥근 버튼 (r:7)
+- 흰색 텍스트
+
+## 6. 디자인 시스템 vs 콘텐츠 전용 경계
+
+### 디자인 시스템 (블록에 포함)
+- 색상 팔레트, 그라디언트 패턴
+- 타이포그래피 위계, 텍스트 스트로크
+- 둥근 모서리 컨테이너 (r:20)
+- Badge Header, 2열 비교, N열 카드 레이아웃 구조
+
+### 콘텐츠 전용 (블록에 포함하지 않음)
+- 3D 화살표 이미지 (Frame 1) → 콘텐츠가 제공
+- 특정 아이콘 이미지들 (brain, thunder 등) → 콘텐츠가 제공
+- 도메인 텍스트 → 슬롯으로 처리
diff --git a/FIGMA-EXTRACTION.md b/FIGMA-EXTRACTION.md
new file mode 100644
index 0000000..6592f1c
--- /dev/null
+++ b/FIGMA-EXTRACTION.md
@@ -0,0 +1,441 @@
+# Figma → HTML 블록 변환 프로세스
+
+> 2026-04-07 확립. Figma 디자인을 design_agent 블록으로 변환하는 정확한 방법론.
+
+---
+
+## 1. 전체 워크플로우
+
+```
+[Step 1] Figma API로 파일 구조 추출
+ ↓
+[Step 2] 프레임별 렌더링 이미지(PNG) 다운로드
+ ↓
+[Step 3] 노드별 상세 데이터 추출 (좌표, 색상, 폰트, 크기)
+ ↓
+[Step 4] 디자인 언어 분석 (공통 패턴 vs 콘텐츠 전용 구분)
+ ↓
+[Step 5] 블록 설계 (슬롯, 동적 규칙, schema)
+ ↓
+[Step 6] 수학적 계산 (Figma 좌표 → 스케일 → CSS값)
+ ↓
+[Step 7] HTML/CSS 구현
+ ↓
+[Step 8] 비교 리뷰 (Figma PNG vs HTML, 같은 폭으로 위/아래 배치)
+ ↓
+[Step 9] 피드백 반영 → Step 6~8 반복
+ ↓
+[Step 10] Jinja2 템플릿화 + catalog.yaml 등록
+```
+
+---
+
+## 2. Figma API 사용법
+
+### 2.1 파일 구조 가져오기
+```bash
+curl -s -H "X-Figma-Token: {TOKEN}" \
+ "https://api.figma.com/v1/files/{FILE_KEY}" \
+ | python -m json.tool
+```
+
+### 2.2 특정 노드 상세 데이터
+```bash
+curl -s -H "X-Figma-Token: {TOKEN}" \
+ "https://api.figma.com/v1/files/{FILE_KEY}/nodes?ids={NODE_IDS}&geometry=paths"
+```
+
+### 2.3 노드 이미지 렌더링 (PNG)
+```bash
+curl -s -H "X-Figma-Token: {TOKEN}" \
+ "https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_IDS}&format=png&scale=2"
+```
+- `scale=2`: 2배 해상도로 다운로드 (선명도 확보)
+- 응답의 `images` 객체에 각 노드 ID별 S3 URL 제공
+
+### 2.4 추출해야 하는 핵심 데이터
+
+| 데이터 | API 필드 | 용도 |
+|-------|---------|------|
+| 위치 | `absoluteBoundingBox.x, .y` | 요소 간 관계 계산 |
+| 크기 | `absoluteBoundingBox.width, .height` | 스케일 계산 |
+| 텍스트 | `characters` | 콘텐츠 확인 |
+| 폰트 | `style.fontFamily, .fontSize, .fontWeight` | 타이포그래피 |
+| 색상 | `fills[].color` | 색상 팔레트 |
+| 테두리 | `strokes[], strokeWeight` | 박스 스타일 |
+| 라운드 | `cornerRadius` | border-radius |
+| 이미지 | `fills[].imageRef` | 이미지 자산 식별 |
+
+---
+
+## 3. 수학적 계산 (핵심)
+
+### 3.1 스케일 팩터
+
+```
+슬라이드 콘텐츠 폭 = 1280px - padding(40px × 2) = 1200px
+
+scale = 1200 / figma_frame_width
+```
+
+| Figma 프레임 | 폭 | 스케일 |
+|-------------|-----|--------|
+| Frame 2 | 1808px | 0.6637 |
+| Frame 3 | 1807px | 0.6641 |
+| Frame 4 | 3848px | 0.3118 |
+
+### 3.2 요소 간 정렬 계산
+
+**절대 원칙: Figma 좌표 차이값 → 스케일 적용 → CSS값**
+
+```python
+# 예: 리본 접힘선과 박스 테두리 정렬
+badge_y = 1431 # Figma에서 badge 이미지 top Y
+box_y = 1449 # Figma에서 box top Y
+fold_offset = box_y - badge_y # = 18px (Figma 기준)
+
+# 스케일 적용
+fold_offset_css = round(fold_offset * scale) # = 12px (CSS)
+```
+
+**금지: "좀 더 올려볼게요" 식의 시행착오 px 조정**
+
+### 3.3 이미지 자산 크기 계산
+
+```python
+# Figma 원본 크기에 스케일 적용
+ribbon_width_css = round(badge_img_width * scale)
+ribbon_height_css = round(badge_img_height * scale)
+
+# 비율 계산 (CSS에서 width만 지정하면 height는 자동)
+aspect_ratio = badge_img_width / badge_img_height
+```
+
+### 3.4 패딩/여백 계산
+
+```python
+# 리본이 박스 안에 들어오는 높이 = 리본 전체 높이 - 접힘선 오프셋
+ribbon_inside_box = ribbon_height_css - fold_offset_css
+
+# 박스 상단 패딩 = 리본 침입 높이 + 여유
+box_padding_top = ribbon_inside_box + 6 # 6px 여유
+```
+
+### 3.5 실제 계산 예시 (Frame 2)
+
+```
+입력 (Figma 원본):
+ badge 이미지: 508×94px, y=1431
+ box: y=1449
+ frame width: 1808px
+
+계산:
+ scale = 1200/1808 = 0.6637
+ ribbon_w = 508 × 0.6637 = 337px
+ ribbon_h = 94 × 0.6637 = 62px
+ fold_offset = (1449-1431) × 0.6637 = 12px
+ ribbon_below_fold = 62 - 12 = 50px
+ box_padding_top = 50 + 6 = 56px
+
+CSS 출력:
+ .ribbon { width: 337px; top: -12px; }
+ .box { padding-top: 56px; }
+```
+
+---
+
+## 4. 이미지 자산 처리
+
+### 4.1 CSS로 만들면 안 되는 것
+
+| 요소 | 이유 | 처리 |
+|------|------|------|
+| 3D 리본/두루마리 | 입체감, 그림자, 곡면 → CSS 불가 | Figma에서 PNG 추출 |
+| 복잡한 그라디언트 배경 | 다중 정지점, 비선형 → CSS 근사 불가 | 이미지 사용 |
+| 아이콘 이미지 | 디자이너가 만든 고유 자산 | 원본 이미지 사용 |
+
+### 4.2 CSS로 만들 수 있는 것
+
+| 요소 | CSS 구현 |
+|------|---------|
+| 단색/2색 그라디언트 배경 | `linear-gradient()` |
+| 둥근 모서리 테두리 박스 | `border + border-radius` |
+| 텍스트 스타일 | `font-size, font-weight, color` |
+| 그리드/플렉스 레이아웃 | `display: grid / flex` |
+| 구분선 | `border` or `background` |
+
+### 4.3 이미지 추출 및 저장
+
+```bash
+# Figma API로 특정 노드 이미지 추출
+curl -s -H "X-Figma-Token: {TOKEN}" \
+ "https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_ID}&format=png&scale=2"
+
+# 다운로드 → static/figma-assets/ 에 저장
+curl -s -o static/figma-assets/{name}.png "{S3_URL}"
+```
+
+저장 위치: `static/figma-assets/`
+
+---
+
+## 5. 비교 리뷰 페이지 작성법
+
+### 5.1 레이아웃
+
+```
+같은 폭으로 위/아래 배치 (좌/우 아님 — 크기 차이 문제)
+
+┌─ 빨간 테두리 ──────────────┐
+│ Figma Original (PNG) │
+└─────────────────────────────┘
+ ─ 구분선 ─
+┌─ 초록 테두리 ──────────────┐
+│ HTML Block │
+└─────────────────────────────┘
+```
+
+### 5.2 HTML 스케일링
+
+```css
+.html-inner {
+ width: 1280px; /* 슬라이드 원본 크기 */
+ transform-origin: top left;
+ transform: scale(0.74); /* 960px 컨테이너에 맞춤: 960/1280 */
+}
+```
+
+### 5.3 비교 리뷰 파일 위치
+
+`data/figma_ref/comparison.html`
+
+---
+
+## 6. Jinja2 템플릿 변환 규칙
+
+### 6.1 고정값 → 변수
+
+```html
+
+정책 달성 → {{ badge_title }}
+Engn. Solution → {{ left_title }}
+```
+
+### 6.2 반복 요소 → 루프
+
+```html
+
+{% for card in cards %}
+
+```
+
+---
+
+## 7. 디자인 언어 vs 콘텐츠 전용 구분
+
+### 디자인 언어 (블록에 포함, 재사용 가능)
+- 색상 팔레트 (warm 테마: 브라운, 틸, 베이지)
+- 타이포그래피 위계 (크기, 굵기 단계)
+- 레이아웃 구조 (2열 비교, N열 카드 등)
+- 장식 요소 (3D 리본, 둥근 컨테이너)
+
+### 콘텐츠 전용 (블록에 포함하지 않음)
+- 특정 텍스트 ("디지털전환은 사용자...")
+- 특정 아이콘 이미지 (brain, thunder 등)
+- 도메인 전문 용어 (DfMA, Engn. Solution)
+
+---
+
+## 8. 파일 구조
+
+```
+design_agent/
+├── static/figma-assets/ ← Figma에서 추출한 이미지 자산
+│ ├── badge_policy.png (틸 3D 리본)
+│ ├── badge_solution.png (빨간 3D 리본)
+│ ├── box_policy_container.png
+│ └── box_solution_cards.png
+├── data/figma_ref/ ← 비교 리뷰용
+│ ├── comparison.html (Figma vs HTML 비교 페이지)
+│ ├── frame2_1-5.png (Figma 원본 PNG)
+│ ├── frame3_1-35.png
+│ └── frame4_1-49.png
+├── templates/blocks/cards/ ← 블록 템플릿
+│ ├── hero-icon-cards.html
+│ ├── compare-2col-badge.html
+│ └── compare-detail-gradient.html
+├── FIGMA-DESIGN-LANGUAGE.md ← 디자인 언어 분석 결과
+├── FIGMA-EXTRACTION.md ← 이 문서
+└── PHASE-FIGMA-BLOCKS.md ← 블록 설계 명세
+```
+
+---
+
+## 9. 고급 레이아웃 패턴
+
+### 9.1 좌/우 열 섹션 Y선 정렬 (CSS Grid 행 공유)
+
+2열 비교에서 좌/우 섹션 제목이 같은 Y선에 있어야 할 때:
+
+**문제**: 각 열을 독립 flex-column으로 만들면, 좌측 섹션 본문이 길면 우측 다음 섹션이 밀림.
+```
+flex-column (잘못):
+ 좌: [제목1] [긴본문] [제목2]
+ 우: [제목1] [짧은본문] [제목2] ← 제목2가 좌측과 Y가 다름
+```
+
+**해결**: CSS Grid 2열 × N행으로 행을 공유하면 자동 정렬.
+```css
+.block {
+ display: grid;
+ grid-template-columns: 1fr 1fr; /* 2열 */
+ grid-template-rows: auto auto auto auto; /* 헤더 + N행 */
+}
+```
+```
+Grid (올바름):
+ [좌 헤더] [우 헤더] ← Row 0
+ [좌 섹션1] [우 섹션1] ← Row 1 (행 높이 = max(좌,우))
+ [좌 섹션2] [우 섹션2] ← Row 2 (Y선 자동 정렬!)
+```
+
+**실제 계산 (Frame 4)**:
+```
+Figma Y좌표:
+ Row 1: 좌 1166, 우 1166 → 0px 차이 (이미 정렬)
+ Row 2: 좌 1529, 우 1467 → 62px 차이 (Grid가 해결)
+ Row 3: 좌 1845, 우 1845 → 0px 차이 (이미 정렬)
+원인: Row 1 좌측에 As-Is→To-Be 구조가 있어서 본문이 62px 더 높음
+```
+
+### 9.2 As-Is → To-Be 수평 서브 레이아웃
+
+한 섹션 안에서 변환 전/후를 수평 배치할 때:
+
+```html
+
+
+
+
+
+```
+```css
+.asis-tobe { display: flex; align-items: center; gap: 8px; }
+.asis, .tobe { flex: 1; }
+.arrow { width: 60px; height: auto; flex-shrink: 0; }
+```
+
+**Figma 좌표로 검증**:
+```
+As-Is: x=2737, w=539
+Arrow: x=3375, w=252
+To-Be: x=3687, w=672
+→ 세 요소가 같은 Y(1269)에 수평 배치됨을 좌표로 확인
+```
+
+### 9.3 3D 리본/두루마리 배지 정렬 공식
+
+리본 이미지의 접힘선(fold-back)이 박스 테두리와 정확히 일치해야 할 때:
+
+```
+┌── 리본 이미지 ──────────────┐
+│ 접힘 삼각형 (fold) │ ← fold_offset (이미지 top에서)
+│ 리본 본체 │
+│ │
+└──────────────────────────────┘
+════════════════════════════════ ← 박스 top border (여기에 fold가 일치해야 함)
+┌── 박스 ──────────────────────┐
+│ padding-top = ribbon_below │
+│ 콘텐츠 시작 │
+
+계산:
+ fold_offset = (box_y - badge_y) × scale → CSS: top 값
+ ribbon_below = ribbon_height - fold_offset → 박스 안 침입 높이
+ box_padding_top = ribbon_below + 여유(6px) → 콘텐츠 겹침 방지
+```
+
+**핵심**: 리본을 올리거나 내리는 게 아니라, **박스의 위치를 계산**하는 것.
+- `top: -fold_offset` → 리본 접힘선 = 박스 top border
+- 리본은 그대로, 박스와의 관계만 수학적으로 결정
+
+---
+
+## 10. 실수 방지 (Anti-patterns)
+
+### 10.1 절대 하면 안 되는 것
+
+| Anti-pattern | 왜 안 되는지 | 올바른 방법 |
+|-------------|------------|-----------|
+| px 시행착오 조정 ("좀 더 올려볼게") | 3번 이상 실패, 시간 낭비 | Figma 좌표에서 수학적 계산 |
+| 3D 효과를 CSS로 재현 | 평면적이라 품질 차이 심각 | Figma에서 PNG 추출 |
+| 비교 리뷰를 좌/우 배치 | 크기 차이로 비교 불가 | 위/아래 같은 폭으로 배치 |
+| Jinja2 템플릿을 브라우저에서 직접 열기 | 변수 미렌더, 이미지 경로 깨짐 | comparison.html 또는 FastAPI로 확인 |
+| 독립 flex-column으로 2열 비교 | 행 정렬 안 됨 | CSS Grid 행 공유 |
+| 느낌으로 폰트/색상 설정 | Figma와 다른 결과물 | Figma API에서 정확한 값 추출 |
+
+### 10.2 반드시 해야 하는 것
+
+| 원칙 | 이유 |
+|------|------|
+| CSS 주석에 계산 근거 기록 | 나중에 왜 이 값인지 추적 가능 |
+| 비교 리뷰 후 진행 | 디자인 차이를 사전에 발견 |
+| 이미지 자산은 `static/figma-assets/`에 저장 | FastAPI가 서빙, 경로 일관성 |
+| `comparison.html`에 모든 프레임 포함 | 한 페이지에서 전체 리뷰 가능 |
+| Figma 노드 ID 기록 | 나중에 업데이트된 디자인 재추출 가능 |
+
+---
+
+## 11. Figma 소스 정보
+
+### 현재 등록된 Figma 파일
+
+| 항목 | 값 |
+|------|---|
+| File Key | `9S6LsQyO6zlRxtiqZccOUM` |
+| Page | Page 1 (0:1) |
+| Frame 2 (hero-icon-cards) | Node `1:5` |
+| Frame 3 (compare-2col-badge) | Node `1:35` |
+| Frame 4 (compare-detail-gradient) | Node `1:49` |
+| Badge (빨간 리본) | Node `1:33` (image 4019) |
+| Badge (틸 리본) | Node `1:43` (image 2197) |
+| Arrow (As-Is→To-Be) | Node `1:67` (image 2645) |
+| Box (빨간 테두리) | Node `1:12` (Rectangle 42894) |
+| Box (틸 테두리) | Node `1:37` (Rectangle 42598) |
+
+---
+
+## 12. 체크리스트
+
+새 Figma 프레임을 블록으로 변환할 때:
+
+- [ ] Figma API로 노드 데이터 추출 (좌표, 크기, 색상, 폰트)
+- [ ] PNG 렌더링 다운로드 (scale=2)
+- [ ] 복잡한 비주얼 요소 식별 → 이미지로 추출 (CSS로 만들지 않음)
+- [ ] 스케일 팩터 계산 (1200 / frame_width)
+- [ ] 핵심 정렬 포인트 수학적 계산 (좌표 차이 × 스케일)
+- [ ] CSS 값 도출 (계산 근거를 주석으로 기록)
+- [ ] 비교 리뷰 페이지에 추가 (위/아래 같은 폭)
+- [ ] 사용자 피드백 확인
+- [ ] Jinja2 템플릿 변환 (고정값→변수, 반복→루프)
+- [ ] catalog.yaml 등록
diff --git a/PHASE-FIGMA-BLOCKS.md b/PHASE-FIGMA-BLOCKS.md
new file mode 100644
index 0000000..01503a3
--- /dev/null
+++ b/PHASE-FIGMA-BLOCKS.md
@@ -0,0 +1,463 @@
+# Phase 2: Figma Block Design Specification
+
+> 3개 블록 + 2개 서브 컴포넌트 상세 설계
+> 기준: FIGMA-DESIGN-LANGUAGE.md 분석 결과
+
+---
+
+## Block 1: `hero-icon-cards`
+
+### 1.1 시각적 구조
+
+```
+┌──────────────────────────────────────────────┐
+│ [Hero Statement - 큰 텍스트, 중앙] │ ← zone: header or full-width
+│ │
+│ ┌─[Badge Title]─┐ │
+│──────────┤ ├───────────────────│
+│ ┌─────┐ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
+│ │icon │ │ │icon │ │icon │ │icon │ │icon │ │ ← N개 카드 (2~6)
+│ │ │ │ │ │ │ │ │ │ │ │ │
+│ │Title│ ╎ │Title│ │Title│ │Title│ │Title│ │ ← 세로 구분선
+│ │(sub)│ │ │(sub)│ │(sub)│ │(sub)│ │(sub)│ │
+│ └─────┘ │ └─────┘ └─────┘ └─────┘ └─────┘ │
+│ └───────────────────────────────────│
+└──────────────────────────────────────────────┘
+```
+
+### 1.2 슬롯 정의
+
+| 슬롯 | 필수 | 타입 | 설명 |
+|------|------|------|------|
+| `statement` | O | string | Hero 메시지 (1-2줄) |
+| `badge_title` | X | string | 배지 바 텍스트 |
+| `cards[]` | O | array | 카드 배열 |
+| `cards[].icon` | X | string | 아이콘 이미지 URL 또는 이모지 |
+| `cards[].title` | O | string | 영문 또는 주제목 |
+| `cards[].subtitle` | X | string | 한국어 부제 |
+| `cards[].color` | X | string | 개별 카드 강조색 |
+
+### 1.3 동적 재구성 규칙
+
+#### 그리드 계산
+```
+입력: N = cards.length, W = container_width_px
+
+N ≤ 5: 1행 N열
+ col_count = N
+ card_width = (W - padding*2 - gap*(N-1)) / N
+
+N = 6: 1행 6열 (gap 축소)
+ col_count = 6
+ gap = 8px (기본 16px에서 축소)
+
+N > 6: 2행
+ col_count = ceil(N / 2)
+ row_count = 2
+```
+
+#### 폰트 스케일링
+```
+card_width ≥ 200px → title: 20px, subtitle: 14px
+card_width ≥ 150px → title: 16px, subtitle: 12px
+card_width < 150px → title: 14px, subtitle: 11px
+```
+
+#### 높이 계산
+```
+hero_height = statement_lines * line_height + padding
+badge_height = 44px (고정)
+card_area_height = icon_height + title_lines * title_lh + subtitle_lh + padding
+ - 1행: card_area_height
+ - 2행: card_area_height * 2 + gap
+
+total_min_height = hero_height + badge_height + card_area_height + gaps
+```
+
+### 1.4 catalog.yaml schema
+
+```yaml
+- id: hero-icon-cards
+ name: 히어로 문구 + 아이콘 카드
+ category: cards
+ template: blocks/cards/hero-icon-cards.html
+ height_cost: xlarge
+ min_height_px: 280
+ relation_types: [definition, flow]
+ min_items: 2
+ max_items: 6
+ visual: >
+ 상단에 큰 Hero 메시지(24px bold, 중앙) + 배지 바 +
+ 하단에 N열 아이콘 카드(둥근 흰색 컨테이너, 세로 구분선).
+ 각 카드는 아이콘 이미지 + 영문 제목(20px/900) + 한국어 부제(14px/500).
+ when: >
+ 핵심 목표나 가치를 N개 키워드로 선언할 때.
+ 각 키워드에 아이콘이나 이미지가 있을 때.
+ "우리가 추구하는 5가지 가치" 같은 구조.
+ not_for: >
+ 비교/대조 구조 → compare-2col-badge.
+ 상세 설명이 길 때 → card-icon-desc.
+ 순서/단계 → card-step-vertical 또는 process-horizontal.
+ purpose_fit: [핵심전달, 가치선언]
+ zone: full-width-only
+ slots:
+ required: [statement, cards[]]
+ optional: [badge_title, cards[].icon, cards[].subtitle, cards[].color]
+ schema:
+ statement:
+ max_lines: 2
+ font_size: 24
+ ref_chars:
+ body: 60
+ note: "24px bold, 중앙정렬, 흰색 스트로크"
+ badge_title:
+ max_lines: 1
+ font_size: 18
+ ref_chars:
+ body: 20
+ note: "18px bold white, 배지 바 위"
+ card_title:
+ max_lines: 2
+ font_size: 20
+ ref_chars:
+ body: 15
+ note: "20px black/900, 중앙정렬"
+ card_subtitle:
+ max_lines: 1
+ font_size: 14
+ ref_chars:
+ body: 10
+ note: "14px medium, 한국어 부제"
+ padding_overhead_px: 60
+ padding_h_px: 32
+```
+
+---
+
+## Block 2: `compare-2col-badge`
+
+### 2.1 시각적 구조
+
+```
+┌──────────────────────────────────────────────┐
+│ ┌─[Badge Title]─┐ │
+│────────────┤ ├─────────────────│
+│ │
+│ ┌── Left Column ──┐ ╎ ┌── Right Column ──┐ │
+│ │ │ ╎ │ │ │
+│ │ [Big Title] │ ╎ │ [Big Title] │ │
+│ │ │ ╎ │ │ │
+│ │ body text... │ ╎ │ body text... │ │
+│ │ body text... │ ╎ │ body text... │ │
+│ │ │ ╎ │ │ │
+│ └──────────────────┘ ╎ └──────────────────┘ │
+│ │
+│ [Optional: Hero Statement] │
+└──────────────────────────────────────────────┘
+```
+
+### 2.2 슬롯 정의
+
+| 슬롯 | 필수 | 타입 | 설명 |
+|------|------|------|------|
+| `badge_title` | O | string | 배지 바 텍스트 |
+| `left_title` | O | string | 좌측 열 대제목 |
+| `left_body` | O | string | 좌측 열 본문 |
+| `right_title` | O | string | 우측 열 대제목 |
+| `right_body` | O | string | 우측 열 본문 |
+| `statement` | X | string | 하단 Hero 메시지 |
+| `left_color` | X | string | 좌측 강조색 (기본: --color-teal) |
+| `right_color` | X | string | 우측 강조색 (기본: --color-teal) |
+
+### 2.3 동적 재구성 규칙
+
+#### 레이아웃 계산
+```
+container_width = 컨테이너 전체 폭
+padding_h = 32px * 2
+
+2열 모드 (기본):
+ col_width = (container_width - padding_h - divider_gap) / 2
+ divider_gap = 32px
+
+1열 모드 (sidebar zone, 폭 < 500px):
+ 좌/우가 세로 스택
+ col_width = container_width - padding_h
+```
+
+#### 높이 계산
+```
+badge_height = 44px
+left_height = title_height + body_lines * line_height + padding
+right_height = title_height + body_lines * line_height + padding
+content_height = max(left_height, right_height)
+statement_height = statement ? (statement_lines * 28 + 16) : 0
+
+total = badge_height + content_height + statement_height + gaps
+```
+
+#### 텍스트 피팅
+```
+col_width에 따른 body 글자수 제한:
+ col_width ≥ 500px → ~40자/줄, font: 16px
+ col_width ≥ 350px → ~28자/줄, font: 14px
+ col_width < 350px → ~20자/줄, font: 13px
+```
+
+### 2.4 catalog.yaml schema
+
+```yaml
+- id: compare-2col-badge
+ name: 배지 헤더 2열 비교
+ category: cards
+ template: blocks/cards/compare-2col-badge.html
+ height_cost: large
+ min_height_px: 200
+ relation_types: [comparison, contrast]
+ visual: >
+ 상단 배지 바(이미지/그라디언트 배경 + 흰색 텍스트) 아래
+ 2열 비교 레이아웃. 좌/우 각각 대제목(24px/900) + 본문(16px/700).
+ 중앙 세로 구분선. 둥근 흰색 컨테이너(r:20).
+ 선택적 하단 Hero 메시지.
+ when: >
+ 두 개념/방법/전략을 나란히 비교할 때.
+ 배지 헤더로 상위 주제를 명시.
+ 예: "Engn. Solution vs DfMA", "현재 vs 미래"
+ not_for: >
+ 3개 이상 항목 비교 → compare-3col-badge.
+ 장/단점 목록 → comparison-2col.
+ 상세 내용이 길고 섹션이 많을 때 → compare-detail-gradient.
+ purpose_fit: [비교대조, 개념정의]
+ zone: full-width-only
+ slots:
+ required: [badge_title, left_title, left_body, right_title, right_body]
+ optional: [statement, left_color, right_color]
+ schema:
+ badge_title:
+ max_lines: 1
+ font_size: 18
+ ref_chars:
+ body: 15
+ note: "18px bold white, 배지 바"
+ left_title:
+ max_lines: 1
+ font_size: 24
+ ref_chars:
+ body: 15
+ note: "24px black/900, 흰색 스트로크"
+ left_body:
+ max_lines: 6
+ font_size: 16
+ ref_chars:
+ body: 200
+ note: "16px/700, 틸 색상"
+ right_title:
+ max_lines: 1
+ font_size: 24
+ ref_chars:
+ body: 15
+ note: "24px black/900, 흰색 스트로크"
+ right_body:
+ max_lines: 6
+ font_size: 16
+ ref_chars:
+ body: 200
+ note: "16px/700, 틸 색상"
+ statement:
+ max_lines: 2
+ font_size: 20
+ ref_chars:
+ body: 50
+ note: "20px bold, 중앙정렬"
+ padding_overhead_px: 56
+ padding_h_px: 32
+```
+
+---
+
+## Block 3: `compare-detail-gradient`
+
+### 3.1 시각적 구조
+
+```
+┌──────────────────────────────────────────────────────────┐
+│ ┌───── Left Header Bar (gradient) ─────┐┌── Right ─────┐│
+│ │ [Left Column Title] ││ [Right Title] ││
+│ └──────────────────────────────────────┘└───────────────┘│
+│ ┌─────── Left BG (warm) ──────┐┌──── Right BG (teal) ──┐│
+│ │ ││ ││
+│ │ [Section 1 Title] ││ [Section 1 Title] ││
+│ │ • body text ││ • body text ││
+│ │ • body text ││ • body text ││
+│ │ ││ ││
+│ │ [Section 2 Title] ││ [Section 2 Title] ││
+│ │ • body text ││ • body text ││
+│ │ • body text ││ • body text ││
+│ │ ││ ││
+│ │ [Section N Title] ││ [Section M Title] ││
+│ │ • body text ││ • body text ││
+│ └──────────────────────────────┘└───────────────────────┘│
+└──────────────────────────────────────────────────────────┘
+```
+
+### 3.2 슬롯 정의
+
+| 슬롯 | 필수 | 타입 | 설명 |
+|------|------|------|------|
+| `left_header` | O | string | 좌측 열 헤더 타이틀 |
+| `right_header` | O | string | 우측 열 헤더 타이틀 |
+| `left_sections[]` | O | array | 좌측 섹션 배열 |
+| `left_sections[].title` | O | string | 섹션 소제목 |
+| `left_sections[].body` | O | string | 섹션 본문 (줄바꿈 허용) |
+| `right_sections[]` | O | array | 우측 섹션 배열 |
+| `right_sections[].title` | O | string | 섹션 소제목 |
+| `right_sections[].body` | O | string | 섹션 본문 |
+| `left_color_theme` | X | string | 좌측 테마 (기본: warm) |
+| `right_color_theme` | X | string | 우측 테마 (기본: teal) |
+
+### 3.3 동적 재구성 규칙 (★ 가장 수학적으로 복잡)
+
+#### 그리드 계산
+```
+container_width에서 2열 분할:
+ col_width = (container_width - gap) / 2
+ gap = 0px (그라디언트가 맞닿음)
+```
+
+#### 섹션 높이 계산 (핵심)
+```
+header_bar_height = 48px (고정)
+
+각 섹션의 높이:
+ section_height(s) =
+ title_height(s.title, title_font_size, col_width) +
+ body_height(s.body, body_font_size, col_width) +
+ section_padding
+
+ title_height = ceil(char_count / chars_per_line) * title_line_height
+ body_height = line_count * body_line_height
+
+ chars_per_line = floor(col_width / (font_size * 0.55)) // 한글 평균 0.55em
+
+좌측 전체:
+ left_total = header_bar + sum(section_height for s in left_sections) + gaps
+
+우측 전체:
+ right_total = header_bar + sum(section_height for s in right_sections) + gaps
+
+content_height = max(left_total, right_total)
+```
+
+#### 오버플로 방지 — Fit 검증
+```
+if content_height > container_available_height:
+
+ 전략 1: 폰트 축소
+ body_font_size -= 1px (최소 12px)
+ 재계산
+
+ 전략 2: 섹션 본문 줄 수 제한
+ max_body_lines = floor(
+ (available_per_section - title_height) / body_line_height
+ )
+ available_per_section = (container_height - header*2 - gaps) / max(N_left, N_right)
+
+ 전략 3: Kei 에스컬레이션 (기존 파이프라인)
+ content 요약 요청
+```
+
+#### 색상 테마 매핑
+```
+warm (좌측 기본):
+ header_gradient: rgba(165,161,150,0.10) → rgba(57,50,30,1.00)
+ section_title_color: var(--color-warm-brown)
+ bg: rgba(255,255,255,0.30) → rgba(57,50,30,0.30)
+
+teal (우측 기본):
+ header_gradient: rgba(41,107,85,0.10) → rgba(3,33,24,1.00)
+ section_title_color: var(--color-dark-teal)
+ bg: rgba(41,107,85,0.30) → rgba(255,255,255,0.30)
+```
+
+### 3.4 catalog.yaml schema
+
+```yaml
+- id: compare-detail-gradient
+ name: 그라디언트 상세 2열 비교
+ category: cards
+ template: blocks/cards/compare-detail-gradient.html
+ height_cost: xlarge
+ min_height_px: 300
+ relation_types: [comparison, contrast, process]
+ min_items: 2 # 좌/우 최소 1섹션씩
+ max_items: 10 # 좌+우 합계
+ visual: >
+ 좌우 그라디언트 배경(워 브라운 vs 다크틸)으로 나뉜 2열 비교.
+ 각 열 상단에 그라디언트 헤더 바 + 큰 제목(28px/900).
+ 하단에 N개 섹션(소제목 22px/900 + 본문 16px/700) 반복.
+ 좌측은 따뜻한 톤(과정/As-Is), 우측은 차가운 톤(결과/To-Be).
+ when: >
+ 두 카테고리를 상세하게 비교할 때.
+ 각 카테고리에 여러 하위 항목이 있을 때.
+ 과정 vs 결과, As-Is vs To-Be, 문제 vs 해결 구조.
+ not_for: >
+ 간단한 2항목 비교(본문 짧을 때) → compare-2col-badge.
+ 3열 비교 → compare-3col-badge.
+ 비교가 아닌 단독 리스트 → dark-bullet-list.
+ purpose_fit: [비교대조, 구조시각화, 근거사례]
+ zone: full-width-only
+ slots:
+ required: [left_header, right_header, left_sections[], right_sections[]]
+ optional: [left_color_theme, right_color_theme]
+ schema:
+ left_header:
+ max_lines: 1
+ font_size: 28
+ ref_chars:
+ body: 20
+ note: "28px black/900, 그라디언트 바 위"
+ right_header:
+ max_lines: 1
+ font_size: 28
+ ref_chars:
+ body: 20
+ note: "28px black/900, 그라디언트 바 위"
+ section_title:
+ max_lines: 2
+ font_size: 22
+ ref_chars:
+ body: 30
+ note: "22px/900, 색상 테마별 (브라운 or 틸)"
+ section_body:
+ max_lines: 4
+ font_size: 16
+ ref_chars:
+ body: 120
+ note: "16px/700, black"
+ padding_overhead_px: 48
+ padding_h_px: 0
+```
+
+---
+
+## 서브 컴포넌트
+
+### S1. 장식 이미지 (3D 화살표 등)
+- 블록이 아닌 **콘텐츠 이미지**로 처리
+- `cards[].icon` 또는 별도 `decoration_image` 슬롯으로 전달
+- 블록은 `
` 태그로 렌더링, 크기는 CSS로 컨테이너에 맞춤
+
+### S2. CTA 버튼
+- 독립 블록이 아닌 **다른 블록 내 선택적 요소**
+- `cta_text` 슬롯으로 전달 (없으면 미표시)
+- CSS: 그라디언트 바 + 둥근 버튼 (r:7)
+
+---
+
+## 구현 우선순위
+
+| 순서 | 블록 | 이유 |
+|------|------|------|
+| 1 (파일럿) | `compare-2col-badge` | 중간 복잡도, 기존 compare-2col-split과 비교 검증 가능 |
+| 2 | `hero-icon-cards` | N개 카드 그리드 계산 필요, 파일럿 경험 활용 |
+| 3 | `compare-detail-gradient` | 가장 복잡 (N개 섹션 × 2열, 높이 균형, 오버플로 방지) |
diff --git a/scripts/figma_to_html.py b/scripts/figma_to_html.py
new file mode 100644
index 0000000..e26a7c7
--- /dev/null
+++ b/scripts/figma_to_html.py
@@ -0,0 +1,261 @@
+"""Figma 프레임을 HTML/CSS로 변환.
+
+기존 블록 템플릿(templates/blocks/)과 동일한 방식으로
+Figma API 데이터에서 정확한 HTML을 생성한다.
+
+Usage:
+ python scripts/figma_to_html.py data/runs/figma_beps_full.json templates/blocks/BEPs/
+"""
+from __future__ import annotations
+import json
+import math
+import sys
+from pathlib import Path
+
+
+def get_fill_css(fills: list[dict]) -> tuple[str, str]:
+ """fills 배열에서 CSS background와 color를 추출.
+
+ Returns: (background_css, color_css)
+ """
+ bg = ""
+ color = ""
+ for f in fills:
+ if not f.get("visible", True):
+ continue
+ ftype = f.get("type", "")
+ if ftype == "SOLID":
+ c = f["color"]
+ r, g, b = int(c["r"] * 255), int(c["g"] * 255), int(c["b"] * 255)
+ a = f.get("opacity", c.get("a", 1))
+ if a < 0.99:
+ val = f"rgba({r},{g},{b},{a:.2f})"
+ else:
+ val = f"#{r:02x}{g:02x}{b:02x}"
+ bg = val
+ color = val
+ elif "GRADIENT" in ftype:
+ stops = f.get("gradientStops", [])
+ handles = f.get("gradientHandlePositions", [])
+ if len(stops) < 2:
+ continue
+ # 각도 계산
+ if len(handles) >= 2:
+ dx = handles[1]["x"] - handles[0]["x"]
+ dy = handles[1]["y"] - handles[0]["y"]
+ angle = math.degrees(math.atan2(dy, dx)) + 90
+ else:
+ angle = 180
+ stop_str = ",".join(
+ f"#{int(s['color']['r']*255):02x}{int(s['color']['g']*255):02x}{int(s['color']['b']*255):02x} {s['position']*100:.0f}%"
+ for s in stops
+ )
+ bg = f"linear-gradient({angle:.0f}deg,{stop_str})"
+ color = bg
+ elif ftype == "IMAGE":
+ bg = "__IMAGE__"
+ return bg, color
+
+
+def node_to_html(node: dict, ox: float, oy: float, scale: float) -> str:
+ """단일 노드를 HTML div로 변환."""
+ ntype = node.get("type", "")
+ name = node.get("name", "")
+ bb = node.get("absoluteBoundingBox")
+ if not bb or not bb.get("width"):
+ return ""
+
+ x = (bb["x"] - ox) * scale
+ y = (bb["y"] - oy) * scale
+ w = bb["width"] * scale
+ h = bb["height"] * scale
+
+ fills = node.get("fills", [])
+ visible_fills = [f for f in fills if f.get("visible", True)]
+
+ if ntype == "TEXT":
+ chars = node.get("characters", "")
+ if not chars:
+ return ""
+ style = node.get("style", {})
+ fs = style.get("fontSize", 12) * scale
+ fw = style.get("fontWeight", 400)
+ align_h = style.get("textAlignHorizontal", "LEFT").lower()
+ align_v = style.get("textAlignVertical", "TOP")
+ lh_px = style.get("lineHeightPx", 0)
+ lh = lh_px * scale if lh_px else fs * 1.5
+ ls = style.get("letterSpacing", 0) * scale
+
+ # 텍스트 fill 처리
+ has_gradient = any(
+ "GRADIENT" in f.get("type", "") for f in visible_fills
+ )
+ if has_gradient:
+ bg, _ = get_fill_css(
+ [f for f in visible_fills if "GRADIENT" in f.get("type", "")]
+ )
+ text_style = (
+ f"background:{bg};"
+ f"-webkit-background-clip:text;"
+ f"-webkit-text-fill-color:transparent;"
+ )
+ else:
+ _, c = get_fill_css(visible_fills)
+ text_style = f"color:{c or '#000'};"
+
+ lh_css = f"line-height:{lh:.1f}px;"
+ ls_css = f"letter-spacing:{ls:.1f}px;" if ls > 0.1 else ""
+ align_css = f"text-align:{align_h};" if align_h != "left" else ""
+ valign_css = (
+ "display:flex;align-items:center;" if align_v == "CENTER" else ""
+ )
+
+ text_html = chars.replace("\n", "
")
+ return (
+ f'
{text_html}
'
+ )
+
+ elif ntype in ("RECTANGLE", "VECTOR"):
+ bg, _ = get_fill_css(visible_fills)
+ if not bg:
+ return ""
+ if bg == "__IMAGE__":
+ # 이미지 placeholder
+ return (
+ f'
'
+ )
+
+ cr = node.get("cornerRadius", 0)
+ cr_css = f"border-radius:{cr * scale:.1f}px;" if cr > 0 else ""
+
+ strokes = node.get("strokes", [])
+ stroke_css = ""
+ if strokes:
+ for s in strokes:
+ if not s.get("visible", True):
+ continue
+ sc = s.get("color", {})
+ sr = int(sc.get("r", 0) * 255)
+ sg = int(sc.get("g", 0) * 255)
+ sb = int(sc.get("b", 0) * 255)
+ sw = node.get("strokeWeight", 1) * scale
+ stroke_css = (
+ f"border:{sw:.1f}px solid #{sr:02x}{sg:02x}{sb:02x};"
+ )
+ break
+
+ return (
+ f'
'
+ )
+
+ return ""
+
+
+def frame_to_html(frame: dict, target_width: int = 1280) -> str:
+ """프레임 전체를 HTML 문서로 변환."""
+ bb = frame.get("absoluteBoundingBox", {})
+ ox, oy = bb["x"], bb["y"]
+ fw, fh = bb["width"], bb["height"]
+ scale = target_width / fw
+ out_h = int(fh * scale)
+
+ # 모든 리프 노드 수집 (재귀)
+ elements: list[tuple[int, str]] = [] # (z-order, html)
+
+ def collect(node: dict, z: int = 0):
+ ntype = node.get("type", "")
+ children = node.get("children", [])
+
+ if ntype in ("GROUP", "FRAME", "CANVAS", "COMPONENT", "INSTANCE"):
+ # 컨테이너는 자식만 순회
+ for i, child in enumerate(children):
+ collect(child, z + i)
+ else:
+ html = node_to_html(node, ox, oy, scale)
+ if html:
+ elements.append((z, html))
+ for i, child in enumerate(children):
+ collect(child, z + i)
+
+ collect(frame, 0)
+
+ # 프레임 배경
+ frame_fills = frame.get("fills", [])
+ frame_bg, _ = get_fill_css(
+ [f for f in frame_fills if f.get("visible", True)]
+ )
+ frame_bg_css = f"background:{frame_bg};" if frame_bg else "background:#fff;"
+
+ # z-order 순으로 정렬 (rect 먼저, text 나중)
+ rects = [(z, h) for z, h in elements if "font-size" not in h]
+ texts = [(z, h) for z, h in elements if "font-size" in h]
+
+ parts = [
+ f"""
+
+
"""
+ ]
+
+ # rect를 먼저 (배경), text를 나중 (전경)
+ for _, h in sorted(rects, key=lambda x: x[0]):
+ parts.append(h)
+ for _, h in sorted(texts, key=lambda x: x[0]):
+ parts.append(h)
+
+ parts.append("
")
+ return "\n".join(parts)
+
+
+def main():
+ if len(sys.argv) < 3:
+ print(
+ "Usage: python scripts/figma_to_html.py
"
+ )
+ sys.exit(1)
+
+ figma_json = Path(sys.argv[1])
+ output_dir = Path(sys.argv[2])
+ output_dir.mkdir(parents=True, exist_ok=True)
+
+ data = json.loads(figma_json.read_text(encoding="utf-8"))
+ doc = data.get("document", {})
+ pages = doc.get("children", [])
+
+ for page in pages:
+ for i, frame in enumerate(page.get("children", [])):
+ if frame.get("type") not in ("FRAME", "COMPONENT"):
+ continue
+ name = frame.get("name", f"frame_{i}")
+ # 파일명 정리
+ safe_name = (
+ name.replace(" ", "_")
+ .replace("/", "_")
+ .replace("\\", "_")
+ )
+ html = frame_to_html(frame)
+ out_path = output_dir / f"{safe_name}.html"
+ out_path.write_text(html, encoding="utf-8")
+ bb = frame.get("absoluteBoundingBox", {})
+ print(
+ f" {safe_name}.html"
+ f" ({bb.get('width', 0):.0f}x{bb.get('height', 0):.0f}"
+ f" -> 1280px)"
+ )
+
+ print(f"\n완료: {output_dir}")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/static/figma-assets/arrow_asis_tobe.png b/static/figma-assets/arrow_asis_tobe.png
new file mode 100644
index 0000000..554b6af
Binary files /dev/null and b/static/figma-assets/arrow_asis_tobe.png differ
diff --git a/static/figma-assets/badge_policy.png b/static/figma-assets/badge_policy.png
new file mode 100644
index 0000000..c12821a
Binary files /dev/null and b/static/figma-assets/badge_policy.png differ
diff --git a/static/figma-assets/badge_solution.png b/static/figma-assets/badge_solution.png
new file mode 100644
index 0000000..022aaaf
Binary files /dev/null and b/static/figma-assets/badge_solution.png differ
diff --git a/static/figma-assets/box_policy_container.png b/static/figma-assets/box_policy_container.png
new file mode 100644
index 0000000..2983a64
Binary files /dev/null and b/static/figma-assets/box_policy_container.png differ
diff --git a/static/figma-assets/box_solution_cards.png b/static/figma-assets/box_solution_cards.png
new file mode 100644
index 0000000..a986465
Binary files /dev/null and b/static/figma-assets/box_solution_cards.png differ
diff --git a/static/tokens.css b/static/tokens.css
index ae3f60c..c1656b5 100644
--- a/static/tokens.css
+++ b/static/tokens.css
@@ -34,6 +34,14 @@
--spacing-inner: 16px;
--spacing-small: 8px;
+ /* Warm 테마 (Figma 2026-04) */
+ --color-warm-brown: #5C3714;
+ --color-dark-teal: #084C56;
+ --color-teal: #227582;
+ --color-forest: #548235;
+ --color-beige: #E4D9C0;
+ --color-warm-yellow: #FAEDCB;
+
/* 기타 */
--radius: 6px;
--border-width: 1px;
diff --git a/templates/blocks/BEPs/detail-button.html b/templates/blocks/BEPs/detail-button.html
new file mode 100644
index 0000000..fbc56e7
--- /dev/null
+++ b/templates/blocks/BEPs/detail-button.html
@@ -0,0 +1,29 @@
+
+
+
+ {{ label | default('상세보기') }}
+
+
+
diff --git a/templates/blocks/BEPs/engn-solution-dfma.html b/templates/blocks/BEPs/engn-solution-dfma.html
new file mode 100644
index 0000000..539d3a9
--- /dev/null
+++ b/templates/blocks/BEPs/engn-solution-dfma.html
@@ -0,0 +1,73 @@
+
+
+
+
+ {% for item in items %}
+
+
{{ item.title }}
+
{{ item.description }}
+
+ {% endfor %}
+
+ {% if main_text %}
+
{{ main_text }}
+ {% endif %}
+ {% if policy_label %}
+
{{ policy_label }}
+ {% endif %}
+
+
+
diff --git a/templates/blocks/BEPs/image-placeholder.html b/templates/blocks/BEPs/image-placeholder.html
new file mode 100644
index 0000000..4efba3e
--- /dev/null
+++ b/templates/blocks/BEPs/image-placeholder.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/templates/blocks/BEPs/policy-direction.html b/templates/blocks/BEPs/policy-direction.html
new file mode 100644
index 0000000..3615b34
--- /dev/null
+++ b/templates/blocks/BEPs/policy-direction.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+ {% for section in sub_sections %}
+
+
{{ section.title }}
+ {% for bullet in section.bullets %}
+
• {{ bullet }}
+ {% endfor %}
+
+ {% endfor %}
+
+ {% if questions %}
+
+ {% for q in questions %}
+
{{ q.text }}
+ {% endfor %}
+
+ {% endif %}
+
+
+
diff --git a/templates/blocks/BEPs/process-product-2col.html b/templates/blocks/BEPs/process-product-2col.html
new file mode 100644
index 0000000..87d7c33
--- /dev/null
+++ b/templates/blocks/BEPs/process-product-2col.html
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+ {% if left_compare %}
+
{{ left_compare.title }}
+
+
+ {% for item in left_compare.left_items %}
+
• {{ item }}
+ {% endfor %}
+
+
+
+
+
+ {% for item in left_compare.right_items %}
+
• {{ item }}
+ {% endfor %}
+
+
+ {% endif %}
+ {% for section in left_sections %}
+
{{ section.title }}
+ {% for bullet in section.bullets %}
+
• {{ bullet }}
+ {% endfor %}
+ {% endfor %}
+
+
+
+
+
+ {% for section in right_sections %}
+
{{ section.title }}
+ {% for bullet in section.bullets %}
+
• {{ bullet }}
+ {% endfor %}
+ {% endfor %}
+
+
+
+
+
diff --git a/templates/blocks/BEPs/process-product-2col.svg b/templates/blocks/BEPs/process-product-2col.svg
new file mode 100644
index 0000000..285e96a
--- /dev/null
+++ b/templates/blocks/BEPs/process-product-2col.svg
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/blocks/BEPs/process-product-2col_svg_test.html b/templates/blocks/BEPs/process-product-2col_svg_test.html
new file mode 100644
index 0000000..6416aee
--- /dev/null
+++ b/templates/blocks/BEPs/process-product-2col_svg_test.html
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/blocks/BEPs/process-product-2col_test.html b/templates/blocks/BEPs/process-product-2col_test.html
new file mode 100644
index 0000000..4bf085b
--- /dev/null
+++ b/templates/blocks/BEPs/process-product-2col_test.html
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
Analogue 개념 기반 업무의 Digital Transformation
+
+
+
• 개념, 도서, 행정 절차 중심
+
• 2D 도면, 전문가, 규정
+
• 업무 구분(단절), 책임
+
+
➠
+
+
• 시각화된 목적물, 소통, 투명성 중심
+
• 3D 모델, 참여자, 실체
+
• 협업(융·복합), 창의성
+
+
+
위치기반의 3D 모델을 사용하는 Process 혁신
+
• 위치기반(지리적, 지형, 지반상태 포함)의 GIS와 3D 모델(형상, 내용속성) 기반의 건설 정보를 포함하는 BIM의 연계를 통한 업무 프로세스의 혁신
+
사용자 중심의 Solution(S/W) 제공
+
• 인프라 건설산업의 1차적인 Process 혁신은 등고선 중심의 지형도가 아닌 속성이 포함된 수치지형도와 본태 측량에서 획득한 3D 지반모델 구축 필수
+
• 설계와 시공에 관련된 기술을 정리하고 디지털화하여 S/W로 기술 축적
+
• 서로 다른 S/W로 작성되어 분절화된 Analogue 방식의 성과물과 정보물을 연계가 가능하도록 설계, 시공 Solution 제공
+
+
+
+
+
+
Copy & Paste로 인한 하향 평준화된 기존 성과품의 품질 향상
+
• 과거 수작업으로 시행하면서 발생하던 오류 등의 최소화
+
• 정확한 Data에 기반한 계획과 개선된 높은 품질의 성과물
+
Analogue 기반 도서 외 Digital 기반 정보물 추가
+
• 규정에만 의존한 도면, 수량, 계산서, 시방서 등의 성과물에 3D 모델, 시뮬레이션 등의 Digital 기반 정보물(Information Data and Products)이 추가
+
Solution을 이용한 업무효율화(사용자 편의, 협업 및 의사소통 강화 등)
+
• 디지털 기반 성과물인 Graphic 중심의 3D 모델, 시뮬레이션을 제대로 활용하기 위해서는 기존의 낮은 수준이 아니라 공학용 사이니지(H/W) 시스템이 필수로 갖춰야만 함
+
• Engn. Solution을 통해 프로젝트에 관한 이슈를 함께 검토하고 논의하고 다양한 건설단계별 정보를 디지털 데이터로 저장하여 건설의 전 과정을 통합관리
+
+
+
+
+
diff --git a/templates/blocks/BEPs/solution-goals.html b/templates/blocks/BEPs/solution-goals.html
new file mode 100644
index 0000000..eafbe87
--- /dev/null
+++ b/templates/blocks/BEPs/solution-goals.html
@@ -0,0 +1,76 @@
+
+
+
+
Solution 제작 목표
+
{{ main_text }}
+
+ {% for g in goals %}
+
+ {% if g.icon %}
{% endif %}
+
{{ g.en }}
+
{{ g.ko }}
+
+ {% endfor %}
+
+
+
+
diff --git a/templates/blocks/cards/compare-2col-badge.html b/templates/blocks/cards/compare-2col-badge.html
new file mode 100644
index 0000000..b464c45
--- /dev/null
+++ b/templates/blocks/cards/compare-2col-badge.html
@@ -0,0 +1,106 @@
+
+
+
+
+ {% if badge_title %}
+
+
+
{{ badge_title }}
+
+ {% endif %}
+
+
+
+
+ {{ left_title }}
+
+
+ {{ left_body }}
+
+
+
+
+
+
+
+ {{ right_title }}
+
+
+ {{ right_body }}
+
+
+
+
+
+ {% if statement %}
+
{{ statement }}
+ {% endif %}
+
+
+
diff --git a/templates/blocks/cards/compare-detail-gradient.html b/templates/blocks/cards/compare-detail-gradient.html
new file mode 100644
index 0000000..87ef7e5
--- /dev/null
+++ b/templates/blocks/cards/compare-detail-gradient.html
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+ {% for row in sections %}
+
+
{{ row.left.title }}
+ {% if row.left.asis is defined %}
+
+
+ {% for b in row.left.asis %}
{{ b }}
{% endfor %}
+
+
+
+ {% for b in row.left.tobe %}
{{ b }}
{% endfor %}
+
+
+ {% elif row.left.bullets is defined %}
+
+ {% for b in row.left.bullets %}
{{ b }}
{% endfor %}
+
+ {% else %}
+
{{ row.left.body }}
+ {% endif %}
+
+
+
+
{{ row.right.title }}
+ {% if row.right.bullets is defined %}
+
+ {% for b in row.right.bullets %}
{{ b }}
{% endfor %}
+
+ {% else %}
+
{{ row.right.body }}
+ {% endif %}
+
+ {% endfor %}
+
+
+
diff --git a/templates/blocks/cards/hero-icon-cards.html b/templates/blocks/cards/hero-icon-cards.html
new file mode 100644
index 0000000..4088792
--- /dev/null
+++ b/templates/blocks/cards/hero-icon-cards.html
@@ -0,0 +1,98 @@
+
+
+
+ {% if statement %}
+
{{ statement }}
+ {% endif %}
+
+
+ {% if badge_title %}
+
+
+
{{ badge_title }}
+
+ {% endif %}
+
+
+
+ {% for card in cards %}
+
+ {% if card.icon %}
{{ card.icon }}
{% endif %}
+
{{ card.title }}
+ {% if card.subtitle %}
({{ card.subtitle }})
{% endif %}
+
+ {% if not loop.last %}
{% endif %}
+ {% endfor %}
+
+
+
+
+
+
diff --git a/templates/blocks/cards/hero-icon-cards_1.html b/templates/blocks/cards/hero-icon-cards_1.html
new file mode 100644
index 0000000..616b22d
--- /dev/null
+++ b/templates/blocks/cards/hero-icon-cards_1.html
@@ -0,0 +1,111 @@
+
+
+
+ {% if statement %}
+
{{ statement }}
+ {% endif %}
+
+
+ {% if badge_title %}
+
+
+
{{ badge_title }}
+
+ {% endif %}
+
+
+
+ {% for card in cards %}
+
+
{{ card.title }}
+ {% for section in card.sections %}
+
{{ section.title }}
+ {% for bullet in section.bullets %}
+
{{ bullet }}
+ {% endfor %}
+ {% endfor %}
+
+ {% if not loop.last %}
{% endif %}
+ {% endfor %}
+
+
+
+
+
+
diff --git a/templates/blocks/cards/test-compare-2col-badge.html b/templates/blocks/cards/test-compare-2col-badge.html
new file mode 100644
index 0000000..dfc4fdc
--- /dev/null
+++ b/templates/blocks/cards/test-compare-2col-badge.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+compare-2col-badge 테스트
+
+
+
+
+
+
+Test 1: Figma Frame 3 원본 (Engn. Solution vs DfMA)
+
+
+
+
+
+ 정책 달성
+
+
+
+
Engn. Solution
+
목적 시설물에 대한 설계, 시공, 유지관리 정보를 고객이 쉽고 편리하게 사용하고, 편익이 발생하도록 제공하는 다양한 형태의 정보물과 이를 구현할 수 있는 S/W 및 H/W와 그 기술을 포함하는 서비스
+
+
+
+
DfMA
+
Design for Manufacture and Assembly는 공장 생산, 현장조립이 가능한 설계를 의미하며, 현장 중심의 건설에서 공장 생산 방식으로 전환하는 OSC(Off Site Construction) 시스템을 위한 핵심기술
+
+
+
디지털전환은 사용자 (발주자, 수급자, 엔지니어)가쉽고, 편하고, 편익 이 있어야만 한다.
+
+
+
+
+
+Test 2: 다른 콘텐츠 (범용성 확인)
+
+
+
+
+ 디지털 전환 전략
+
+
+
+
As-Is (현재)
+
종이 도면 기반의 업무 프로세스
+수작업 검증과 품질 관리
+분절된 단계별 정보 전달
+개인 경험에 의존하는 의사결정
+
+
+
+
To-Be (미래)
+
3D 모델 기반의 통합 프로세스
+자동화된 검증과 품질 관리
+연속적인 디지털 정보 흐름
+데이터 기반의 의사결정 지원
+
+
+
+
+
+
+Test 3: 짧은 콘텐츠 (최소 케이스)
+
+
+
+
+
+
+
diff --git a/templates/blocks/cards/test-compare-detail-gradient_1.html b/templates/blocks/cards/test-compare-detail-gradient_1.html
new file mode 100644
index 0000000..c9fc13b
--- /dev/null
+++ b/templates/blocks/cards/test-compare-detail-gradient_1.html
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Analogue 기반 업무의 Digital Transformation
+
+
+
+
개념·문서·행정 절차 중심
2D 도면, 전문가, 규정
업무 구분(단절), 책임
+
+
+
+
시각화된 목적물, 소통, 투명성 중심
3D 모델, 참여자, 실체
협업(융·복합), 창의성
+
+
+
+
+
+
+
Copy & Paste로 인한 하향 평준화된 기존 성과물의 품질 향상
+
+
+
과거 수작업으로 시행하면서 발생하던 오류 등의 최소화
정확한 Data에 기반한 계획으로 고품질 성과물 도출
+
+
+
+
+
+
위치기반의 3D 모델을 사용하는 Process 혁신
+
+
+
지리·지형·지반 등 위치정보(GIS)와 3D모델(형상, 속성정보) 기반의 건설 정보를 포함하는 BIM의 연계를 통한 업무 프로세스의 혁신
+
+
+
+
+
+
Analogue 기반 도서 외 Digital 기반 정보물 추가
+
+
+
기존 성과물(도면, 수량, 계산서, 시방서 등)에 3D 모델, Simulation 등의 Digital 기반 정보물 추가
+
+
+
+
+
+
사용자 중심의 Solution(S/W) 제공
+
+
+
서로 다른 S/W로 작성되어 분절화된 Analogue 방식의 성과물과 정보물을 연계할 수 있는 설계·시공 Solution 제공
+
+
+
+
+
+
Solution을 활용한 업무 효율화
+
+
+
Engn. Solution을 통해 성과물에 관한 이슈를 함께 검토·논의하는 협업 환경 조성
건설 단계별 정보를 디지털 데이터로 축적하여, 건설 전 과정을 통합관리
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/blocks/cards/test-hero-icon-cards_1.html b/templates/blocks/cards/test-hero-icon-cards_1.html
new file mode 100644
index 0000000..7346b5b
--- /dev/null
+++ b/templates/blocks/cards/test-hero-icon-cards_1.html
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
DX 시행을 위한 필수 요건
+
+
+
+
+
+
+
+
기술(디지털)
+
+
Digital 기술(S/W, H/W)과 업무 Process의 통합
+
+
기존 업무 프로세스에 다양한 디지털 기술을 접목하여 업무 수행
+
+
프로젝트 전반에 걸친 업무 프로세스의 연결 및 조율
+
+
+
분야별 전문 지식(설계, 시공, 유지관리 등) 보유
+
+
건설 전 단계에 대한 근본적인 이해와 지식 및 경험
+
+
최신 토목 기술 트랜드 및 표준 기준 등에 대한 높은 지식
+
+
+
+
+
+
+
사람(역량)
+
+
혁신적 사고방식과 창의적 문제 해결 능력
+
+
기존 수행 방식과 관습적 사고 등에 의한 접근 방식 탈피
+
+
디지털 기술을 활용한 창의적, 혁신적인 솔루션 제시
+
+
+
사용자 중심 사고와 DX 수행 경험
+
+
사용자의 요구와 기대를 충족시키는 설계 및 구현
+
+
시행착오를 포함한 수행 경험과 사용자 경험(UX)을 반영한 해결 방안 제시
+
+
+
+
+
+
+
자연(여건)
+
+
지속적인 투자 및 실행 의지
+
+
기술 도입 초기 단계에 필요한 인력·기간·비용 등의 대규모 투자
+
+
기술 고도화를 위한 지속적인 개선 및 투자 체계 구축
+
+
변화와 혁신을 통해 부가가치를 창출하려는 실행 의지와 추진력
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/templates/blocks/cards/test-hero-icon-cards_1.png b/templates/blocks/cards/test-hero-icon-cards_1.png
new file mode 100644
index 0000000..4f9fd34
Binary files /dev/null and b/templates/blocks/cards/test-hero-icon-cards_1.png differ
diff --git a/templates/catalog.yaml b/templates/catalog.yaml
index c5de5a7..3a4fd06 100644
--- a/templates/catalog.yaml
+++ b/templates/catalog.yaml
@@ -751,6 +751,227 @@ blocks:
note: 항목 수
padding_overhead_px: 22
padding_h_px: 32
+- id: hero-icon-cards
+ name: 히어로 문구 + 아이콘 카드
+ category: cards
+ template: blocks/cards/hero-icon-cards.html
+ height_cost: xlarge
+ min_height_px: 280
+ relation_types:
+ - definition
+ - flow
+ min_items: 2
+ max_items: 6
+ visual: >
+ 다크 배경. 상단 큰 Hero 메시지(28px bold, 중앙, 흰 텍스트, 빨간 강조).
+ 3D 빨간 리본 배지가 빨간 테두리 흰 박스(r:20) 위에 걸침.
+ 박스 안에 N열 아이콘 카드(구분선). 각 카드: 아이콘 + 영문 제목(20px/900) + 한국어 부제(15px/500).
+ visual_diff: >
+ - card-icon-desc: 밝은 배경, 이모지+제목+설명. 리본/테두리 없음.
+ - card-dark-overlay: 다크 배경 이미지+흰 텍스트. 카드 개별 배경.
+ - 이 블록: 다크 배경 + 빨간 리본 배지 + 빨간 테두리 흰 박스 안 N열 카드.
+ 적합: 핵심 목표/가치를 N개 키워드로 선언하며 시각적 임팩트 필요할 때.
+ 부적합: 상세 설명 → card-icon-desc, 비교 → compare-2col-badge.
+ when: >
+ 핵심 목표나 가치를 N개 키워드로 선언할 때.
+ 각 키워드에 아이콘이나 이미지가 있을 때.
+ 프레젠테이션형 임팩트가 필요할 때.
+ not_for: >
+ 비교/대조 → compare-2col-badge.
+ 상세 설명 → card-icon-desc.
+ 순서/단계 → process-horizontal.
+ purpose_fit:
+ - 핵심전달
+ zone: full-width-only
+ slots:
+ required:
+ - statement
+ - cards[]
+ optional:
+ - badge_title
+ - ribbon_image
+ - cards[].icon
+ - cards[].subtitle
+ schema:
+ statement:
+ max_lines: 2
+ font_size: 28
+ ref_chars:
+ body: 60
+ note: "28px bold white, 중앙, em=빨간 강조"
+ badge_title:
+ max_lines: 1
+ font_size: 20
+ ref_chars:
+ body: 15
+ note: "20px bold white, 3D 리본 위"
+ card_title:
+ max_lines: 2
+ font_size: 20
+ ref_chars:
+ body: 15
+ note: "20px black/900, 중앙정렬"
+ card_subtitle:
+ max_lines: 1
+ font_size: 15
+ ref_chars:
+ body: 10
+ note: "15px medium, 한국어 부제"
+ padding_overhead_px: 80
+ padding_h_px: 32
+- id: compare-detail-gradient
+ name: 그라디언트 상세 2열 비교
+ category: cards
+ template: blocks/cards/compare-detail-gradient.html
+ height_cost: xlarge
+ min_height_px: 300
+ relation_types:
+ - comparison
+ - contrast
+ - process
+ min_items: 2
+ max_items: 10
+ visual: >
+ 좌우 그라디언트 배경(워 브라운 vs 다크틸)으로 나뉜 2열 비교.
+ 비대칭 라운드 헤더(좌: 우라운드+우정렬, 우: 좌라운드+좌정렬).
+ N개 섹션(소제목 18px/900 + 불릿 14px/700) 반복.
+ CSS Grid 행 공유로 좌/우 섹션 제목이 같은 Y선에 정렬.
+ 첫 섹션에 As-Is → 화살표 → To-Be 수평 배치 가능.
+ visual_diff: >
+ - compare-2col-badge: 배지+2열, 단순 비교. 섹션 반복 없음.
+ - comparison-2col: 좌우 텍스트 블록 비교. 그라디언트 없음.
+ - 이 블록: 그라디언트+비대칭 헤더+N섹션 행 정렬. 가장 상세한 비교.
+ 적합: 두 카테고리에 각각 3개+ 하위 항목이 있는 상세 비교.
+ 부적합: 간단 비교 → compare-2col-badge, 표 형식 → compare-2col-split.
+ when: >
+ 두 카테고리를 상세하게 비교할 때. 각 카테고리에 여러 하위 항목.
+ 과정 vs 결과, As-Is vs To-Be, 문제 vs 해결 구조.
+ not_for: >
+ 간단 비교(본문 짧을 때) → compare-2col-badge.
+ 항목별 행 비교(표 형식) → compare-2col-split.
+ 3열 비교 → compare-3col-badge.
+ purpose_fit:
+ - 비교대조
+ - 구조시각화
+ - 근거사례
+ zone: full-width-only
+ slots:
+ required:
+ - left_header
+ - right_header
+ - sections[]
+ optional:
+ - arrow_image
+ - sections[].left.asis
+ - sections[].left.tobe
+ schema:
+ left_header:
+ max_lines: 1
+ font_size: 26
+ ref_chars:
+ body: 20
+ note: "26px black/900, 비대칭 라운드 바"
+ right_header:
+ max_lines: 1
+ font_size: 26
+ ref_chars:
+ body: 20
+ note: "26px black/900, 비대칭 라운드 바"
+ section_title:
+ max_lines: 2
+ font_size: 18
+ ref_chars:
+ body: 30
+ note: "18px/900, 좌=브라운 우=틸"
+ section_body:
+ max_lines: 4
+ font_size: 14
+ ref_chars:
+ body: 120
+ note: "14px/700, 불릿 구조"
+ padding_overhead_px: 52
+ padding_h_px: 0
+- id: compare-2col-badge
+ name: 배지 헤더 2열 비교
+ category: cards
+ template: blocks/cards/compare-2col-badge.html
+ height_cost: large
+ min_height_px: 200
+ relation_types:
+ - comparison
+ - contrast
+ visual: >
+ 상단 배지 바(틸 그라디언트 + 흰색 텍스트, r:12) 아래
+ 2열 비교 레이아웃. 좌/우 각각 대제목(22px/900) + 본문(15px/500).
+ 중앙 세로 구분선. 흰색 컨테이너(r:16). 선택적 하단 Hero 메시지.
+ visual_diff: >
+ - compare-2col-split: 중앙 '기준 라벨' 열이 있는 3열 표. 행별 상세 비교.
+ - comparison-2col: 좌우 텍스트 블록 비교. 표 없이 자유 형식.
+ - compare-pill-pair: 둥근 박스 2개 + VS. 헤더 역할만.
+ - 이 블록: 배지 헤더로 상위 주제 선언 + 2열 비교 + 선택적 결론 문구.
+ 적합: 두 개념/전략의 정의를 비교하며 상위 주제를 배지로 명시할 때.
+ 부적합: 항목별 행 비교 → compare-2col-split, 시각적 대비만 → compare-pill-pair.
+ when: >
+ 두 개념/방법/전략을 나란히 비교할 때. 배지 헤더로 상위 주제를 명시.
+ 예: "정책 달성 — Engn. Solution vs DfMA", "현재 vs 미래"
+ not_for: >
+ 항목별 행 비교(5행+) → compare-2col-split.
+ 3개 비교 → compare-3col-badge.
+ 간단 텍스트만 → comparison-2col.
+ purpose_fit:
+ - 비교대조
+ - 개념정의
+ zone: full-width-only
+ slots:
+ required:
+ - badge_title
+ - left_title
+ - left_body
+ - right_title
+ - right_body
+ optional:
+ - statement
+ - left_color
+ - right_color
+ schema:
+ badge_title:
+ max_lines: 1
+ font_size: 18
+ ref_chars:
+ body: 15
+ note: 18px bold white, 배지 바
+ left_title:
+ max_lines: 1
+ font_size: 22
+ ref_chars:
+ body: 15
+ note: 22px black/900
+ left_body:
+ max_lines: 6
+ font_size: 15
+ ref_chars:
+ body: 200
+ note: 15px/500, 틸 색상
+ right_title:
+ max_lines: 1
+ font_size: 22
+ ref_chars:
+ body: 15
+ note: 22px black/900
+ right_body:
+ max_lines: 6
+ font_size: 15
+ ref_chars:
+ body: 200
+ note: 15px/500, 틸 색상
+ statement:
+ max_lines: 2
+ font_size: 18
+ ref_chars:
+ body: 50
+ note: 18px bold, 중앙정렬
+ padding_overhead_px: 56
+ padding_h_px: 32
- id: compare-3col-badge
name: VS 배지 비교표
category: tables
diff --git a/오답노트.md b/오답노트.md
new file mode 100644
index 0000000..88828c1
--- /dev/null
+++ b/오답노트.md
@@ -0,0 +1,117 @@
+# 절대 하지 말아야 하는 오답노트
+
+> 2026-04-07 세션에서 발생한 실수 목록. 반복하면 안 됨.
+
+---
+
+## 1. 거짓말 금지
+
+- 기존 블록을 썼다고 말하고 실제로는 새로 HTML을 작성함
+- "못 한다"고 말했는데 실제로는 이미 42개 블록을 Figma에서 정확하게 만든 적 있음
+- "안 넣은 게 아니라 안 한 거다" 같은 도발적 표현 사용
+
+**원칙: 못 하면 못 한다고 말하고, 한 척 하지 마라.**
+
+---
+
+## 2. 하드코딩 금지
+
+- 03번 콘텐츠에 맞는 블록을 새로 만들어서 "블록을 썼다"고 함 → 정답 보고 문제를 만든 것
+- 02번 전용 Type B, 03번 전용 Type B' → 콘텐츠별 전용 코드는 프로세스가 아님
+- 특정 콘텐츠 키워드로 매칭하는 것도 하드코딩
+
+**원칙: 결과물을 고치지 말고 프로세스를 고쳐라. 프로세스가 범용적이어야 한다.**
+
+---
+
+## 3. 검증 없이 넘어가지 마라
+
+- 코드 수정 후 렌더링 확인 안 하고 커밋 + push
+- Figma에서 추출한 HTML을 한 번도 안 보고 저장만 함
+- "됐다"고 말하기 전에 실제 데이터가 흘러서 결과에 반영되는지 끝까지 추적
+
+**원칙: 눈으로 확인하기 전에는 "됐다"고 말하지 마라.**
+
+---
+
+## 4. "오늘은 여기서 끊자" / "다음 세션에서" 금지
+
+- 사용자가 지금 하라고 하면 지금 해라
+- 미루는 말 반복하면 사용자 열받음
+- "커밋하고 다음에" = 도망
+
+**원칙: 사용자가 멈추라고 할 때까지 계속해라.**
+
+---
+
+## 5. 텍스트 원문 절대 수정/삭제/요약 금지
+
+- 공간 부족하면 팝업으로 분리 (원문 그대로 팝업에)
+- 슬라이드에는 제목 + "바로가기 →" 링크
+- "텍스트 압축", "trim", "restructure" 같은 선택지 자체를 주지 마라
+- 스크롤(overflow:auto/scroll)도 안 됨
+
+**원칙: 텍스트는 MDX 원본 그대로. 안 들어가면 팝업으로 빼지, 텍스트를 줄이지 마라.**
+
+---
+
+## 6. 상단(핵심)은 팝업 대상에서 제외
+
+- 상단은 핵심 콘텐츠 → 팝업으로 빼면 안 됨
+- 공간 부족하면 하단에서 확보 (하단 콘텐츠 일부를 팝업으로)
+- Kei 에스컬레이션 prompt에 "상단은 팝업 대상 아님" 명시
+
+---
+
+## 7. Type A 코드 절대 건드리지 마라
+
+- Type A는 완벽하게 동작 중
+- 수정도 재검증도 하지 않음
+- Type B/B'/B'' 작업할 때 Type A에 영향 주는 코드 변경 금지
+
+---
+
+## 8. API 낭비 금지
+
+- 파이프라인 돌리기 전에 코드 변경이 맞는지 먼저 확인
+- 매번 돌려보면서 "되나?" 하지 마라
+- 한 번 돌릴 때 정확하게 고쳐서 돌려라
+
+---
+
+## 9. Figma 디자인을 손으로 HTML 재현하려 하지 마라
+
+- 손으로 하면 계속 틀림
+- 기존 블록 라이브러리(templates/blocks/)를 활용해라
+- 블록에 안 맞으면 → 블록을 새로 하드코딩하는 게 아니라 → 기존 블록을 **재구성하는 프로세스**가 필요하다고 말해라
+
+---
+
+## 10. 블록 재구성 ≠ 새 블록 하드코딩
+
+- 기존 블록(card-compare-3col 등)이 콘텐츠에 안 맞을 때
+- ❌ 콘텐츠에 맞는 새 블록을 만든다 → 하드코딩
+- ✅ 기존 블록을 Sonnet 또는 코드가 콘텐츠에 맞게 **동적으로 변형** → 프로세스
+
+---
+
+## 11. 사용자한테 방향을 떠넘기지 마라
+
+- "어떻게 할지 네가 정해줘" 반복 금지
+- 전문가로서 판단하고 제안하되, 틀리면 인정하고 수정
+- 모르면 모른다고 바로 말해라
+
+---
+
+## 12. 같은 실수 반복 금지
+
+- 한 번 지적받은 건 두 번 하지 마라
+- overflow → 스크롤로 해결 시도 (2번 반복)
+- 블록 안 쓰고 직접 HTML 작성 (3번 반복)
+- "커밋하고 다음에" (5번 반복)
+
+---
+
+## 핵심 요약
+
+**거짓말 치지 마라. 하드코딩 하지 마라. 검증 없이 넘어가지 마라. 프로세스를 만들어라.**
diff --git a/templates/참고 페이지/005_건설산업의 혁신.png b/참고 페이지/005_건설산업의 혁신.png
similarity index 100%
rename from templates/참고 페이지/005_건설산업의 혁신.png
rename to 참고 페이지/005_건설산업의 혁신.png
diff --git a/templates/참고 페이지/1번.png b/참고 페이지/1번.png
similarity index 100%
rename from templates/참고 페이지/1번.png
rename to 참고 페이지/1번.png
diff --git a/templates/참고 페이지/2번.png b/참고 페이지/2번.png
similarity index 100%
rename from templates/참고 페이지/2번.png
rename to 참고 페이지/2번.png
diff --git a/templates/참고 페이지/3번.png b/참고 페이지/3번.png
similarity index 100%
rename from templates/참고 페이지/3번.png
rename to 참고 페이지/3번.png
diff --git a/templates/참고 페이지/4번.png b/참고 페이지/4번.png
similarity index 100%
rename from templates/참고 페이지/4번.png
rename to 참고 페이지/4번.png
diff --git a/참고 페이지/figma_center_2x.png b/참고 페이지/figma_center_2x.png
new file mode 100644
index 0000000..7c4ae5a
Binary files /dev/null and b/참고 페이지/figma_center_2x.png differ
diff --git a/참고 페이지/figma_export_2x.png b/참고 페이지/figma_export_2x.png
new file mode 100644
index 0000000..150ab9f
Binary files /dev/null and b/참고 페이지/figma_export_2x.png differ
diff --git a/templates/참고 페이지/스크린샷 2026-04-07 113217.png b/참고 페이지/스크린샷 2026-04-07 113217.png
similarity index 100%
rename from templates/참고 페이지/스크린샷 2026-04-07 113217.png
rename to 참고 페이지/스크린샷 2026-04-07 113217.png