untracked files on main: dceb101 feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)

This commit is contained in:
2026-05-21 22:07:41 +09:00
commit 8f085a28d3
3220 changed files with 985495 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
# Block Catalog v1
# 파이프라인이 콘텐츠에 맞는 블록을 선택할 때 사용하는 메타데이터.
# 각 블록의 구조, 슬롯, 호환 recipe를 정의.
blocks:
- id: prerequisites-3col
source_frame: "1171281190"
structure_type: 3col-parallel
description: "3열 병렬 비교. 각 열에 gradient 바 + 2 섹션(제목+설명). 기술/사람/환경 같은 병렬 조건 나열에 적합."
keywords: [필수, 요건, 조건, 기술, 사람, 자연, 역량, 디지털, 투자, 병렬, 3가지]
slots:
- zone_title # 블록 대제목 (e.g. "필수조건")
- sub_title # 열별 섹션 소제목 (×6: 3열 × 2섹션)
- body # 열별 섹션 설명 텍스트 (×6)
recipe_compat: [direct_fit, parallel_cluster]
not_for: [long_table, image_heavy, comparison_2col, sequential_flow]
- id: compare-vs-rows
source_frame: "1171281195"
structure_type: compare-2col-badge
description: "좌/우 비교 행 N개 + 중앙 뱃지. 서브헤더 + 결론 포함. BIM vs DX 같은 이분법 비교에 적합."
keywords: [비교, 차이, BIM, DX, 대비, 프로세스, 성과물, 개념, 활용]
slots:
- zone_title # 블록 대제목
- sub_title # 서브헤더 (e.g. "BIM vs DX")
- bullets # 좌/우 비교 항목 (×N행)
recipe_compat: [direct_fit, two_col_text_visual, comparison]
not_for: [image_heavy, single_column, 3col_layout]
- id: compare-table-3col
source_frame: "1171281204"
structure_type: table-3col
description: "CSS grid 3열 비교 표. 행 라벨 + 3열 셀. R13 flex pair 불릿. S/W 유형 비교 같은 다축 비교에 적합."
keywords: [비교, 구분, 상용, 전용, 개발, 개념, 성과품, 표, 3열]
slots:
- zone_title # 블록 대제목
- preview # 표 헤더 + 행 데이터 (행 라벨, 3열 셀)
recipe_compat: [direct_fit, comparison]
not_for: [image_heavy, single_column, narrative_flow]
- id: compare-table-2col
source_frame: "1171281203"
structure_type: table-2col
description: "CSS grid 2열 비교 표. 행 라벨 + 2열 셀. R13 flex pair 불릿. Package vs Solution 같은 이분법 표에 적합."
keywords: [비교, 구분, 정의, 특징, 장점, 단점, 표, 2열, 패키지, 솔루션]
slots:
- zone_title # 블록 대제목
- preview # 표 헤더 + 행 데이터
recipe_compat: [direct_fit, comparison]
not_for: [image_heavy, single_column, narrative_flow]
- id: stacked-arrow-list
source_frame: "1171281180"
structure_type: list-stacked
description: "계단식 pill 행 리스트. 화살표 마커 + 텍스트. 좌측 아크 장식 선택적. 항목 나열/흐름에 적합."
keywords: [시공, 정보물, 모델, 시뮬레이션, 도면, 성과물, 목록, 항목, 나열]
slots:
- zone_title # 블록 대제목
- bullets # pill 행 텍스트 (×N)
recipe_compat: [direct_fit, list_flow]
not_for: [comparison, image_heavy, long_table]
- id: issues-paired-rows
source_frame: "1171281194"
structure_type: 2col-paired
description: "좌/우 이슈 행 N개. pill 라벨 + 본문, 가운데 구분선. 현황/문제점 같은 대칭 이슈 나열에 적합."
keywords: [현황, 문제점, 이슈, 개념, 접근, 방향, 방식, 오류, 대칭]
slots:
- zone_title # 블록 대제목
- sub_title # pill 라벨 (×N행 × 좌우)
- body # 좌/우 본문 (×N행)
recipe_compat: [direct_fit, two_col_text_visual]
not_for: [long_table, image_heavy, 3col_layout]
@@ -0,0 +1,45 @@
# IMP-38 V4 max_rank 정책 — separate yaml (catalog 오염 방지)
#
# 도입 배경:
# 기존 `lookup_v4_match_with_fallback(max_rank=3)` hardcoded → rank 4~32 의 등록 frame 도달 못함
# mdx05-2 같이 V4 rank 1~9 가 catalog 미등록 + rank 10~ 등록 case → chain_exhausted → unit 생성 X
#
# 4 round 합의 (IMP-38 #67):
# - Codex #1: frame_contracts.yaml 오염 회피 → 별 yaml 파일 (이 파일)
# - Codex #2: 3 변수 분리 (configured / judgments / catalog count)
# - Codex #3: effective_extended_ceiling = min(configured, len(judgments_full32))
#
# 적용 path: src/phase_z2_mapper.py 의 load_v4_fallback_policy() loader
# + src/phase_z2_pipeline.py 의 lookup_v4_match_with_fallback() 동적 max_rank logic
policy_type: dynamic_usable_count_based
# usable_threshold N:
# rank 1~default_max_rank 중 "usable" predicate 충족 frame 수 >= N → default_max_rank 유지
# < N → extended_max_rank 로 확장
usable_threshold: 1
# default_max_rank:
# normal case (usable_count >= threshold) 의 fallback chain 길이
# mdx03 같이 rank 1 use_as_is 매칭 잘 되는 case 보호
default_max_rank: 3
# extended_max_rank:
# usable_count < threshold case 의 확장 ceiling
# mdx05-2 같이 rank 1~9 미등록 case 처리
# ★ 실제 effective_extended_ceiling = min(extended_max_rank, len(judgments_full32))
# (Codex #2 정정: yaml ceiling 무력화 방지 + V4 schema 범위 초과 방지)
extended_max_rank: 32
# usable predicate (3-tier):
# (a) phase_z_status in MVP1_ALLOWED_STATUSES (matched_zone / adapt_matched_zone)
# (b) get_contract(template_id) is not None (catalog 등록)
# (c) capacity_fit ok (raw_content 제공 시만 — optional)
# 의미 신뢰 vs catalog presence trade-off:
# N=1 = 가장 보수 (rank 1 usable 시 확장 X — mdx03 정상 case 보호)
# default_max_rank=3 = 의미 신뢰 범위 (V4 rank 1~3)
# extended_max_rank=32 = catalog presence fallback (rank 4~32)
# graceful fallback (yaml 없을 시):
# loader 가 default {default_max_rank: 3, extended_max_rank: 3} 로 fall through (backward compat)
@@ -0,0 +1,242 @@
<!-- Phase Z-2 MVP-1.5b frame-derived adapted block. -->
{#
─────────────────────────────────────────────────────────────────────────────
Visual Provenance — figma_to_html_agent/blocks/1171281203/ (frame 23)
─────────────────────────────────────────────────────────────────────────────
Frame 23 = "Application S/W 의 구분".
구조 (figma 1:1) :
① title gradient (Application S/W = orange→brown, 의 구분 = green→black)
② CSS Grid table (label col + col_a + col_b)
③ header row :
- col 1 (label) = teal #589e8d
- col 2 (Package) = teal #589e8d
- col 3 (Solution) = orange #ef7a26
④ N data rows : zebra (odd 흰 0.85 / even peach rgba(253,198,158,0.2))
- 각 row : 좌측 label cell + 좌 data + 우 data
- cell border : 1.5px solid #888
⑤ bullet (•) + accent (.hl orange #a14101, .big 50px 강조)
slots (bim_dx_comparison_table 와 동일 — builder = process_product_pair) :
- title : section.title (zone 중목차)
- col_a_label / col_b_label : h3 title (Package / Solution 또는 mdx h3 title)
- col_a_body.sections[].title : subsection title (= row label)
- col_a_body.sections[].text_lines / transforms
- col_b_body : same
Axis A+B fit — container query (cqh) + jinja line count 산식.
─────────────────────────────────────────────────────────────────────────────
#}
<style>
.f23b {
width: 100%; height: 100%;
display: flex; flex-direction: column;
gap: 4px;
font-family: 'Noto Sans KR', 'Pretendard', sans-serif;
word-break: keep-all;
container-type: size;
container-name: f23b-root;
}
/* 중목차 (zone title) — gradient 보존, 사용자 룰 (미변경). */
.f23b__title {
font-size: var(--font-zone-title);
font-weight: 700;
line-height: var(--lh-zone-title);
background-image: linear-gradient(180deg, #000 0%, #883700 100%);
-webkit-background-clip: text; background-clip: text;
color: transparent;
flex-shrink: 0;
filter: drop-shadow(0 0 3px rgba(50,44,30,0.4));
}
/* CSS Grid table — 사용자 lock 2026-05-15 : 의미 없는 label col (구분/숫자)
제거, 2 col 로 전체 폭 활용. */
.f23b__tbl {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: minmax(0, auto);
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
border-radius: clamp(4px, 1cqh, 8px);
border: 1px solid #888;
}
/* 회전 비활성 — 사용자 lock 2026-05-15 : 03-2 는 2 col table 유지. */
/* header row */
.f23b__th {
padding: clamp(4px, 1.5cqh, 10px) clamp(4px, 1cqw, 8px);
display: flex; align-items: center; justify-content: center;
font-weight: 700;
font-size: var(--font-sub-title);
line-height: 1.15;
color: #fff;
text-align: center;
letter-spacing: -0.03em;
border-right: 1px solid #888;
}
.f23b__th:last-child { border-right: none; }
.f23b__th--label,
.f23b__th--a { background: #589e8d; } /* figma teal */
.f23b__th--b { background: #ef7a26; } /* figma orange */
/* data row cells */
.f23b__td {
padding: clamp(4px, 1.2cqh, 10px) clamp(4px, 1cqw, 10px);
border-top: 1px solid #888;
border-right: 1px solid #888;
color: #000;
font-size: var(--font-body);
line-height: clamp(1.2em, calc(70cqh / var(--max-body-lines, 6)), 1.6em);
font-weight: 500;
overflow: hidden;
min-height: 0;
}
.f23b__td:last-child { border-right: none; }
.f23b__td--label {
font-weight: 700;
text-align: center;
display: flex; align-items: center; justify-content: center;
font-size: var(--font-sub-title);
line-height: 1.2;
}
/* zebra — 첫 번째 data row 가 row N, 두 번째 row 가 N+1 ... */
.f23b__row--odd .f23b__td { background: rgba(255,255,255,0.85); }
.f23b__row--even .f23b__td { background: rgba(253,198,158,0.2); }
/* bullet + accent */
.f23b__td .text-line {
position: relative;
padding-left: clamp(12px, 2cqw, 18px);
}
.f23b__td .text-line::before {
content: "•";
position: absolute;
left: clamp(2px, 0.5cqw, 6px); top: 0;
color: #555;
font-weight: 700;
line-height: inherit;
}
.f23b__td .text-line strong { font-weight: 700; color: #a14101; } /* figma .hl */
.f23b__td .accent { font-weight: 700; color: #a14101; }
.f23b__td .text-line--sub { padding-left: clamp(20px, 4cqw, 28px); color: #444; }
.f23b__td .text-line--sub::before {
content: "\25B8"; /* ▸ */
left: clamp(8px, 1.5cqw, 14px);
color: #888;
font-size: 0.85em;
}
/* AS-IS/TO-BE mini transforms (콘텐츠 무손실 + 내용 기반 폭). 사용자 lock
2026-05-15 : 표 안 내용이 좌측정렬 환경이라 mini table 도 좌측 정렬.
cell 안 텍스트는 그대로 center align. */
.f23b__transforms {
display: table;
margin-left: 0;
margin-right: auto;
border-collapse: separate;
border-spacing: 6px 1px;
font-size: 0.95em;
}
.f23b__transforms-head,
.f23b__transforms-row { display: table-row; }
.f23b__transforms-head > * {
display: table-cell;
text-align: center;
font-weight: 900;
font-size: 0.85em;
color: #6b4423;
padding: 0 4px 1px;
border-bottom: 1px solid rgba(107,68,35,0.3);
}
.f23b__transforms-cell,
.f23b__transforms-arrow {
display: table-cell;
text-align: center;
padding: 1px 6px;
vertical-align: middle;
}
.f23b__transforms-cell { color: #1a1a1a; font-weight: 500; }
.f23b__transforms-cell--to { color: #a14101; font-weight: 700; }
.f23b__transforms-arrow { font-weight: 700; color: #9c6f3f; }
/* row title (subsection label) — figma 의 좌측 td-label 같은 row-bound label */
.f23b__item-title {
font-weight: 900;
color: #6b4423;
margin-bottom: 2px;
font-size: 0.95em;
}
</style>
<div class="f23b" data-frame-id="1171281203" data-template-id="app_sw_package_vs_solution">
<div class="f23b__title">{{ slot_payload.title }}</div>
<div class="f23b__tbl">
{# header row — 2 col (label col 제거, 사용자 lock 2026-05-15) #}
<div class="f23b__th f23b__th--a">{{ slot_payload.col_a_label | safe }}</div>
<div class="f23b__th f23b__th--b">{{ slot_payload.col_b_label | safe }}</div>
{# data rows — col_a_body.sections 와 col_b_body.sections 를 row 별 페어 #}
{% set a_secs = slot_payload.col_a_body.sections or [] %}
{% set b_secs = slot_payload.col_b_body.sections or [] %}
{% set row_count = [a_secs | length, b_secs | length] | max %}
{% for i in range(row_count) %}
{% set a = a_secs[i] if i < (a_secs | length) else none %}
{% set b = b_secs[i] if i < (b_secs | length) else none %}
{% set row_class = 'f23b__row--' ~ ('odd' if loop.index0 % 2 == 0 else 'even') %}
{# col_a cell #}
<div class="f23b__td {{ row_class }}" style="--max-body-lines: {{ (a.text_lines | length if a and a.text_lines else 1) + (a.transforms | length if a and a.transforms else 0) }};">
{% if a %}
{% if a.title %}<div class="f23b__item-title">{{ a.title | safe }}</div>{% endif %}
{% if a.transforms %}
<div class="f23b__transforms">
<div class="f23b__transforms-head">
<span class="f23b__transforms-from">AS-IS</span>
<span></span>
<span class="f23b__transforms-to">TO-BE</span>
</div>
{% for t in a.transforms %}
<div class="f23b__transforms-row">
<div class="f23b__transforms-cell f23b__transforms-cell--from">{{ t.from | safe }}</div>
<div class="f23b__transforms-arrow"></div>
<div class="f23b__transforms-cell f23b__transforms-cell--to">{{ t.to | safe }}</div>
</div>
{% endfor %}
</div>
{% elif a.text_lines %}
{% for line in a.text_lines %}<div class="text-line{% if line.indent and line.indent > 0 %} text-line--sub{% endif %}">{{ line.text | safe }}</div>{% endfor %}
{% endif %}
{% endif %}
</div>
{# col_b cell #}
<div class="f23b__td {{ row_class }}" style="--max-body-lines: {{ (b.text_lines | length if b and b.text_lines else 1) + (b.transforms | length if b and b.transforms else 0) }};">
{% if b %}
{% if b.title %}<div class="f23b__item-title">{{ b.title | safe }}</div>{% endif %}
{% if b.transforms %}
<div class="f23b__transforms">
<div class="f23b__transforms-head">
<span class="f23b__transforms-from">AS-IS</span>
<span></span>
<span class="f23b__transforms-to">TO-BE</span>
</div>
{% for t in b.transforms %}
<div class="f23b__transforms-row">
<div class="f23b__transforms-cell f23b__transforms-cell--from">{{ t.from | safe }}</div>
<div class="f23b__transforms-arrow"></div>
<div class="f23b__transforms-cell f23b__transforms-cell--to">{{ t.to | safe }}</div>
</div>
{% endfor %}
</div>
{% elif b.text_lines %}
{% for line in b.text_lines %}<div class="text-line{% if line.indent and line.indent > 0 %} text-line--sub{% endif %}">{{ line.text | safe }}</div>{% endfor %}
{% endif %}
{% endif %}
</div>
{% endfor %}
</div>
</div>
@@ -0,0 +1,194 @@
<!-- Phase Z-2 신규 frame partial — Frame 9 (1171281180) pre_construction_model_info_stacked.
2026-05-14 — V4 04-1 top rank-1 매칭 (의미 conf=0.722) + structure cardinality 5 일치.
사용자 룰 부합 : Figma visual (계단 pill / 5 색상 / vertical label) 유지 + 콘텐츠 cardinality
에 맞춰 보완 (pill 안 1 라인 → multi-line: pill_N_label + pill_N_body). -->
{#
─────────────────────────────────────────────────────────────────────────────
Visual Provenance — figma_to_html_agent/blocks/1171281180/index.html
─────────────────────────────────────────────────────────────────────────────
PROMOTED (Figma 1:1):
- title-bar background : #fbd5b9 (line 43) + box-shadow
- title text gradient : #cc5200 (line 128) 강조부
- vertical label color : #144838 (line 68) + text-shadow drop
- pill background : rgba(255,255,255,0.5) (line 88)
- pill border-radius : 30px (line 89)
- pill box-shadow : 2px 4px 5px rgba(0,0,0,0.5) (line 90)
- 5 pill border-bottom 색상 (line 141, 147, 153, 159, 165):
1: #fb5915, 2: #e79000, 3: #e9a804, 4: #919f00, 5: #0d6361
ADAPTED (zone flex 재구성):
- 1153×592 absolute → flex column layout
- pill height 70px hardcode → dynamic (콘텐츠 양에 따라 auto)
- 계단 배치 (width/margin-left hardcode) → 가운데 정렬 + width 100%
(계단은 1 라인 짧은 텍스트 전제, multi-line 콘텐츠엔 부적합)
- 1 pill 1 라인 → pill_N_label (title) + pill_N_body (text_lines)
(사용자 룰 = frame 구조를 콘텐츠에 맞춰 보완)
NOT PROMOTED:
- 좌측 arc-deco SVG (장식 — 공간 절약 위해 단순화)
- arrow PNG (CSS triangle 대체 가능, 일단 ▶ 문자 사용)
slots : title + pill_N_label / pill_N_body (N = 1.._slot_count) — dynamic.
#}
<style>
.f9b {
width: 100%; height: 100%;
display: flex; flex-direction: column;
gap: 6px;
font-family: 'Noto Sans KR', 'Pretendard', sans-serif;
word-break: keep-all;
position: relative;
}
/* 2026-05-14 — title 스타일 통일 (frame 29 process_product_two_way 와 동일 패턴) :
배경 box + box-shadow 제거, gradient 텍스트 만 유지. 사용자 룰 Q1. */
.f9b__title {
font-size: var(--font-zone-title);
font-weight: 700;
line-height: var(--lh-zone-title);
background-image: linear-gradient(180deg, #000 0%, #883700 100%);
-webkit-background-clip: text; background-clip: text;
color: transparent;
flex-shrink: 0;
padding-left: 4px;
}
.f9b__body {
flex: 1;
display: flex;
flex-direction: row;
gap: 8px;
min-height: 0;
}
/* 좌측 vertical label — 콘텐츠 있을 때만 표시 */
.f9b__vlabel {
flex: 0 0 auto;
width: 56px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: var(--font-sub-title);
line-height: 1.2;
color: #144838;
text-align: center;
writing-mode: vertical-rl;
text-orientation: upright;
text-shadow: 0 2px 2px rgba(0,0,0,0.15);
}
/* 2026-05-14 — pill 배치 vertical → horizontal (grid). 5 pills 를 가로로 나열 →
우측 공란 활용 + 세로 공간 절약. auto-fit + minmax 으로 N 동적.
gap 넓찍 (사용자 lock 2026-05-14). */
.f9b__pill-rows {
flex: 1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 10px;
min-height: 0;
}
/* 2026-05-14 — bullet 줄간격 1/3 으로 압축 + 다른 간격 (padding, label↔body) 살짝 늘림. */
.f9b__pill {
min-height: 0;
background: rgba(255,255,255,0.7);
border-radius: 10px;
border-bottom: 3px solid;
box-shadow: 1px 2px 4px rgba(0,0,0,0.15);
padding: 8px 12px;
display: flex;
flex-direction: column;
gap: 5px;
overflow: hidden;
align-self: start;
}
/* Figma frame 9 의 5 색상 — nth-child cycle. N>5 면 default border-bottom 색 (회색). */
.f9b__pill:nth-child(5n+1) { border-bottom-color: #fb5915; }
.f9b__pill:nth-child(5n+2) { border-bottom-color: #e79000; }
.f9b__pill:nth-child(5n+3) { border-bottom-color: #e9a804; }
.f9b__pill:nth-child(5n+4) { border-bottom-color: #919f00; }
.f9b__pill:nth-child(5n) { border-bottom-color: #0d6361; }
.f9b__pill-label {
font-size: var(--font-sub-title);
font-weight: 900;
line-height: 1.2;
color: #144838;
margin-bottom: 4px;
display: flex;
align-items: center;
gap: 6px;
}
/* arrow indicator — Figma frame 9 의 좌측 화살표 (▶) approximation */
.f9b__pill-label::before {
content: "▶";
flex-shrink: 0;
color: currentColor;
font-size: 0.85em;
}
/* 2026-05-14 — bullet 줄간격 1/3 으로 압축 : line-height 1.3 → 1.05 (약 1/3 감소).
bullet 간 gap 2 → 1 로 더 압축. 가독성 임계 (한 bullet 안 line wrap 시 라인 겹침 직전). */
.f9b__pill-body {
flex: 1 1 auto;
overflow: hidden;
min-height: 0;
color: #0c271e;
font-size: var(--font-body);
line-height: 1.05;
display: flex;
flex-direction: column;
gap: 1px;
}
/* 첫 line = 인용구 (italic + 무 마커) — 04-1 의 따옴표 인용구 패턴 */
.f9b__pill-body .text-line:first-child {
font-style: italic;
color: #5C3714;
padding-left: 0;
}
/* 이후 lines = 각 이슈 bullet (▪ 마커로 구분 강화) */
.f9b__pill-body .text-line:not(:first-child) {
position: relative;
padding-left: 12px;
}
.f9b__pill-body .text-line:not(:first-child)::before {
content: "▪";
position: absolute;
left: 0;
color: #919f00;
font-size: 0.9em;
}
</style>
<div class="f9b" data-frame-id="1171281180" data-template-id="pre_construction_model_info_stacked">
<div class="f9b__title">{{ slot_payload.title }}</div>
<div class="f9b__body">
{# vertical label — slot_payload.vlabel 있을 때만 표시 (현재 v0 미사용) #}
{% if slot_payload.vlabel %}
<div class="f9b__vlabel">{{ slot_payload.vlabel | safe }}</div>
{% endif %}
<div class="f9b__pill-rows">
{% set slot_count = slot_payload._slot_count or 5 %}
{% for n in range(1, slot_count + 1) %}
{% set label = slot_payload['pill_' ~ n ~ '_label'] %}
{% set body = slot_payload['pill_' ~ n ~ '_body'] %}
{% if label or body %}
<div class="f9b__pill" data-frame-slot-id="pill_dynamic" data-pill-n="{{ n }}">
{% if label %}
<div class="f9b__pill-label">{{ label | safe }}</div>
{% endif %}
{% if body %}
<div class="f9b__pill-body">
{% for line in body %}<div class="text-line{% if line.indent and line.indent > 0 %} text-line--indent-{{ line.indent }}{% endif %}">{{ line.text | safe }}</div>{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
@@ -0,0 +1,134 @@
<!-- Phase Z-2 MVP-1 scaffold — MVP-2 이후 정식 Family CSS partial 로 대체. legacy structures/* 는 stylization 참고만. -->
{# Frame 29 (1171281210) / process_product_two_way
slots: title, banner_{left, right}, process/product (semantic naming)
family: compare / layout: banner-top-2col-bottom / cardinality: 2
시각 언어 (analysis.md + preview.png 참고 — frame 29 미변환이라 한계) :
- 상단 배너 2 개 : 좌(과정) 우(결과)
- 하단 2 col : 각각 변환 카드 (AS-IS → TO-BE) 또는 bullets
MVP-1 한계 명시 :
- frame 29 는 figma_to_html_agent 1:1 변환 미존재 (analysis 만)
- banner 그라데이션 / cell 색은 frame 13 의 시각 언어와 일관 (blue 기반) #}
<style scoped>
.frame-pp2w {
display: flex;
flex-direction: column;
height: 100%;
gap: 4px;
padding: 4px 0;
}
.frame-pp2w__title {
font-size: var(--font-zone-title);
font-weight: var(--weight-zone-title);
line-height: var(--lh-zone-title);
color: #1e293b;
padding-left: 4px;
}
.frame-pp2w__banners {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
}
.frame-pp2w__banner {
padding: 4px 10px;
background: linear-gradient(90deg, #1e40af 0%, #2563eb 100%);
color: #fff;
font-size: var(--font-sub-title);
font-weight: var(--weight-sub-title);
line-height: var(--lh-sub-title);
text-align: center;
border-radius: 3px;
}
.frame-pp2w__cols {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
flex: 1;
min-height: 0;
}
.frame-pp2w__col {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
overflow: hidden;
}
/* AS-IS → TO-BE transform 카드 */
.frame-pp2w__transform {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 4px;
align-items: center;
font-size: var(--font-caption);
font-weight: var(--weight-caption);
line-height: var(--lh-caption);
}
.frame-pp2w__cell {
padding: 3px 6px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 3px;
color: #475569;
overflow: hidden;
}
.frame-pp2w__cell--to {
background: #dbeafe;
border-color: #93c5fd;
color: #1e40af;
font-weight: 700;
}
.frame-pp2w__arrow {
color: #94a3b8;
font-weight: 700;
font-size: var(--font-body);
}
/* bullets (transforms 없거나 추가 항목) */
.frame-pp2w__bullets {
font-size: var(--font-caption);
font-weight: var(--weight-caption);
line-height: var(--lh-caption);
color: #475569;
overflow: hidden;
}
.frame-pp2w__bullets ul { padding-left: 12px; margin-top: 2px; }
.frame-pp2w__bullets li { margin-bottom: 2px; }
.frame-pp2w__bullets strong { color: #1e293b; font-weight: 700; }
</style>
<div class="frame-pp2w" data-frame-id="1171281210" data-template-id="process_product_two_way">
<div class="frame-pp2w__title">{{ slot_payload.title }}</div>
<div class="frame-pp2w__banners">
<div class="frame-pp2w__banner">{{ slot_payload.banner_left }}</div>
<div class="frame-pp2w__banner">{{ slot_payload.banner_right }}</div>
</div>
<div class="frame-pp2w__cols">
<div class="frame-pp2w__col" data-col="process">
{% if slot_payload.process.transforms %}
{% for t in slot_payload.process.transforms %}
<div class="frame-pp2w__transform">
<div class="frame-pp2w__cell">{{ t.from }}</div>
<div class="frame-pp2w__arrow"></div>
<div class="frame-pp2w__cell frame-pp2w__cell--to">{{ t.to }}</div>
</div>
{% endfor %}
{% endif %}
{% if slot_payload.process.bullets %}
<div class="frame-pp2w__bullets">{{ slot_payload.process.bullets | safe }}</div>
{% endif %}
</div>
<div class="frame-pp2w__col" data-col="product">
{% if slot_payload.product.transforms %}
{% for t in slot_payload.product.transforms %}
<div class="frame-pp2w__transform">
<div class="frame-pp2w__cell">{{ t.from }}</div>
<div class="frame-pp2w__arrow"></div>
<div class="frame-pp2w__cell frame-pp2w__cell--to">{{ t.to }}</div>
</div>
{% endfor %}
{% endif %}
{% if slot_payload.product.bullets %}
<div class="frame-pp2w__bullets">{{ slot_payload.product.bullets | safe }}</div>
{% endif %}
</div>
</div>
</div>
@@ -0,0 +1,115 @@
<!-- Phase Z-2 MVP-1 scaffold — MVP-2 이후 정식 Family CSS partial 로 대체. legacy structures/* 는 stylization 참고만. -->
{# Frame 13 (1171281190) / three_parallel_requirements
slots: title, pillar_{1,2,3}_{label, body}
family: list / layout: 3-column / cardinality: 3
시각 언어 도출 (figma_to_html_agent/blocks/1171281190/index.html 참고) :
- 3 columns + 각 좌측 color bar (gradient)
- 색상 : 기술 blue / 사람 orange / 자연 green (Figma 색)
- 각 col 2 sections (heading + desc) — MVP-1 은 단일 body 로 단순화
- top/bottom 검정 border (col 외곽)
MVP-1 단순화 :
- zoom 안 씀 — 기존 px 직접 (compact scale)
- 한자 (技/人/天) 안 씀 — 라벨만 #}
<style scoped>
.frame-3par {
display: flex;
flex-direction: column;
height: 100%;
gap: 4px;
padding: 4px 0;
}
.frame-3par__title {
font-size: var(--font-zone-title);
font-weight: var(--weight-zone-title);
line-height: var(--lh-zone-title);
color: #1e293b;
padding-left: 4px;
}
.frame-3par__cols {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 8px;
flex: 1;
min-height: 0;
}
.frame-3par__pillar {
display: flex;
flex-direction: row;
border-top: 1.5px solid #1e293b;
border-bottom: 1.5px solid #1e293b;
min-width: 0;
overflow: hidden;
}
/* 좌측 color bar (Figma 색상 도출) */
.frame-3par__bar {
flex: 0 0 36px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: var(--font-caption);
font-weight: 700;
text-align: center;
writing-mode: vertical-rl;
text-orientation: mixed;
padding: 4px 0;
line-height: 1.2;
}
/* 3 색 — Figma 추출값 */
.frame-3par__pillar:nth-child(1) .frame-3par__bar {
background: linear-gradient(180deg, #0D78D0 0%, #023056 100%);
}
.frame-3par__pillar:nth-child(2) .frame-3par__bar {
background: linear-gradient(180deg, #FF9A23 0%, #CC5200 100%);
}
.frame-3par__pillar:nth-child(3) .frame-3par__bar {
background: linear-gradient(180deg, #39BE49 21%, #23742C 100%);
}
.frame-3par__body-wrap {
flex: 1;
display: flex;
flex-direction: column;
padding: 6px 8px;
gap: 4px;
min-width: 0;
overflow: hidden;
}
.frame-3par__label {
font-size: var(--font-sub-title);
font-weight: var(--weight-sub-title);
line-height: var(--lh-sub-title);
color: #1e293b;
padding-bottom: 3px;
border-bottom: 1px dashed #94a3b8;
}
.frame-3par__pillar:nth-child(1) .frame-3par__label { color: #134D7F; }
.frame-3par__pillar:nth-child(2) .frame-3par__label { color: #D18B37; }
.frame-3par__pillar:nth-child(3) .frame-3par__label { color: #23742C; }
.frame-3par__body {
font-size: var(--font-body);
font-weight: var(--weight-body);
line-height: var(--lh-body);
color: #475569;
overflow: hidden;
}
.frame-3par__body ul { padding-left: 14px; margin-top: 2px; }
.frame-3par__body li { margin-bottom: 2px; }
.frame-3par__body strong { color: #1e293b; font-weight: 700; }
</style>
<div class="frame-3par" data-frame-id="1171281190" data-template-id="three_parallel_requirements">
<div class="frame-3par__title">{{ slot_payload.title }}</div>
<div class="frame-3par__cols">
{% for pillar in slot_payload.pillars %}
<div class="frame-3par__pillar">
<div class="frame-3par__bar">{{ pillar.label }}</div>
<div class="frame-3par__body-wrap">
<div class="frame-3par__label">{{ pillar.label }}</div>
<div class="frame-3par__body">{{ pillar.body | safe }}</div>
</div>
</div>
{% endfor %}
</div>
</div>