oh-my-design-cli 1.2.0 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -170,15 +170,321 @@ DESIGN.md가 없어서 reference 한 개를 골라 부트스트랩할게요. <ta
170
170
  - 카탈로그에 없는 id → "해당 id는 reference 카탈로그에 없어요. top-5 중에서 골라주세요."
171
171
  - `중단` → 종료
172
172
 
173
+ ### 3.7 라이브 reference capture (선택)
174
+
175
+ 선택된 reference의 라이브 사이트에서 디자인 토큰 + 시각 reference + 폰트를 가져오면 master phase 정확도가 크게 올라간다. 단일 mode (이전의 clone/inspired 갈래는 v1.3.3에서 폐기 — 시각 동일성은 brand creative work을 가져와야 가능하고, 그건 IP 영역).
176
+
177
+ 사용자에게 한 줄로 묻기:
178
+
179
+ ```
180
+ <id> 라이브 자료를 가져올까요? (토큰·구조 cue·폰트·hero screenshot)
181
+
182
+ 가져옴: 컬러·radius·간격·폰트(open-source CDN)·구조 cue(carousel/CTA 모양/nav)·voice register
183
+ 가져오지 않음: brand mascot 일러스트·마케팅 사진·로고(사용자 자체 자산으로 시작)
184
+
185
+ 답: yes / skip
186
+ ```
187
+
188
+ `yes` → omd:reference-capture skill 호출 (Skill 툴) → 끝나면 Step 4로
189
+ `skip` → 바로 Step 4
190
+
191
+ omd:reference-capture는 LICENSE-NOTE.md / attribution.md / fonts.json / structure.json 작성. 모든 brand-identifying 자산은 `assets/_reference/<id>/`에 reference 용도로 보존되며 사용자 product DOM에는 들어가지 않는다.
192
+
193
+ ## Step 3.8 — Surface signal 추출 (master prompt에 한 줄 전달)
194
+
195
+ 사용자 task에서 surface idiom 신호 추출. 이 신호는 master가 reference-capture 자료 중 어떤 부분을 더 무겁게 볼지 결정한다.
196
+
197
+ ### 키워드 → surface 매핑
198
+
199
+ | 키워드 (KR/EN/JP/TW) | surface_signal |
200
+ |---|---|
201
+ | `랜딩`, `홈`, `메인`, `landing`, `home`, `main`, `marketing`, `홍보`, `프로모션`, 「ランディング」, `首頁` | **`marketing`** |
202
+ | `대시보드`, `앱`, `화면`, `설정`, `관리`, `dashboard`, `app`, `settings`, `console`, `admin`, 「ダッシュボード」, `儀表板` | **`product`** |
203
+ | `문서`, `가이드`, `docs`, `documentation`, `help`, 「ドキュメント」, `文件` | **`docs`** |
204
+ | `온보딩`, `시작하기`, `가입`, `onboarding`, `signup`, 「オンボーディング」, `註冊` | **`onboarding`** |
205
+ | (위에 매치 없음) | **`null`** (master 자유 판단) |
206
+
207
+ ### Step 4 prompt에 포함되어야 할 필드
208
+
209
+ 기존 `<RUN_DIR + task + chosen_ref_id>` 에 surface_signal과 reference-capture 자료 경로를 명시:
210
+
211
+ ```
212
+ RUN_DIR: <path>
213
+ task: <user task>
214
+ chosen_ref_id: <id>
215
+ surface_signal: marketing | product | docs | onboarding | null
216
+ reference_capture_dir: assets/_reference/<id>/ | null
217
+ ```
218
+
219
+ reference_capture_dir이 존재하면 master는 그 디렉토리의 `tokens.json`, `structure.json`, `screenshots/*.png` 를 **모두 활용**한다 (canonical DESIGN.md만 보지 말 것).
220
+
173
221
  ## Step 4 — Master 호출 (handoff loop)
174
222
 
175
223
  Subagent (master)는 AskUserQuestion 직접 호출 불가 (main-thread 전용). file-based handoff 패턴으로 돌린다.
176
224
 
225
+ ### Master에게 강제되는 시각 grounding 규칙
226
+
227
+ master에게 전달되는 prompt 첫 단락은 **반드시** 다음을 포함:
228
+
229
+ ```
230
+ 시각 grounding 규칙 (위반 = regression):
231
+ 1. reference_capture_dir이 null이 아니면, master의 Component phase 진입 전에
232
+ `<reference_capture_dir>/screenshots/hero-desktop.png`을 Read 툴로 **이미지로**
233
+ 읽는다. 텍스트 분석만으로 끝내지 말 것 — 실제 hero composition
234
+ (carousel/illustration/data-card/text-only 등)을 시각 inspect 후 진행.
235
+ 2. `<reference_capture_dir>/structure.json`에 기록된 hero.type, cta.dominant_shape,
236
+ nav.structure, page.viewport_heights를 wireframe + component 결정에 반영.
237
+ surface_signal과 structure.json이 일치하면 그대로, 어긋나면 surface_signal 우선.
238
+ 3. `<reference_capture_dir>/tokens.json`의 live_overrides가 있으면 그 값을 우선
239
+ 사용. canonical DESIGN.md 값은 voice/principles/motion philosophy에만 절대 권위.
240
+ 4. `<reference_capture_dir>/fonts.json`의 `live_observed: true` 항목은 생성 HTML
241
+ `<head>`에 반드시 `html_link` 그대로 박을 것. 누락 시 시스템 fallback으로
242
+ 렌더되어 결과가 spec과 silently 어긋남 (예: Pretendard 미로드 → 둥근 시스템 폰트).
243
+ canonical DESIGN.md가 언급한 폰트라도 `live_observed: false`면 적용 금지
244
+ (대표 함정: "BM JUA accent only" 같은 spec 문구를 잘못 적용해서 둥근 디스플레이체로 헤더 렌더).
245
+ 5. **사용자 product 결과물 자산 정책** (단일 mode — 이전 clone/inspired 갈래는 v1.3.3 폐기):
246
+ - **brand creative work (mascot 일러스트·마케팅 사진·고유 ornament)는 product DOM 미사용.** captured logo/screenshot은 `assets/_reference/<id>/` 안에 reference로만 보존.
247
+ - 마케팅 카피·tagline·slogan은 라이브 사이트에서 verbatim 인용 금지. voice register + 문장 길이만 따르고 텍스트는 사용자 프로젝트 맥락에서 새로 작성.
248
+ - **헤더 logo = product name 워드마크만** (v1.3.7부터 — 별도 icon/shapes mark 금지):
249
+ ```html
250
+ <a class="logo" href="/" style="display:inline-flex;align-items:center;text-decoration:none;">
251
+ <span class="logo-wordmark">{{PRODUCT_NAME}}</span>
252
+ </a>
253
+ <style>
254
+ .logo-wordmark{
255
+ font-family:"Bricolage Grotesque","Space Grotesk","DM Serif Display","Fraunces",-apple-system,sans-serif;
256
+ font-size:22px;font-weight:700;letter-spacing:-0.04em;
257
+ color:var(--color-text-heading);
258
+ }
259
+ </style>
260
+ ```
261
+ - **그냥 폰트 + 텍스트**가 logo 역할. shapes mark / DiceBear icon / svg 마크 추가 금지 — 사용자가 짜증나는 "AI가 만든 generic 로고" 회피.
262
+ - 디스플레이 폰트는 `<head>`에 `<link rel="stylesheet">` 의무 로드. brand vibe별 선호:
263
+ - cool / minimal / fintech serious → **Space Grotesk** 또는 **Bricolage Grotesque** (geometric, modern sans)
264
+ - warm / playful / community → **Bricolage Grotesque** (variable, 친근)
265
+ - editorial / luxury / serif → **DM Serif Display** 또는 **Fraunces** (display serif)
266
+ - CDN URLs (omd:asset-fetch §6 참고, 모두 SIL OFL):
267
+ - `https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@400;500;600;700;800&display=swap`
268
+ - `https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap`
269
+ - `https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&display=swap`
270
+ - `https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300..900&display=swap`
271
+
272
+ - **{{PRODUCT_NAME}} 결정 logic**:
273
+ 1. 사용자 prompt에 명시된 product name 있으면 그걸로 (예: "내 가계부 앱 만들어줘" → 사용자에게 "이름은 뭘로 할까요? skip하면 placeholder"라고 한 줄 묻기 — 답 받으면 그대로, skip이면 step 2)
274
+ 2. 사용자 응답 없거나 임의 placeholder 필요 → **brand vibe 맞는 generic placeholder 1개**:
275
+
276
+ | brand vibe | placeholder 후보 (1개 선택) |
277
+ |---|---|
278
+ | cool / fintech / minimal | `Folio`, `Mint`, `Nord`, `Vector`, `Norma`, `Atlas` |
279
+ | warm / conversational / friendly | `Pop`, `Spark`, `Halo`, `Cosy`, `Hue` |
280
+ | community / local / marketplace | `Townie`, `Hood`, `Plot`, `Block`, `Brick` |
281
+ | editorial / commerce / curated | `Folio`, `Curator`, `Volume`, `Edit` |
282
+ | mobility / service | `Lane`, `Trip`, `Glide`, `Loop` |
283
+ | productivity / advisor | `Compass`, `Norm`, `Plan`, `Beacon` |
284
+
285
+ 3. 선택된 이름을 `<title>`, hero copy `[YOUR PRODUCT NAME]` 자리, footer brand 자리 등 **product 전체에 일관 적용** — 사용자가 swap할 때 grep 한 번으로 끝나도록 단일 토큰 유지.
286
+
287
+ - 사용자에게 한 줄 알림 (생성 후): "Product name으로 `Folio` 사용 — 본인 이름으로 바꾸려면 `landing.html`에서 `Folio` grep replace."
288
+
289
+ 6. **자산 라이브러리 — 검증된 CDN URL만 사용** (mandatory):
290
+
291
+ **각 항목 옆 timestamp는 마지막 200 OK 확인 일자.** 이 리스트에 있는 URL은 generator가 즉시 사용 가능. 이 리스트에 없는 자산은 generator가 임의로 URL 추측·생성 금지.
292
+
293
+ **CHARACTERS / AVATARS (hero 캐릭터 자리, CC0)**:
294
+ - DiceBear notionists — `https://api.dicebear.com/9.x/notionists/svg?seed=<seed>&size=<n>` ✓ 2026-05-14
295
+ - DiceBear lorelei — `https://api.dicebear.com/9.x/lorelei/svg?seed=<seed>&size=<n>` ✓ 2026-05-14
296
+ - DiceBear personas — `https://api.dicebear.com/9.x/personas/svg?seed=<seed>&size=<n>` ✓ 2026-05-14
297
+ - DiceBear 전체 스타일 카탈로그: `notionists | notionists-neutral | lorelei | personas | adventurer | avataaars | bottts | croodles | fun-emoji | thumbs` — 모두 CC0
298
+ - `seed` 값은 brand-relevant 토큰 사용 (예: `banksalad-advisor-001`) — deterministic 출력 보장
299
+
300
+ **ICONS (product 카테고리·feature 카드·nav)**:
301
+ - Lucide (ISC) — `https://cdn.jsdelivr.net/npm/lucide-static@latest/icons/<name>.svg` ✓ 2026-05-14
302
+ - Heroicons (MIT) — `https://cdn.jsdelivr.net/npm/heroicons@2/24/outline/<name>.svg` ✓ 2026-05-14
303
+ - Tabler Icons (MIT) — `https://cdn.jsdelivr.net/npm/@tabler/icons@latest/icons/<name>.svg` ✓ 2026-05-14
304
+
305
+ **PATTERNS / BACKGROUNDS**:
306
+ - Hero Patterns (CC BY 4.0) — `https://heropatterns.com` ✓ 2026-05-14 (도큐 보고 inline SVG 복사)
307
+ - 원형 그라데이션 ornament은 `radial-gradient + filter: blur()` CSS로 generator가 직접 생성 (brand-specific creative work 아님)
308
+
309
+ **FONTS (open-source)**:
310
+ - Pretendard (SIL OFL) — `https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css` ✓ 2026-05-14
311
+ - Wanted Sans (SIL OFL) — `https://cdn.jsdelivr.net/gh/wanteddev/wanted-sans@latest/packages/wanted-sans/fonts/webfonts/static/complete/WantedSans.css` ✓ 2026-05-14
312
+ - Noto Sans KR (SIL OFL) — `https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap` ✓ 2026-05-14
313
+ - Inter (SIL OFL) — Google Fonts
314
+
315
+ **이전 버전에 있던 미검증 항목 (skill 제거됨)**: Humaaans github raw / Open Peeps github raw / unDraw direct CDN / Storyset CDN — 직접 SVG URL 패턴 미공식, 404 빈번. 다시 등재하려면 verify 후 timestamp 부착.
316
+
317
+ **Fetch 시 절차**:
318
+ - 자산 1회 `curl -sIL -o /dev/null -w "%{http_code}"` 로 200 확인
319
+ - 결과물 `attribution.md` 에 사용된 URL + 라이선스 + fetch 일자 명시
320
+ - `<!-- asset-source: <CDN url>, license: <license>, status: fetched|fallback -->` 코멘트를 HTML에 박을 것
321
+
322
+ **CDN fetch 실패 fallback 정책 (위반 = regression)**:
323
+ - **금지**: 핸드드로잉 inline SVG 캐릭터 합성 (stick figure, 손으로 그린 face 등 — sub-agent가 "curl 못 한다" 핑계 도피 안티패턴). **v1.3.4의 Karrot에서 다시 발생함 — 이번에 gate를 강화.**
324
+ - **허용 fallback (우선순위 순)**:
325
+ 1. 동일 카테고리의 다른 검증 URL 시도 (DiceBear notionists 실패 → lorelei → personas)
326
+ 2. 모두 실패 시 brand-color 그라데이션 placeholder box:
327
+ ```html
328
+ <div class="hero-character-fallback"
329
+ style="aspect-ratio:1; background:linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%); border-radius:24px; display:flex; align-items:center; justify-content:center;">
330
+ <span style="color:#fff; font-size:14px; opacity:.7;">[CHARACTER IMAGE]</span>
331
+ </div>
332
+ ```
333
+ 3. 사용자에게 한 줄로 알림: "CDN 자산 fetch 실패, brand-color placeholder 사용."
334
+ - **사용자 product DOM에 amateur SVG는 절대 들어가지 않음.**
335
+
336
+ **Handcraft gate (v1.3.5 신설 — Karrot 사고 재발 방지)**:
337
+ - hero-character.svg 파일을 emit하기 직전 **상단에 의무 코멘트**:
338
+ ```xml
339
+ <!-- omd-asset: source=https://api.dicebear.com/9.x/<style>/svg?seed=<seed>, license=CC0, fetched=<ISO-date> -->
340
+ ```
341
+ - 이 코멘트가 없거나 source URL이 검증 catalog (omd:asset-fetch §2)에 없으면 — generator는 **그 파일 product DOM에 embed 금지**. fallback gradient placeholder 사용.
342
+ - product DOM 작성 후 self-audit grep:
343
+ - `grep -cE '<path d="M[0-9.,\sCQTLZ\- ]{200,}"' landing.html` (긴 path data = 손으로 그린 anatomy 흔적)
344
+ - 차트(.hero-chart) / Lucide 아이콘(viewBox 0 0 24 24) 외에 long path **0** 이어야 함. > 0 이면 sub-agent가 다시 fetch.
345
+ - product DOM 안 `<svg>` 인라인은 두 카테고리만 허용:
346
+ 1. 차트 / 데이터 시각화 (line/bar/area chart — 데이터 패턴, brand-specific X)
347
+ 2. Lucide / Heroicons / Tabler 아이콘 인라인 (작은 viewBox)
348
+
349
+ 기타 모든 시각 자산은 catalog `<img>` URL 통해 로드. **catalog 외 임의 SVG 그리기 금지.**
350
+
351
+ 자세한 catalog + 검증 gate는 `skills/omd-asset-fetch/SKILL.md` 참조.
352
+
353
+ 7. **CSS 컨테이너 일관성** (위반 = regression):
354
+ - 모든 top-level 섹션은 **단일 공유 클래스** `.container-inner` (또는 동등한 이름)을 사용.
355
+ - 그 클래스 한 곳에 `max-width: <N>px; margin: 0 auto; padding: 0 24px` 정의. **섹션별로 padding 값 다르게 정의 금지** — header / hero / features / footer 모두 같은 inner 컨테이너로 left/right edge가 정확히 일치해야 함.
356
+ - 결과 HTML 작성 후 self-audit: 모든 section의 inner element가 동일한 horizontal padding 적용됐는지 grep으로 확인. 어긋난 곳 있으면 통일.
357
+ - 직접 결함 예시 (v1.3.3에서 발생): `.header-inner { padding: 0 24px }` + `.hero-inner { padding: 0 }` → 뷰포트 wide에서 24px offset.
358
+
359
+ 8. **Hero composition decomposition** (위반 = regression):
360
+ - **먼저 rule 12 (Hero archetype)에서 archetype 1개 선택**. 모든 archetype은 분리된 elements 원칙 적용.
361
+ - 시각 요소는 분리된 element:
362
+ - `.hero-character` / `.hero-visual` — img / 또는 inline svg 단일 (DiceBear 등 검증 자산)
363
+ - `.hero-chart` — 별도 inline `<svg>` (차트는 brand-specific creative work 아닌 일반 데이터 시각화 패턴)
364
+ - `.hero-stat-card` — HTML `<div>` 카드 (숫자·라벨·trend chip)
365
+ - `.hero-ornament` — 배경 ornament (CSS radial-gradient + blur)
366
+ - `.hero-slide` (carousel archetype용) — 각 slide 분리된 element
367
+ - 각 element는 absolute positioning 또는 grid로 조합.
368
+ - **금지**: 캐릭터 + 차트 + 데이터 카드를 single SVG 파일 안에 통합 (v1.3.3 결함).
369
+
370
+ 9. **Hero archetype selection** (v1.3.8 신설 — monotone hero 회귀 방지):
371
+
372
+ v1.3.5~v1.3.7에서 brand가 다른데 hero 구도는 매번 "text-left + character-right + floating cards" 동일 → "와우" 약화. 이 rule부터 sub-agent는 brand vibe + surface signal 기반으로 **7가지 archetype 중 1개를 명시 선택**하고 그에 맞춰 구성.
373
+
374
+ ### archetype catalog
375
+
376
+ **A. `left-character`** (현행 default)
377
+ - 좌측: 카피 + CTA + carousel dot
378
+ - 우측: 캐릭터 일러스트 + floating data card 2-3개
379
+ - 적합: 핀테크 advisor / 데이터 어드바이저 / B2C 모바일 앱
380
+
381
+ **B. `center-text`**
382
+ - 중앙 정렬 헤드라인 + lead + 2 CTAs
383
+ - 아래: 큰 visual (screenshot / video / illustration) 단일
384
+ - 좌우 floating card 없음, 시선 단일 축
385
+ - 적합: AI / 개발자 도구 / Linear-style minimal SaaS / 검색 중심 portal
386
+
387
+ **C. `carousel`**
388
+ - 풀-블리드 또는 wide carousel — 3-5 slide auto-rotate
389
+ - 각 slide: 자체 카피 + visual + CTA (모두 다른 메시지)
390
+ - dot pagination + auto 5-7s
391
+ - 적합: 커뮤니티 마켓플레이스 / 이커머스 / 다양한 카테고리 promoting
392
+
393
+ **D. `split-screen`**
394
+ - 50/50 그리드: 좌측 = 카피 + CTA / 우측 = 큰 product 이미지 또는 booking widget
395
+ - floating card 없음, 큰 우측 단일 visual
396
+ - 적합: 모빌리티 (booking strip) / 여행 (검색 widget) / B2B (product 스크린샷)
397
+
398
+ **E. `editorial-magazine`**
399
+ - 풀-블리드 헤로 사진 + overlay 텍스트 (top-strip + cover caption + 큰 headline)
400
+ - 잡지 표지 메타포 (Vol.X · Year · Issue 같은 editorial 메타)
401
+ - 적합: 큐레이트 커머스 / 패션 / 라이프스타일 / 문화
402
+
403
+ **F. `dashboard-preview`**
404
+ - 좌측 또는 상단: 짧은 카피 + CTA
405
+ - 메인 visual: 큰 product UI 스크린샷 (가짜 dashboard mock)
406
+ - 시선 = "이게 우리 product 모습" 자체
407
+ - 적합: B2B SaaS / analytics / productivity 도구
408
+
409
+ **G. `quote-led`**
410
+ - 큰 인용구 (display serif, 80-120px) — 사용자 testimonial 또는 brand thesis
411
+ - 아래: 짧은 보강 카피 + CTA
412
+ - 캐릭터 / 차트 / floating 없음 — 단 한 문장의 권위
413
+ - 적합: 컨설팅 / 에이전시 / 럭셔리 brand / 사명 강조 surface
414
+
415
+ ### archetype 선택 룰 (brand vibe ↔ archetype)
416
+
417
+ | brand category / vibe | 1순위 | 2순위 (variation) |
418
+ |---|---|---|
419
+ | Fintech (advisor) — banksalad, kakaopay, toss | A `left-character` 또는 F `dashboard-preview` | B `center-text` |
420
+ | AI / Developer tools — anthropic, claude, vercel | B `center-text` | F `dashboard-preview` |
421
+ | Productivity / SaaS — linear, notion, asana | F `dashboard-preview` | B `center-text` |
422
+ | E-commerce (curated/editorial) — 29cm, musinsa, ssense | E `editorial-magazine` | C `carousel` |
423
+ | Community marketplace — karrot, dcard, mercari | C `carousel` | A `left-character` |
424
+ | Mobility / service — socar, uber, lime | D `split-screen` (booking widget) | C `carousel` |
425
+ | Travel — airbnb, yanolja, kayak | D `split-screen` (검색 widget) | E `editorial-magazine` |
426
+ | Luxury / heritage — ferrari, hermes | G `quote-led` 또는 E `editorial-magazine` | D `split-screen` |
427
+ | Consultancy / B2B services | G `quote-led` | B `center-text` |
428
+ | Health / wellness — gangnamunni, headspace | B `center-text` 또는 D `split-screen` | A `left-character` |
429
+
430
+ ### 선택 절차
431
+
432
+ 1. brand의 `web/references/<id>/DESIGN.md` §1 (Visual Theme) + fingerprint `category`를 읽고 위 표에서 1순위 archetype 선택
433
+ 2. 사용자가 같은 brand로 **이미 한 번 실험**했으면 (`.omd/runs/INDEX.md`에 기록) → 2순위 사용해서 variation 제공
434
+ 3. 사용자가 명시 ("center 정렬로", "carousel로") → 그대로 따름
435
+ 4. 선택된 archetype을 `experiment-meta.json`의 `hero_archetype` 필드에 명시 (gallery 표시용)
436
+ 5. 사용자에게 한 줄 알림: "Hero archetype: `center-text` 사용 — Linear-style minimal SaaS 매칭"
437
+
438
+ ### archetype 구현 시 공통 제약
439
+
440
+ - 모든 archetype은 rule 8 decomposition 따름 (element 분리)
441
+ - 모든 archetype은 rule 10 reveal safety net 적용
442
+ - 모든 archetype은 rule 5 wordmark-only logo 사용
443
+ - 모든 archetype은 rule 6 verified asset catalog만 사용
444
+ - archetype별 specific 자산 (예: editorial-magazine의 헤로 photo) — Picsum/Loremflickr 결정적 URL 필수
445
+
446
+ 10. **IntersectionObserver reveal safety net** (v1.3.6 신설, 위반 = regression):
447
+
448
+ 10. **IntersectionObserver reveal safety net** (v1.3.6 신설, 위반 = regression):
449
+
450
+ 이전 batch에서 toss + socar 둘 다 round-2 refinement에서 **동일한 결함을 fix**함 — IntersectionObserver-기반 scroll reveal이 fullpage screenshot 또는 인쇄 모드에서 viewport 밖 element를 `opacity: 0`으로 영구히 hidden시킴. 시스템 결함이라서 skill에 박음:
451
+
452
+ - reveal 패턴 사용 시 **safety net 의무**:
453
+ 1. CSS `@keyframes failsafeReveal` 정의:
454
+ ```css
455
+ @keyframes failsafeReveal { to { opacity: 1; transform: none; } }
456
+ [data-reveal] { animation: failsafeReveal 0.01s ease 2s forwards; }
457
+ html.js-ready [data-reveal] { animation: none; } /* JS ready 시 IO가 주도 */
458
+ .is-revealed { opacity: 1 !important; transform: none !important; }
459
+ ```
460
+ 2. JS 초기화 시점에 `document.documentElement.classList.add('js-ready')` 추가 → IO 정상 동작 path 진입
461
+ 3. JS 미로드 / IO 미지원 / fullpage screenshot 시 → 2s 후 자동 reveal (CSS animation forwards)
462
+ 4. `@media (prefers-reduced-motion: reduce)` 에서는 모든 reveal element를 `opacity: 1` 즉시
463
+ - **금지**: `opacity: 0` initial state + IO trigger만 + safety net 없음 — fullpage screenshot 시 빈 섹션 발생
464
+
465
+ 11. **"와우" 수준 polish 체크리스트** (master는 다음 중 최소 5개 구현):
466
+ - [ ] 헤더 sticky + scroll 시 subtle backdrop blur or shadow
467
+ - [ ] Hero 카르셀 (auto-rotate 5-7s, dot indicator, smooth fade)
468
+ - [ ] CTA hover micro-interaction (brand의 motion philosophy 따라 — banksalad면 "lighten on hover")
469
+ - [ ] 숫자 count-up 애니메이션 (자산·사용자 수 등 stat에)
470
+ - [ ] Scroll reveal stagger (IntersectionObserver, 60ms 간격)
471
+ - [ ] 차트 stroke-dashoffset draw-in (financial 도메인이면 mandatory)
472
+ - [ ] Feature 카드 hover에 subtle elevation + icon rotate/scale
473
+ - [ ] Background ornament — 원형 그라데이션 blur layer (CSS `filter: blur(120px)`로 ambient color wash)
474
+ - [ ] `prefers-reduced-motion` 가드 (모든 모션 끔)
475
+ - [ ] Footer가 정보 풍부 (4 column, legal·company·resources·contact)
476
+ - [ ] 모바일 viewport에서도 깔끔 (768px / 375px 둘 다)
477
+
478
+ 모든 모션은 brand DESIGN.md §15 (Motion & Easing)의 duration scale + easing 토큰 따름.
479
+ ```
480
+
481
+ 이 규칙은 Step 4의 master spawn prompt에 매번 prefix로 들어간다 (사용자에게 노출 X — 내부 contract).
482
+
177
483
  ### 루프 의사코드
178
484
 
179
485
  ```
180
486
  spawn_count = 0
181
- prompt = "<RUN_DIR + task + chosen_ref_id>. Phase 1부터 시작."
487
+ prompt = "<위 grounding 규칙 prefix> + <RUN_DIR + task + chosen_ref_id + surface_signal + reference_capture_dir>. Phase 1부터 시작."
182
488
 
183
489
  while spawn_count < 12 (safety cap):
184
490
  result = Agent({