Files
C.E.L_Slide_test2/templates/phase_z2/families/three_persona_benefits.html
kyeongmin c1df656312 feat(#65): IMP-36 fit/rotation generalization (u1~u8)
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>
2026-05-23 01:18:20 +09:00

272 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 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>