Generalize Phase Z frame partial responsive fit / rotation to four canonical
F13/F14/F20/F8 family partials. Surface = 13 canonical partials; 19
builder-only contracts remain explicitly out of scope.
u1 test_imp17_comment_anchor: re-pin L570->L578 (restructure+IMP-17),
L571->L579 (IMP-29 -> IMP-47B supersession). Stage 1 red baseline gate.
u2 frame_contracts.yaml: add rotation_eligible (P1) + body_fit_pattern2 (P2)
bool axes on 13 partial-backed contracts. P1 True: F13/F14/F20/F8 (4).
P2 True: F23 + P1_set (5). F29 columns[1].body_parser column_plain ->
column_with_transform (P3 parity).
u3 test_imp36_fit_rotation_generalization (NEW, 166 lines): static
parametrized assertions for P1 metadata + CQ presence, P1 opt-out
absence, P2 --max-body-lines + clamp + cqh, P2 opt-out absence, 19
builder-only exclusion.
u4 three_parallel_requirements (F13): introduce f13b-root container-name +
container-type:size + @container (aspect-ratio<1.5) rotation;
add inline --max-body-lines + body line-height clamp/cqh/calc.
u5 three_persona_benefits (F14): f14b-root P1 + P2 cqh/jinja body fit.
Persona colors (#285b4a/#445a2f/#743002) and circle SVG aspect 1/1
preserved.
u6 dx_sw_necessity_three_perspectives (F20): f20b-root P1 + P2 cqh/jinja
body fit under IMP-49 partial-fidelity lock.
u7 info_management_what_how_when (F8): f8b-root P1 + P2 cqh/jinja body fit.
u8 test_imp36_overflow_chain_self_fire (NEW, 299 lines): Selenium self-fire
harness for F13/F14/F20/F8 at aspect 1.78 vs 1.0. Asserts line-height
changes, font-size invariance across all 4 frames (no per-frame exempt),
grid columns rotate 3 -> 1, OVERFLOW_CASCADE_ORDER remains 4-tuple.
Stage 4 verification (HEAD 6f1c736 pre-commit baseline):
u1 2/2 PASS, u3 33/33 PASS, u8 9/9 PASS (live Chrome).
Regression sweep tests/phase_z2 + tests/orchestrator_unit 335/335 PASS.
font-size mutations introduced: 0.
Pre-existing red (test_imp47b_step12_ai_wiring x3, ai_fallback_master_flag
default_off x1) verified unchanged via stash swap -> not introduced.
Guardrails honored:
- cqh / clamp / container query only (no shared margin/padding/gap shrink).
- font-size invariant under aspect change (P2 mutates line-height +
--max-body-lines only).
- No cross-frame .fNb__ class borrowing (IMP-49 partial-fidelity lock).
- F14 circle SVG aspect 1/1 untouched; persona colors preserved.
- AI isolation: no HTML structure generation; AI calls remain zone-content.
- 1 turn = 1 step; commit excludes .claude/settings.json and all
out-of-scope untracked worktree per Stage 4 binding contract.
source_comment_ids: Stage 1 #13/#14; Stage 2 #21/#22; Stage 3 #4 + Codex #4
YES; Stage 4 Claude #1 + Codex #3 PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
272 lines
13 KiB
HTML
272 lines
13 KiB
HTML
<!-- Phase Z-2 MVP-1.5b frame-derived adapted block.
|
||
§17 룰 — Figma 시각 언어 promote, geometry 만 zone-compatible adapt. -->
|
||
{#
|
||
─────────────────────────────────────────────────────────────────────────────
|
||
Visual Provenance — figma_to_html_agent/blocks/1171281191/ (frame 14)
|
||
─────────────────────────────────────────────────────────────────────────────
|
||
Frame 14 = "주체별 기대효과" (cards-3col-persona, 2601×1927 px, scale 0.49213).
|
||
3 컬럼 카드 : 발주자 / 시공자 / 설계자 각 persona 별 *목표* + bullet 7 개 안팎.
|
||
본 partial 은 `figma_to_html_agent/blocks/1171281191/index.html` (184 lines) 을
|
||
base 로 Phase Z 규약 (Jinja slot + token CSS + slide-base wrap) adapt.
|
||
|
||
본 commit (Track A 1/16 second refinement, F2 re-do post-Codex round 22 catch) :
|
||
asset-promotion policy = per-asset case-by-case (round 13 §2.3 + round 24 합의).
|
||
strict CSS-only over-generalization (first refinement `2fcd8bb`) 정정.
|
||
|
||
PROMOTED — RASTER (Phase Z `copy_assets()` 가 figma_to_html_agent/blocks/1171281191/
|
||
assets/ → {run_dir}/assets/three_persona_benefits/ 복사) :
|
||
- col_bg_texture (4a17cd1d...png) — 3 컬럼 BG 텍스처. CSS noise/gradient 근사 보다 fidelity 보존.
|
||
- bottom photo client (d2c070f2...png) — 발주자 카드 하단 사진
|
||
- bottom photo constructor (2a6a58e7...png) — 시공자 카드 하단 사진
|
||
- bottom photo designer (39113493...png) — 설계자 카드 하단 사진
|
||
- badge outer client (77f31997...png) — 발주자 round badge outer ring
|
||
- badge inner client (e64c967d...png) — 발주자 round badge inner disk
|
||
- badge outer constructor (1550ec75...png)
|
||
- badge inner constructor (85beaf9d...png)
|
||
- badge outer designer (9ac089fa...png)
|
||
- badge inner designer (4b534ccf...png)
|
||
→ 10 raster (1 BG + 3 photos + 6 badge). Codex round 24 tightening : badge =
|
||
default promote (asset-capable path 검증).
|
||
|
||
PROMOTED — CSS (Figma 색/디자인 의도 → CSS 으로 충분) :
|
||
- 발주자 accent color : Figma TEXT #285b4a (dark green) — index.html L80
|
||
- 시공자 accent color : Figma TEXT #445a2f (olive) — index.html L81
|
||
- 설계자 accent color : Figma TEXT #743002 (red-brown) — index.html L82
|
||
- col-overlay tint : #d6e7c4 / #e1efe1 / #d0c0ad (opacity-80 — solid 색, CSS equivalent)
|
||
- title gradient : #000 → #883700 (F13 zone-title 와 동일 family)
|
||
- bullet check marker : ✓ unicode (실제 png 6896d5c2 와 동등)
|
||
|
||
NOT PROMOTED (MDX 무관 + Figma 데코) :
|
||
- 한자/장식 텍스트 (Figma 안 deco) — index.html 에도 없음. N/A.
|
||
|
||
ADAPTED :
|
||
- Figma 65/50/40px → token-fixed (zone-title 13 / sub-title 12 / caption / body 11)
|
||
- Figma 396×397 absolute round badge → flex card-header 안 round image
|
||
- Figma `zoom: 0.49213` absolute positioning → Phase Z flex column (zone fit)
|
||
- Figma 7 bullets absolute layout → flex column gap auto spacing
|
||
─────────────────────────────────────────────────────────────────────────────
|
||
min_height_px derivation (round 13 §2.2 — derive + confirm) :
|
||
Figma frame 1927 px @ scale 0.49213 → 948 px adapted (full Figma fit).
|
||
Phase Z slide-body ≤ 585 px → adaptive content fit.
|
||
Content density (3 col × 7 bullets + badge + photo) → F29 (345) 보다 약간 ↑.
|
||
Derived = 320 (badge 30 + bullet body ~210 + photo strip ~50 + padding 30).
|
||
Confirm via smoke + V2 validation (MDX 02 sample run).
|
||
─────────────────────────────────────────────────────────────────────────────
|
||
slots : title, personas[].{label, body, color_class}
|
||
- color_class ∈ {client, constructor, designer} (role_order 따라)
|
||
- body = list[{text:str, indent:int}] (parse_quadrant_item 출력)
|
||
- persona suffix "목표" = frame 의 visual 언어 (slot 미사용, partial 노출)
|
||
─────────────────────────────────────────────────────────────────────────────
|
||
Asset path runtime resolution :
|
||
Phase Z `copy_assets(template_id, run_dir)` (phase_z2_pipeline.py:746) copies
|
||
figma_to_html_agent/blocks/1171281191/assets/ → {run_dir}/assets/three_persona_benefits/
|
||
→ partial 의 src="assets/three_persona_benefits/<filename>.png" 가 runtime 에서 resolve.
|
||
#}
|
||
|
||
<style>
|
||
.f14b {
|
||
width: 100%; height: 100%;
|
||
display: flex; flex-direction: column;
|
||
gap: 6px;
|
||
font-family: 'Noto Sans KR', 'Pretendard', sans-serif;
|
||
word-break: keep-all;
|
||
/* IMP-36 (Gitea #65 u5) P1 — partial-side container query root.
|
||
container-type: size 로 aspect-ratio 측정 가능 (cqh / cqi / cqw 도
|
||
동일 root 기준). container-name: f14b-root 는 frame_contracts.yaml
|
||
rotation_eligible: true 와 짝. Circle badge (.f14b__badge aspect-ratio
|
||
1/1) 는 별도 element — 본 root 의 aspect-ratio 측정 대상 아님. */
|
||
container-type: size;
|
||
container-name: f14b-root;
|
||
}
|
||
.f14b__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));
|
||
}
|
||
.f14b__cols {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 10px;
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
}
|
||
.f14b__col {
|
||
position: relative;
|
||
display: flex; flex-direction: column;
|
||
border: 2px solid;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
min-height: 0;
|
||
isolation: isolate;
|
||
}
|
||
/* col-bg 텍스처 (PROMOTED raster) */
|
||
.f14b__col::before {
|
||
content: ""; position: absolute; inset: 0;
|
||
background: url("assets/three_persona_benefits/4a17cd1dddaba8a220b706df3ec052d2bfde4f47.png") center/cover no-repeat;
|
||
opacity: 0.6;
|
||
z-index: -2;
|
||
}
|
||
/* col-overlay (CSS solid tint per persona — index.html L44-47) */
|
||
.f14b__col::after {
|
||
content: ""; position: absolute; inset: 0;
|
||
opacity: 0.8;
|
||
z-index: -1;
|
||
}
|
||
.f14b__col--client { border-color: #285b4a; }
|
||
.f14b__col--constructor { border-color: #445a2f; }
|
||
.f14b__col--designer { border-color: #743002; }
|
||
.f14b__col--client::after { background: #d6e7c4; } /* PROMOTED CSS */
|
||
.f14b__col--constructor::after { background: #e1efe1; } /* PROMOTED CSS */
|
||
.f14b__col--designer::after { background: #d0c0ad; } /* PROMOTED CSS */
|
||
|
||
/* badge — PROMOTED raster (outer + inner ring image, name + 목표 text overlay) */
|
||
.f14b__badge {
|
||
position: relative;
|
||
display: flex; flex-direction: column;
|
||
align-items: center; justify-content: center;
|
||
padding: 6px;
|
||
flex-shrink: 0;
|
||
aspect-ratio: 1 / 1;
|
||
width: 70px; height: 70px;
|
||
margin: 6px auto 4px;
|
||
}
|
||
.f14b__badge-outer, .f14b__badge-inner {
|
||
position: absolute; inset: 0;
|
||
width: 100%; height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
.f14b__badge-inner {
|
||
width: 78%; height: 78%;
|
||
top: 11%; left: 11%;
|
||
}
|
||
.f14b__badge-text {
|
||
position: relative; z-index: 1;
|
||
display: flex; flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
line-height: 1;
|
||
}
|
||
.f14b__badge-name {
|
||
font-size: var(--font-caption);
|
||
font-weight: 700;
|
||
letter-spacing: -0.04em;
|
||
}
|
||
.f14b__badge-suffix {
|
||
font-size: 0.65rem;
|
||
font-weight: 500;
|
||
margin-top: 2px;
|
||
opacity: 0.85;
|
||
}
|
||
.f14b__col--client .f14b__badge-text { color: #285b4a; }
|
||
.f14b__col--constructor .f14b__badge-text { color: #445a2f; }
|
||
.f14b__col--designer .f14b__badge-text { color: #743002; }
|
||
|
||
/* body — bullet list. check marker CSS (PROMOTED CSS, raster 6896d5c2 와 동등) */
|
||
.f14b__body {
|
||
flex: 1 1 auto;
|
||
overflow: hidden;
|
||
padding: 4px 10px;
|
||
color: #1a1a1a;
|
||
min-height: 0;
|
||
display: flex; flex-direction: column;
|
||
gap: 3px;
|
||
}
|
||
.f14b__body .text-line {
|
||
color: inherit;
|
||
font-size: var(--font-body);
|
||
line-height: var(--lh-body);
|
||
position: relative;
|
||
padding-left: 14px;
|
||
}
|
||
/* IMP-36 (Gitea #65 u5) P2 — body fit via cqh + clamp + --max-body-lines.
|
||
persona.body 의 bullet 개수가 늘면 line-height 가 비례로 축소. font-size
|
||
미변경 (사용자 룰). --max-body-lines fallback = 7 (Figma 원본 frame 평균
|
||
bullet 수, file header L8 참조). 60cqh = .f14b__body 영역 비율 근사치
|
||
(title ≈ 15cqh + badge ≈ 18cqh + photo ≈ 7cqh → body ≈ 60cqh). 본 clamp
|
||
은 .text-line 의 var(--lh-body) 를 override (cascade 우선순위). */
|
||
.f14b__body .text-line {
|
||
line-height: clamp(1.15em, calc(60cqh / var(--max-body-lines, 7)), 1.6em);
|
||
}
|
||
/* IMP-36 (Gitea #65 u5) P1 — aspect-ratio < 1.5 rotation rule. zone 의
|
||
가로:세로 비가 1.5 미만으로 좁아지면 3-col grid 가 1-col stack 으로
|
||
회전. Circle badge (.f14b__badge aspect-ratio 1/1) 는 col 내부 element
|
||
이므로 회전 후에도 원형 유지. */
|
||
@container f14b-root (aspect-ratio < 1.5) {
|
||
.f14b__cols { grid-template-columns: 1fr; }
|
||
}
|
||
.f14b__body .text-line--bullet::before {
|
||
content: "\2713";
|
||
position: absolute;
|
||
left: 0; top: 0;
|
||
font-weight: 700;
|
||
}
|
||
.f14b__col--client .f14b__body .text-line--bullet::before { color: #285b4a; }
|
||
.f14b__col--constructor .f14b__body .text-line--bullet::before { color: #445a2f; }
|
||
.f14b__col--designer .f14b__body .text-line--bullet::before { color: #743002; }
|
||
|
||
/* bottom photo — PROMOTED raster (per persona 실사 사진, CSS 불가) */
|
||
.f14b__photo {
|
||
flex-shrink: 0;
|
||
width: 100%; height: 36px;
|
||
overflow: hidden;
|
||
border-radius: 0 0 8px 8px;
|
||
margin-top: 2px;
|
||
}
|
||
.f14b__photo img {
|
||
width: 100%; height: 100%;
|
||
object-fit: cover;
|
||
opacity: 0.7; /* index.html L53 — opacity 0.70 */
|
||
display: block;
|
||
}
|
||
</style>
|
||
|
||
<div class="f14b" data-frame-id="1171281191" data-template-id="three_persona_benefits">
|
||
<div class="f14b__title">{{ slot_payload.title }}</div>
|
||
<div class="f14b__cols">
|
||
{% for persona in slot_payload.personas %}
|
||
<div class="f14b__col f14b__col--{{ persona.color_class }}">
|
||
{# badge — raster outer/inner + CSS text overlay #}
|
||
<div class="f14b__badge">
|
||
{% if persona.color_class == "client" %}
|
||
<img class="f14b__badge-outer" src="assets/three_persona_benefits/77f319979c880da34ff3d423fcd97827f636c01e.png" alt="">
|
||
<img class="f14b__badge-inner" src="assets/three_persona_benefits/e64c967dd00302bfbef6cfbcbb4f7a4db5d9d96c.png" alt="">
|
||
{% elif persona.color_class == "constructor" %}
|
||
<img class="f14b__badge-outer" src="assets/three_persona_benefits/1550ec755fa7922dcfc1c90135a570d6b9df82cc.png" alt="">
|
||
<img class="f14b__badge-inner" src="assets/three_persona_benefits/85beaf9dfc17b7ed4620729a086ba22143606517.png" alt="">
|
||
{% elif persona.color_class == "designer" %}
|
||
<img class="f14b__badge-outer" src="assets/three_persona_benefits/9ac089fa9c5106b6b26d47727003641bb56ba4b0.png" alt="">
|
||
<img class="f14b__badge-inner" src="assets/three_persona_benefits/4b534ccf4e945fbe7436a0d8d96a6deffcfe5cef.png" alt="">
|
||
{% endif %}
|
||
<div class="f14b__badge-text">
|
||
<span class="f14b__badge-name">{{ persona.label | safe }}</span>
|
||
<span class="f14b__badge-suffix">목표</span>
|
||
</div>
|
||
</div>
|
||
|
||
{# body — bullets with CSS check marker #}
|
||
<div class="f14b__body" style="--max-body-lines: {{ (persona.body | length) if persona.body else 7 }};">
|
||
{% if persona.body %}
|
||
{% for line in persona.body %}<div class="text-line text-line--bullet{% if line.indent > 0 %} text-line--indent-{{ line.indent }}{% endif %}">{{ line.text | safe }}</div>{% endfor %}
|
||
{% endif %}
|
||
</div>
|
||
|
||
{# bottom photo — per persona 실사 사진 #}
|
||
<div class="f14b__photo">
|
||
{% if persona.color_class == "client" %}
|
||
<img src="assets/three_persona_benefits/d2c070f200af83f563976b6b0f309d38321d204d.png" alt="">
|
||
{% elif persona.color_class == "constructor" %}
|
||
<img src="assets/three_persona_benefits/2a6a58e7bf7a645b5ede65115feb2890ccc414d1.png" alt="">
|
||
{% elif persona.color_class == "designer" %}
|
||
<img src="assets/three_persona_benefits/39113493f6e3ae76d766e86e293b0f0dcbf55d91.png" alt="">
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|