Commit graph

63 commits

Author SHA1 Message Date
Yun Chan
6805fb2be7 feat(skill): absorb external design skills, restore interview gate, add review route
- Restore the step-0 interview as a mechanical gate the skill explicitly
  depends on; add harness.md (per-harness question tools, limits,
  fallbacks) and brief-interview.md (slots, question cards, rounds).
- Add 10 reference docs absorbed from external design skills
  (accessibility, interaction-feel, elevation, color, icons, product-copy,
  component-systems, critique, change-review, print-email) and extend
  existing references.
- Add a review-only route and two hard-gate clauses (truncated content
  reachability, three-flashes limit).
- design-gate: split tap targets into WCAG 2.5.8 and 44px contract layers,
  run axe-core when available, and fix false positives found on a real
  site (decorative alt="", stacked wordmark line count, url-only pages).
- lint-skill: fail if the interview gate section or its links disappear.
- Ship agents/openai.yaml and THIRD_PARTY_NOTICES.md.
2026-09-24 13:26:03 +09:00
Yun Chan
fb39e4aeb8 chore: refresh final dashboard previews
All checks were successful
ci / build (push) Successful in 3m14s
release / release (push) Successful in 55s
2026-09-12 18:17:14 +09:00
Yun Chan
e09d4efb1c feat: strengthen design skill and refresh all showcases
Some checks failed
ci / build (push) Has been cancelled
2026-09-12 18:16:33 +09:00
Yun Chan
69e5cc1efd Keep gate copies aligned for release
All checks were successful
ci / build (push) Successful in 36s
release / release (push) Successful in 54s
2026-09-12 15:31:12 +09:00
Yun Chan
45ba92ea82 release: v0.11.0 2026-09-12 15:28:46 +09:00
Yun Chan
3c39a1731d Fix scroll-driven proof graphics 2026-08-30 21:49:10 +09:00
Yun Chan
dcb1415aaa Tune specimen completion hold
Some checks failed
ci / build (push) Failing after 12m53s
2026-08-30 19:34:50 +09:00
Yun Chan
5953c97cf6 Fix specimen scroll motion
Some checks failed
ci / build (push) Failing after 11m40s
2026-08-30 19:20:43 +09:00
Yun Chan
7ef9106723 fix(site): add explicit not-found page
All checks were successful
ci / build (push) Successful in 41s
2026-08-30 18:24:48 +09:00
Yun Chan
82e8c8f8ae fix(site): refresh archive proof images
All checks were successful
ci / build (push) Successful in 39s
2026-08-30 17:58:54 +09:00
Yun Chan
13dd160c16 feat(site): redesign promotion experience 2026-08-30 17:52:54 +09:00
Yun Chan
6930906583 release: v0.10.0
All checks were successful
ci / build (push) Successful in 42s
release / release (push) Successful in 1m5s
2026-08-29 21:19:26 +09:00
Yun Chan
2b7fa32b5b feat: 한국어 우선 쇼케이스 정리
All checks were successful
ci / build (push) Successful in 51s
release / release (push) Successful in 2m34s
2026-08-26 18:14:41 +09:00
Yun Chan
e4087c29f5 Bust RFS interaction cache
All checks were successful
ci / build (push) Successful in 33s
2026-08-25 04:48:54 +09:00
Yun Chan
413486958c Upgrade RFS and Saebyeok showcases
Some checks failed
ci / build (push) Has been cancelled
2026-08-25 04:41:51 +09:00
Yun Chan
9529e3747c feat(showcase): 트웬티온스 사이트를 전면 개편한다
All checks were successful
ci / build (push) Successful in 33s
2026-08-25 03:50:28 +09:00
Yun Chan
cb3c323c9f fix(ci): 배포 검증 상태를 격리한다
All checks were successful
ci / build (push) Successful in 33s
2026-08-24 14:14:29 +09:00
Yun Chan
4d813420dc feat(showcase): 신규 쇼케이스 3종 — RFS · 트웬티온스 · 새벽청과
Some checks failed
ci / build (push) Failing after 1m52s
각 워크를 서로 다른 클라이언트로 보고 0~6단계를 따로 돌았다(토큰 체계 신규 정의 =
전체 경로). 셋 모두 라이트/다크 반전을 .band-light / .band-dark 가 --band-* 를
재정의하는 규칙으로 정의해 하드 게이트 4를 통과시켰다 — 반전이 섹션마다의 기분이
아니라 두 클래스가 만드는 규칙이다.

RFS — 실시간 유체 시뮬레이터 (rfs-cfd)
  프리셋 darkroom-spec · 컨셉 "암실과 사양서"
  R1 luminary.ai · R2 teenage.engineering EP-133 · 보조 simscale.com (전부 실측)
  히어로에서 브라우저가 실제로 Navier–Stokes 를 푼다. 순수 WebGL2, three.js 미사용.
  이류 → 온도 소스 → 부력·설비 힘 → 발산 → 압력 Jacobi → 투영 → 파티클 전진.
  격자 224×126 / 파티클 26,000 / Jacobi 22회, 좁은 화면은 절반으로 줄인다.
  prefers-reduced-motion 이면 시작하지 않고 그 자리에 RFS 실물 파티클 렌더가 남는다.
  단위계로 세 번 실패했다: dt 를 프레임 단위로 넘겨 파티클이 0.2초 만에 소멸했고,
  전부 급기구에서만 재생성해 실 한 가닥이 됐으며, 랙이 위로 뿜어 핫/콜드 아일이
  갈라지지 않았다. dt 를 초로 못박고 속도를 초당 격자 셀로 통일해 해결했다.
  수치는 전부 RFS 저장소 README 출처. 정확도 지표는 싣지 않았다 — 사내 검증 자료이고
  현재 통과 상태가 아니다.

트웬티온스 (twentyoz-corp)
  프리셋 field-record · 컨셉 "네 개의 현장"
  R1 samsara.com · R2 lars-mueller-publishers.com · R3 = 현행 twentyoz.kr 감사
  현행 사이트 실측: 대비 실패 172개 요소(9~11px 가 3.61:1), 한글 폰트 미지정으로
  한글이 시스템 기본으로 떨어짐, h1 자간 −0.06em(한글 상한의 세 배), 배경 #020202.
  'Digital Genetics' 은유가 산업용 IoT 사업을 가리고 있었다.
  유지한 자산은 radius 0 과 무채색·모노 기조.
  첫 화면에 회사 이름 대신 현장 선택기를 뒀다 — 라디오 그룹이라 JS 0바이트이고
  좌우 화살표 키가 그냥 먹는다. 파트너 19곳은 원본에 도메인 표기가 없어 나누지 않았다.

새벽청과 (saebyeok-cheonggwa)
  프리셋 market-board · 컨셉 "오늘 새벽 두 시의 값"
  R1 natoora.com · R2 phillips.com · R3 제철 12개월 띠
  커머스가 감추는 시세 변동을 히어로의 주인공으로 삼았다. 상품 사진은 아래로 내렸다.
  경매 시각·경락가격·등급·당도 표시 규정을 조사해 카피의 근거로 썼다.
  무화과 당도를 "—" 로 두고 값이 없는 게 아니라 표시가 허용되지 않는 품목이라고 적었다.
  등락은 색이 아니라 ▲▼보합 기호가 먼저다.
  표의 숫자가 예시임을 판 아래와 푸터 두 곳에 명시하고, 주소·전화는 비웠다(게이트 11).
  사진 4장은 codex 내장 image_gen 생성물이며 합계 341KB.

검증: Astro 빌드 통과, verify 체인 L0~L6 10계층 전부 통과,
showcase-audit 신규 3종 12/12 통과(320/390/768/1440).
성능 실측(RFS): JS 24KB · 총 704KB. 폰트만 530KB 로 예산(200KB) 초과 —
한글 웹폰트 자체가 원인이고 기존 cheongon-care 는 650KB 로 더 크다.
저장소 공통 baseline 이라 이번 판은 기록만 남기고 넘어간다.

기존 16종에서 발견된 70건(대비 미달·히어로 줄 수 초과)은 이번 범위를 넘어
design.md 에 목록만 남겼다.
2026-08-24 12:51:48 +09:00
Yun Chan
123d9fc5a8 fix(showcase): 미정의 CSS 변수·한글 조판·접근성 결함 수리 및 무결성 게이트 두 겹 신설
stylelint 도 html-validate 도 "var(--x) 를 썼는데 --x 가 어디에도 없다"를 잡지
못한다. 미정의 변수는 선언 전체를 무효로 만드는데 콘솔 에러가 없어 조용히 남는다.
검사기를 심자마자 v0.9.1 의 실제 결함이 나왔다.

게이트 신설
- tools/integrity.mjs — L0 무결성. CSS 커스텀 프로퍼티 참조, 상대경로,
  한글 조판 필수 규칙(keep-all·한글 폰트 지정), 슬롭 지문, 스크롤 리스너를 검사.
  verify.mjs 체인에 편입해 실패 시 배포를 막는다.
  폴백이 있는 var(--x, 기본값) 은 선택적 주입이므로 미정의로 세지 않는다.
- tools/showcase-audit.mjs — 320/390/768/1440 실렌더 감사. 가로 스크롤·오버플로·
  컨트롤 줄바꿈·대비 AA·왼쪽 인셋·h1 개수·히어로 줄 수·깨진 이미지·콘솔 에러.
  반투명 배경을 합성해 대비를 재고, WebGL 페이지를 위해 swiftshader 로 렌더한다.
- html-validate 대상을 두 페이지에서 public/work 전체로 넓혔다.

수리한 결함
- 미정의 변수 64건: --dur-fast·--radius-xs 가 정의된 적이 없어 cask-archive,
  cheongon-care, materia-interior 의 transition 선언이 통째로 무효였다.
  새 토큰을 만들지 않고 기존 --dur-quick·--radius 로 매핑했다 — 토큰을 늘리면
  radius 어휘가 4종이 되어 하드 게이트 3에 걸린다.
- 한글 조판: lumina-optics·pulsegate·synapse-bci 가 word-break: keep-all 없이
  IBM Plex Sans 만 지정해 한글이 시스템 기본으로 떨어지고 있었다.
  같은 계열의 IBM Plex Sans KR 을 스택에 넣고 keep-all 을 추가했다.
- 슬롭 지문 19건: text-transform: uppercase 로 만든 영문 대문자 eyebrow 를
  한글 문장으로 바꾸고, 대문자용으로 잡아둔 넓은 자간과 모노 서체도 되돌렸다.
- 하드 게이트 9: materia-interior/hannam-duplex.html 의 100vh → 100dvh.
  이 파일은 어디서도 참조되지 않으면서 인라인 hex 까지 갖고 있어 토큰 기반으로 다시 썼다.
- 접근성 11건: label 하나가 output 과 input 둘에 걸리던 것 5건(font-foundry,
  lumina-optics), dl > div > div > dt 4건(muwol-coffee), div 에 붙은 aria-label
  2건(overtone-sound, pulsegate).

측정기 자체의 오류 둘도 고쳤다 — 눈에 보이지 않는 .sr-only 를 대비 실패로 잡던 것과,
overflow-x: auto 상자 안의 넓은 표를 오버플로로 잡던 것. 재는 도구가 틀리면
통과도 실패도 의미가 없다.
2026-08-24 12:50:57 +09:00
Yun Chan
225c1d9f42 release: v0.9.1 (신규 쇼케이스 3종 정식 출시, CSS 변수 및 경로 무결성 게이트 강화)
Some checks failed
ci / build (push) Failing after 37s
release / release (push) Successful in 1m30s
2026-08-23 23:17:55 +09:00
Yun Chan
e26b00a825 release: v0.9.0 - fix Antigravity skill path and bump version to 0.9.0
Some checks failed
ci / build (push) Failing after 18s
release / release (push) Successful in 1m20s
2026-08-23 21:18:52 +09:00
Yun Chan
46b47884c9 feat(skill): 왼쪽 인셋 리듬·픽셀 판정을 스킬에 내재화
이번 라운드(스크롤 잔류·빈약한 헤더·인셋 불일치 — 사용자 보고 3건)의 교훈을 스킬로 승격:
- design-gate leftInset 검사 내장(텍스트 노드 Range 기준 ≥12px, 모바일 390px 전 뷰) — 오버플로 검사는 오른쪽만 보는 계측 공백을 메움. 도그푸드: 양앱 통과(가온 16·두레 24 각자 일관 축)
- 브랜드 검사 오탐 수정(동일 셀렉터 다중 요소 시 보이는 것 기준 — 도그푸드가 잡음)
- preflight §0-E 왼쪽 인셋 리듬, 하니스 규칙 15~17(기준 갱신 의도 리뷰·스크린샷 픽셀 판정·RED 재현), 사고표 4행
- SKILL.md 규칙 6 보강: 계측의 공백 — 보고가 오면 픽셀 판정으로 수치화하고 게이트에 추가
2026-08-23 15:40:58 +09:00
Yun Chan
2678fd2c9c fix(site,skill): 두레 왼쪽 인셋 리듬 수리 — 제목이 가장자리에 붙어 보이던 사고(v23)
사용자 스크린샷을 픽셀 판정으로 해부: 앱바·검색·칩·카드는 24px에서 시작하는데 h2만 16px — 8px 어긋난 큰 명조 제목이 '가장자리에 붙었다'로 읽혔다. .layout 모바일 인셋을 24px로 통일.
- 게이트 공백: 기존 매트릭스는 오른쪽 오버플로만 검사 — e2e 에 M6 왼쪽 인셋 스위트 신설(60단언, 텍스트 시작점 ≥12px + 헤더-제목 같은 축 ±10px), RED 38 실패로 결함 재현 후 GREEN
- 측정 하자 2건 교정: display:none rect=0 필터, 전폭 컨테이너 보더박스가 아닌 텍스트 시작점 기준
- 스킬 mobile-app-ux.md: '왼쪽 인셋은 하나의 축' 규칙 추가. 시각 기준 갱신, verify L0~L6 전 통과(E2E 1,805단언)
2026-08-23 15:31:32 +09:00
Yun Chan
8547bb11a6 fix(site): 모바일 헤더 재설계 — 크럼이 아니라 브랜드+액션 한 줄(v24)
v23 은 구조(단일 sticky 바)만 맞고 시각적 무게가 틀렸다 — 작은 회색 크럼 텍스트가 52px 바에 혼자 떠 있는 빈약한 헤더였다. 계측이 전부 통과해도 보기에 빈약하면 실패다.
- 탑바에 브랜드(가 모노그램 타일+이름)를 이어받게 하고 크럼은 모바일에서 숨김 — 두레 앱바와 같은 문법(브랜드+액션). 화면 이름은 각 뷰 헤드와 활성 탭이 말한다
- 검증: 360/390/412px 오버플로 0·브랜드 노출, 스크롤 800에서 top=0, 탭 전환 scrollY=0, 비전 검수 통과, 시각 기준 갱신 후 verify L0~L6 전 통과
- 스킬 mobile-app-ux.md 규칙 갱신: 크럼 내보내기 금지 + 계측 통과에도 눈으로 봐야 한다
2026-08-23 15:13:38 +09:00
Yun Chan
d4cd9ca49d fix(site,skill): 모바일 상단 바 수리 — 탭 전환 스크롤 리셋과 단일 sticky 헤더
사용자 보고 '상단 바가 페이지마다 왔다갔다하며 패딩이 없고 포지션도 이상하다'를 계측으로 원인 규명:
- 탭 전환 시 이전 뷰의 window.scrollY 잔류 — 짧은 뷰에선 클램프(600→49)되어 헤더가 반쯤 잘림. setCurrentView 에 scrollTo(0,0) 리셋(양앱)
- ≤900 헤더 이중 스택(브랜드 줄 43px+크럼 52px)·비sticky·safe-top 무 — .side 숨기고 .topbar 를 sticky+safe-top 단일 헤더로(두레 앱바 문법). 데스크톱 무변경
- 재발 방지: e2e M5 헤더·스크롤 스위트 10단언(스크롤 300px+에서 탭 전환 → scrollY=0·헤더 top=0), 시각 기준 갱신(390px 9화면 의도 변경), verify L0~L6 재통과
- 스킬 mobile-app-ux.md 에 '뷰 전환은 스크롤을 리셋·헤더는 하나' 규칙 반영. 가온 CSS v23
2026-08-23 14:50:06 +09:00
Yun Chan
14cb138e1a feat(site,skill): 모바일 앱 수준 UX — 바텀내비·백 키 히스토리·바텀 시트·safe area·PWA 와 L6 시나리오 E2E
- HIG(탭바 5~6·시트)·M3(바텀내비 3~5·48dp)·NN/g·CSS-Tricks 조사(research/mobile-app-ux-v1.md) 후 Nielsen 10 감사로 위반 도출
- 바텀 내비 5탭 + 더보기 바텀 시트(그룹 헤더), 뷰·다이얼로그 pushState/popstate — 안드로이드 백 키가 앱 내 뒤로 작동
- safe area(env+max)·입력 16px(iOS 확대 방지)·coarse 44px 타깃·tap-highlight·overscroll 위생, 인쇄에서 모바일 크롬 제거
- PWA: manifest + sharp 생성 아이콘(가온=장부 문항, 두레=시간표 블록)
- tools/e2e.mjs L6 시나리오: RED 1,742 단언 318 실패 → GREEN 1,762/0, verify 체인 7계층 통과
- 실기기(안드로이드 에뮬레이터·Chrome) 텍스트 근거 검증 — 백 키·시트·탭 전환, iOS 는 WebKit(L4) 대체
- 스킬 내재화: mobile-app-ux.md, audit-gate 7계층·하니스 12~14, design-gate 터치타깃 검사, SKILL.md 규칙 7
- 도그푸드: 게이트가 죽은 선택자 8종·thresholds 병합 버그 적발 — CSS v22(양앱) 범프 후 전체 재검증
2026-08-23 14:33:15 +09:00
Yun Chan
72337b7ee0 feat(skill): 자율 검증 폐쇄 루프 내재화 — 0.6.0
Some checks failed
ci / build (push) Failing after 5s
- 핵심 규칙 6: 완료의 정의는 게이트 통과. 사용자를 QA로 쓰지 않는다
- 5단계 기계 검사 4번째: design-gate 실행 의무화
- references/audit-gate.md: 사고-검사 매핑, SEO/meta 체크리스트, OS/브라우저 특성, 하니스 규칙
- tools/design-gate.mjs: 범용 게이트(메타/SEO·대비·수축·리듬·트랙·스케일×폭 + 옵션 L0/L3/L4, checks 깊은 병합)
- 리듬·트랙 불변식: 숫자 라벨 등폭·빈 셀 트랙 균일·등간격 — 시간표 자동배치 결함 재현 픽스처 검출, 앱 15뷰 통과(오탐 0)

feat(site): 관리 앱 2종 프로덕션 콘솔(v6→v18)

- 가온 학적부 9뷰·두레 수강신청 6뷰: shadcn 문법, Pretendard/Noto Serif/IBM Plex 폰트 전략, 볼드 금지(400/500/600), WCAG AA 대비 전면 교정, 한글 keep-all 조판
- LMS 필수 요소(알림 센터·공지·진도·평가 유형·출결 사유·학점 경고), 12명 기준 데이터 정합, SEO 구조(h1 유일·OG/twitter·og.png)
- 시간표 자동배치 결함 수리(명시적 격자 좌표)
- QA 게이트: L0 stylelint/html-validate · L1 단위 30 · L2 감사 61+불변식 · L3 시각회귀 30화면 · L4 WebKit · L5 키보드 탐색 — npm run verify 실패 시 배포 금지 체인
2026-08-22 21:22:05 +09:00
Yun Chan
30dc6687dd feat(site): 쇼케이스를 별도 페이지로 분리하고 본문을 존댓말로 바꾼다
All checks were successful
ci / build (push) Successful in 18s
홈에 있던 쇼케이스 섹션은 스크린샷 두 장을 카드에 넣고 각도를 준 것이었고,
눌러도 갈 곳이 없었다. 그림만 보여주고 끝나는 자리는 증거가 아니라 장식이다.

- /showcase 를 별도 페이지로 만들었다. 모자이크 그리드에 썸네일을 놓고
  누르면 실제로 도는 사이트(/work/<slug>/)가 열린다.
- 사례 데이터를 src/data/work.ts 한 곳에 모았다. 항목 하나만 추가하면
  그리드에 들어간다 — 페이지 코드는 건드리지 않는다.
- 첫 사례로 목요일의 화원(플로럴 스튜디오)을 넣고 실제 파일을
  public/work/thursday-flowers/ 에 함께 싣는다.
- 홈에서 예전 섹션을 제거하고 내비를 /showcase 로 돌렸다.

본문 말투:

  사이트 문장이 전부 반말이었고 일부는 내부 감사 로그처럼 읽혔다.
  스킬 문서의 단정한 반말은 의도지만, 방문자를 향한 화면은 다르다.
  제목은 단정문이 카피로서 힘을 가지므로 그대로 두고, 설명하는 문장과
  명령형("~해라")을 전부 존댓말로 바꿨다. 실측 결과 홈·쇼케이스 모두 0건.

구현에서 걸린 것:

  Base 레이아웃이 이미 <main class="page"> 를 제공하는데 새 페이지에서
  또 감쌌다. 바깥 page 의 본문 칼럼(561px) 안에 안쪽 page 가 들어가
  전 페이지가 좁아졌다 — 모자이크가 561px, 썸네일이 240px 이었다.
  중첩을 걷어내자 1180px / 596px 로 돌아왔다.
2026-08-21 08:36:35 +09:00
Yun Chan
e4ac43f0b3 feat(core): Antigravity 등 .agents/skills 규약을 읽는 하네스를 지원한다
All checks were successful
ci / build (push) Successful in 25s
release / release (push) Successful in 40s
Antigravity 에서 스킬로 인식되지 않는다는 제보를 받고 확인했다.
설치 경로가 없었던 것이 원인이다.

공식 문서 기준 경로:
  워크스페이스  <root>/.agents/skills/<이름>/
  전역         ~/.gemini/config/skills/<이름>/

.agents/skills 는 특정 제품 경로가 아니라 중립 경로라, 같은 규약을 쓰는
다른 하네스도 함께 읽는다. SKILL.md 구조(프론트매터 + references/)가
Claude Code 와 같아서 변환 없이 그대로 복사하면 된다.

프론트매터 요구사항도 맞는 것을 확인했다 — description 필수, name 선택.
우리 SKILL.md 는 둘 다 갖고 있다.

- targets/antigravity.ts 추가, 설치 대상 5 → 6종
- 하위호환 경로(.agent/skills, 단수)는 감지에만 쓰고 설치는 복수형에만 한다.
  두 곳에 깔면 스킬이 두 번 잡힌다
- 테스트 2개 추가: 범위별 경로, 변환 없이 싣는지 (테스트 25 → 27개)
- 소개 페이지 설치 대상 목록에 반영
2026-08-21 03:16:53 +09:00
Yun Chan
eb9f61e907 fix(site): 스파인 뒤 62dvh 여백을 없애고 게이트 진행도를 목록 기준으로 잰다
All checks were successful
ci / build (push) Successful in 26s
release / release (push) Successful in 50s
진행도를 섹션 높이로 재고 있어서, 여섯 게이트를 다 돌리려면 정거장 목록 뒤에
화면 절반이 넘는 빈 공간(558px)이 필요했다. 콘텐츠가 아니라 스크롤 연료였고
화면에서는 그냥 큰 빈칸으로 보였다.

진행도를 목록이 화면을 지나는 구간으로 바꿨다.
  start = 뷰포트 70% 지점에 목록 위쪽이 닿으면 0
  end   = 뷰포트 30% 지점을 목록 아래쪽이 빠져나가면 1
  travel = 목록 높이 + (start - end)

뷰포트의 40% 가 구간으로 더해지므로, 목록 963px 이 1323px 구간이 되고
게이트당 220px 이다. 여백을 다 지우고도 과민 반응 기준(200px)을 넘긴다.

motion.md 에 "스크롤 구간이 모자라면 여백이 아니라 진행도 공식을 고쳐라" 를
기록했다.
2026-08-21 01:20:35 +09:00
Yun Chan
bd1daa7ec3 fix(site): 히어로 h1 의 clamp 하한을 h2 기준으로 잡는다
All checks were successful
ci / build (push) Successful in 20s
데스크톱만 보고 고쳐서 좁은 화면의 위계가 사라져 있었다.
h1 만 vw 에 비례해 줄고 h2 는 고정 스케일이라 둘이 만난다.

  1440px  65.4 / 33.2 = 1.97 배
  1024px  52.5 / 33.2 = 1.58 배
   390px  35.2 / 33.2 = 1.06 배   ← 사실상 같은 크기

하한을 3rem(48px, h2 의 1.45 배)으로 올려 전 구간을 확보했다.
320 / 390 / 768 / 1024 / 1440 에서 1.45 · 1.45 · 1.63 · 1.87 · 2.12 배.

tokens.md 에 "clamp 의 하한은 같이 변하지 않는 값을 기준으로 정한다" 와
최소·중간·최대 폭 세 곳에서 계산해 확인하라는 절차를 기록했다.
2026-08-21 01:07:47 +09:00
Yun Chan
6b2c9ad9d2 feat(site): 히어로를 첫인상 크기로 되돌린다
All checks were successful
ci / build (push) Successful in 24s
페이지에서 가장 중요한 섹션이 가장 짧았다(0.7 화면). 배분이 중요도와 반대다.

- min-height 72dvh → 88dvh. 100dvh 로 채우지는 않는다. 다음 섹션의 머리가
  조금 보여야 스크롤할 것이 있다는 단서가 된다.
- h1 41.5px → 65.4px. 섹션 h2 가 33.2px 이라 8px 차이였고, 스케일상 한 단계
  위일 뿐 화면에서는 같은 크기로 읽혔다. 처음에 --display(112px)를 썼다가
  배경과 싸워 줄였는데 반대쪽 극단까지 갔다. 지금은 h2 의 1.97 배다.
- 저장소 링크를 헤더로 옮겼으므로 히어로에서는 뺐다. 같은 링크가 두 번 나갔다.

tokens.md 에 히어로 h1 은 섹션 h2 의 1.8~2.2 배라는 기준을 기록했다.
2026-08-21 01:05:32 +09:00
Yun Chan
3271a329af refactor(site): 섹션 순서를 성격별로 묶고 분량을 우선순위에 맞춘다
Some checks are pending
ci / build (push) Waiting to run
지적마다 그 섹션만 키운 결과 15.5 화면이 됐고, 기능 설명과 증거가 번갈아 나와
지금 무슨 이야기를 듣고 있는지 알 수 없었다.

순서를 네 묶음으로 정리했다.
  ① 무엇인가      히어로 · 파이프라인
  ② 무엇을 아는가  SVG 필터 · 한글 조판
  ③ 정말 그런가    사례 · 수치 · 자기 감사 · 규칙
  ④ 행동          설치

분량:
- SVG 필터 3.1 → 2.1 화면. 스킬 문서에 있을 구현 디테일을 페이지에 옮겨 적고
  있었다. 데모는 넷 다 두고 설명만 줄였다.
- .stage 의 padding 에 --wide-pad 를 쓰고 있었다. 이 토큰은 페이지 그리드의
  바깥 칼럼용(최대 10rem)이라 1440px 에서 폭 320px 이 사라졌고, 그만큼 설명
  칸이 170px 로 눌려 여덟 자마다 줄이 바뀌었다.
- 자기 감사 항목 13개 → 6개. 나열이 밀도를 대신하고 있었다.

파이프라인 끝의 마무리 문장을 삭제했다. sticky 게이트가 끝까지 돌 스크롤
구간이 필요해서 만든 자리에 문장을 채운 것이라, 읽는 사람에게는 앞뒤 없이
튀어나온 문장이었다. 여백이 필요하면 여백을 만든다.

전체 15.5 → 13.5 화면.
2026-08-21 00:34:40 +09:00
Yun Chan
9a09678dee feat(site): 리퀴드 글래스 굴절을 올바른 변위 지도로 다시 붙인다
All checks were successful
ci / build (push) Successful in 29s
앞선 커밋에서 "backdrop-filter 체인에서 displacement 가 안 먹는다" 고 적고
데모를 뺐다. 틀렸다. svg-filters.md 에 이미 적혀 있던 두 규칙을 안 지킨 것이다.

- 변위 지도는 노이즈가 아니다. feTurbulence 로 만들면 값이 중립(128)으로
  수렴해 scale 을 아무리 키워도 곱해지는 값이 0 이다.
- feImage 의 크기는 요소 크기와 같아야 한다. 자동으로 안 맞춰준다.

가장자리에서 안쪽으로 미는 그라디언트를 직접 그렸다. linearGradient 한 장으로는
R 과 G 를 따로 못 만들므로 지도를 두 장 놓고 displacement 를 두 번 건다.
쓰지 않는 축에는 128 고정인 B 채널을 물려 이동량을 0 으로 만든다.

실측: 평균 채널차 1.66(난류 지도) → 9.29(그린 지도), 최대 126.
배경에 격자를 깔아 확인했다 — 패널 가장자리에서 격자 셀이 눌린다.

svg-filters.md 에 지도 두 장 기법과 확인 방법을 기록했다.
2026-08-20 23:30:15 +09:00
Yun Chan
ae2980872b feat(site): 재료 섹션을 실무 필터 3종으로 다시 만들고 푸터를 제거
Some checks are pending
ci / build (push) Waiting to run
푸터를 없애라는 지시가 반영돼 있지 않았다. .install-colophon 이 사실상
푸터였고, .install-grid 밖에 있어 padding-inline 을 못 받아 화면 왼쪽 끝에
붙어 있기까지 했다. 제거하고 저장소 링크는 헤더 nav 로 옮겼다.

재료 섹션은 "필터로 질감 만들기"(그레인/굴절/듀오톤)였고 화면에서 아무 말도
하지 않았다. 실무에서 손이 줄어드는 자리로 바꾼다.

- 글자 모양대로 생기는 스크림 — 흰 글자 대비 1.70:1 → 4.79:1 (AA 통과)
- 색각 이상 시뮬레이션 — 상태 배지 셋의 최소 색거리 82 → 2
- 구이(gooey) — 가로로 세어 덩어리 3개 → 1개

세 값 모두 화면을 캡처해 잰 것이다. 처음에는 데모보다 카피를 먼저 썼고,
그 결과 "2.1:1 → 12.4:1" 같은 재지 않은 숫자가 페이지에 실렸다. 전부 교체했다.

backdrop-filter 굴절 데모는 뺐다. feTurbulence 를 변위 지도로 써서 지도가
중립값(128)으로 수렴했고, scale 을 120 까지 올려도 채널차 2.96 이었다.
svg-filters.md 에 "변위 지도는 노이즈가 아니다" 라고 이미 적혀 있었는데
쓰기 전에 읽지 않았다.

섹션 끝의 note 도 삭제했다. color-interpolation-filters 를 명시했다는
문장은 방문자가 그것으로 할 수 있는 일이 없는 내부 자랑이었다.

스킬 내재화:
- svg-filters.md  실무 레시피 3종(스크림/색각/구이) + 구이가 붙는 문턱 계산
                  + feTurbulence 변위 지도 실패 기록
- antipatterns.md 데모를 만들기 전에 카피부터 쓰지 마라
                  + Playwright animations:'disabled' 가 끝 프레임을 찍는 함정
2026-08-20 23:23:24 +09:00
Yun Chan
f7f811ab42 fix(site): 제목-본문 간격을 전역 하나로 통일하고 스파인 리듬을 토큰화
All checks were successful
ci / build (push) Successful in 26s
제목과 본문이 붙어 있던 문제를 고친다.

- h2{margin:0} + p{margin-top:0} 이라 제목 다음 간격이 0px 이었다.
  섹션마다 각자 margin-top 을 붙여 어떤 곳은 0, 어떤 곳은 24px 로 갈려 있었다.
  base.css 에서 한 번만 정하고 각 섹션의 margin-top 오버라이드를 걷어냈다.
- :where() 로 감싼 첫 시도는 특이도 0 이라 p 규칙에 져서 무효였다.
  특이도를 갖추고 p 뒤로 옮겼다.
- 제목을 .head 로 감싼 수치 섹션은 인접 형제가 끊겨 혼자 0px 이었다.
  .head 도 규칙에 포함했다.

파이프라인 정거장 간격이 224px 로 벌어져 목록이 흩어져 보이던 문제도 고친다.

- 읽기 간격과 sticky 스크롤 구간을 같은 값으로 맞추려던 것이 원인이다.
  간격은 --stop-pad(space-5)로 되돌리고, 스크롤 구간은 .after 뒤 여백으로 옮겼다.
  게이트당 237px 확보 — 과민 반응 기준(200px) 위다.
- 마커 원의 top 이 space-4, 패딩이 space-6 로 따로 적혀 있어 원이 제목보다
  24px 위에 떠 있었다. 셋 다 --stop-pad 를 보게 했다.

3D 판 위 본문 대비는 실측 결과 최저 9.83:1 로 AAA 를 넘어 손대지 않았다.

스킬 내재화:
- tokens.md  제목 다음 간격 SSOT, :where() 특이도 함정, 리듬은 지역 토큰으로
- motion.md  읽기 간격과 스크롤 구간은 같은 손잡이가 아니다
- antipatterns.md  재는 도구가 거짓말하는 세 방식
  (reveal 중 측정 / sharp stats 가 extract 를 무시 / 눈보다 숫자)
2026-08-20 22:29:29 +09:00
Yun Chan
e1e6002bee backdrop-filter 오판을 정정하고 리퀴드 글래스 기법을 기록한다
All checks were successful
ci / build (push) Successful in 25s
'backdrop-filter 에 SVG 를 얹으면 Chrome 이 선언 전체를 버린다' 고
적어두고 페이지 프리플라이트 로그에까지 실었다. 사실이 아니었다.

Chrome 151 에서 재측정
  computed 값이 none 이 아니라 url(#id) 로 유지된다
  대비 높은 배경 위에서 실제로 굴절된다(줄무늬가 가장자리에서 압축됨)

진짜 원인은 feImage 의 width/height 를 요소 크기와 맞추지 않은 것이었다.
backdrop-filter 는 필터 이미지를 자동으로 조정하지 않는다.

검색도 다시 했다. feTurbulence grain 에 갇혀 2019년 글만 보고 있었다.
liquid glass 계열 최신 자료를 보니 변위 지도는 노이즈가 아니라
물리 기반 사전계산 이미지였다 — R=X변위, G=Y변위, 128 이 중립,
감쇠는 Squircle 곡선. 캔버스로 즉석 생성하면 240x160 이 2.7KB 다.

스킬 svg-filters.md 에 '리퀴드 글래스' 절을 넣었다.
구조 · 변위 지도 만드는 법 · 함정 넷 · @supports 폴백.
오판 정정도 문서 첫 줄에 남겼다.

한 번 실패한 것을 '안 되는 것' 으로 적으면 그 문장이 다음 사람의
상한선이 된다. 페이지 로그 항목도 그 내용으로 바꿨다.
2026-08-20 22:09:22 +09:00
Yun Chan
31c0690d4b 조판 섹션을 전면 개편한다 — 주장과 데모를 맞춘다
All checks were successful
ci / build (push) Successful in 27s
h2 '라틴 기준으로 만든 도구는 한글에서 멈춘다' 는 무엇이 어떻게 멈추는지
말하지 않았다. 그리고 데모가 그 주장을 증명하지 못했다.

데모가 실제로 실패하고 있었다
  word-break: break-all 과 keep-all 을 나란히 두고 '차이는 한 줄뿐'이라고
  적었는데, 재보니 줄 수 3 · 높이 87px 로 완전히 동일했다.
  어절이 짧아 두 쪽이 같은 자리에서 끊긴 것이다. 차이가 0 인 비교는
  증명이 아니라 주장의 반증이다.

개편
  h2  -> '라틴 기본값은 한글에서 세 군데가 깨진다'
        수치가 데모 개수와 정확히 대응한다
  데모 -> 셋으로. 각각 실제로 갈린다
        줄바꿈  break-all vs keep-all   (긴 어절 + 15rem 고정폭)
        행간    1.45 vs 1.75            (높이 74 vs 89)
        크기    11px vs 15px            (2줄 vs 3줄)

노트도 고쳤다. '로스팅프로파일이 갈린다'고 썼는데 실제로 갈린 건 다른
어절이었다 — 폭에 따라 바뀌므로 어절을 지목하지 않고 현상만 말한다.

스킬 antipatterns.md
  '데모가 주장을 증명하는지 재라' 추가.
  비교 데모의 높이·줄 수를 실제로 재는 코드와, 차이가 안 날 때의 대처.
2026-08-20 22:03:18 +09:00
Yun Chan
61fb0cdcfa 카피를 혜택 중심으로 고치고, 필터 데모를 실무 소재에 건다
All checks were successful
ci / build (push) Successful in 31s
지적 세 가지를 전부 확인했고 실제 결함도 하나 나왔다.

1. 재료 섹션 h2 가 아무것도 전달하지 않았다
   '표면은 CSS 가 정하고, 공간은 GPU 가 맡는다'
   금칙어도 없고 치환 테스트도 통과하는데 독자가 얻는 게 없다.
   구현 이야기를 대구로 포장한 것이었다.
   -> '텍스처 200KB 를 300바이트가 대신한다'

   카피라이팅 방법론(so you can 테스트 / 6~12단어 / 검증 가능한 수치)을
   찾아 적용했다. 우리 antipatterns 의 카피 절은 금지 목록뿐이라
   이런 문장을 못 잡는다 — 작법 절을 새로 넣었다.

2. 필터 데모가 줄무늬였다
   필터가 무엇을 하는지는 보이지만 왜 쓰는지는 안 보인다.
   각 필터를 실제로 걸리는 자리로 옮겼다.
     그레인  -> 매끈한 그라디언트 (없으면 밋밋하다는 대비)
     굴절    -> 글자 (유리 뒤가 실제로 휘는 것)
     듀오톤  -> 사진풍 면 (한 팔레트로 묶이는 것)
   타일마다 '언제 쓰는가' 한 줄을 추가했다.

3. 그레인 필터가 아무 효과도 못 내고 있었다
   feComposite in='SourceGraphic' in2='soft' operator='over' 는
   원본을 노이즈 위에 올린다. 노이즈가 통째로 가려진다.
   에러도 경고도 없이 조용히 죽는다.
   feBlend overlay 로 바꾸고 baseFrequency 0.9 -> 0.62.

섹션 간 검은 띠도 고쳤다. 파이프라인 캔버스가 섹션 패딩(112px) 아래에서
시작해 히어로 배경이 끝난 자리와 사이에 200px 이 비었다.

스킬
  antipatterns.md  카피 작법 — 금지 목록만으로는 못 잡는 실패
  svg-filters.md   데모는 실무 소재에 · feComposite 입력 순서 함정
2026-08-20 21:56:01 +09:00
Yun Chan
8ed45008a0 배포 환경을 실측해 .env.example 에 남긴다
Some checks failed
ci / build (push) Failing after 5s
나스를 찾았다 — Tailscale 의 yunchan-nas (100.116.83.60 / LAN 192.168.0.39),
Synology DS420+. SSH 키(id_ed25519)로 이미 붙어서 비밀번호가 필요 없다.

Forgejo 구성
  컨테이너 kd-forgejo (codeberg.org/forgejo/forgejo:11) + postgres:16-alpine
  웹 3000 -> Cloudflare 프록시로 https://git.chanpaca.net
  git SSH 2222 -> 컨테이너 내부 22

  중요: git.chanpaca.net 은 Cloudflare 뒤라 22/2222 가 둘 다 막혀 있다.
  오리진 IP 를 감추는 정상 구성이고, 그래서 git push 는 도메인이 아니라
  Tailscale 주소로 해야 한다. remote 를 그렇게 걸었다.

  SSH 키가 이미 Forgejo 에 등록돼 있다(encep-win, yunchan 계정).
  push 에 토큰도 비밀번호도 필요 없다.

배포 전 검증
  전체 빌드 통과 · 테스트 22개 통과
  tarball 114.5KB / 22개 파일, 스킬 19개 포함
  실제 npx 설치 왕복: install 19개 -> doctor 정상 -> 드리프트 감지 ->
  update 가 수정 파일 보존 -> uninstall 후 잔여 0

  배포 직전에 결함 하나를 잡았다. Windows 에서 작업해 스킬이 전부 CRLF 로
  저장돼 있었고, 그대로 tarball 에 들어가면 SKILL.md 의 YAML 프론트매터를
  파서에 따라 못 읽는다. 번들 스크립트에서 LF 로 정규화하고
  .gitattributes 도 넣었다. 배포물은 어느 OS 에서 만들든 같아야 한다.

푸터 제거
  출처가 사라지지 않게 설치 섹션 끝에 한 줄만 남겼다(저장소 · MIT).
2026-08-20 16:58:24 +09:00
Yun Chan
8267905c0c 테두리 광을 회전시키고 three.js 모바일 대응을 고친다
회전 테두리
  conic 의 각도를 @property 로 돌리면 매 프레임 배경을 다시 칠한다 —
  컴포지터에서 못 돌고 메인 스레드가 페인트한다(하드 게이트 8).
  정사각형 원뿔 레이어를 마스크 안에서 rotate 시켰다. transform 만 움직인다.
  aspect-ratio: 1 이라야 회전 중 밝기가 일정하고,
  width: 200% 라야 1178x52 헤더에서 모서리까지 닿는다. 주기 11초.

three.js 모바일
  세로 화면에서 게이트가 통째로 안 보이고 있었다.
  수직 시야는 화면 비율과 무관하지만 수평 시야는 aspect 에 비례한다.
  데스크톱(2.5:1)에서 정한 판 폭 15 가 세로 화면(0.45:1)의 수평 시야
  2.5 에 비해 여섯 배라, 첫 판이 화면을 완전히 덮었다.
  fit() 에서 camera.aspect 를 갱신할 때 판의 scale.x 도 같이 잡는다.

  저사양 판정도 고쳤다. hardwareConcurrency <= 4 만 보면 요즘 폰이 전부
  고사양으로 잡힌다(8코어가 흔하다). innerWidth < 760 을 함께 본다.
  DPR 1.5 -> 1.25, 캔버스 불투명도 0.7 -> 0.45(세로는 화면 전체를 덮는다).

스킬
  svg-filters.md  테두리 광 회전은 각도가 아니라 정사각형 레이어 rotate
  three.md        지오메트리를 화면 비율에 맞춰 다시 잡아라
                  저사양 판정에 화면 폭을 넣어라
2026-08-20 16:37:49 +09:00
Yun Chan
54ec6fa582 패럴랙스를 마무리한다 — 원경·중경·근경 세 깊이
전 레이어를 실측하고 지도를 design.md 에 남겼다.

  히어로 배경   셰이더 흐름 필드   scroll()   +56px 하강
  히어로 글자                      scroll()   -88px 상승 (반대 방향)
  파이프라인    게이트 씬          sticky     6단계 통과
    단계명                          —         0 고정
    산출물                          view()    ±14px
  재료·설치·프리플라이트  사진      fixed     0 — 원경
  사례          사진               drift 0.4  이미지 높이의 28.6%
  조판          종이               drift 0.25 -122px — 근경
  재료 타일                        view()     ±26 / ±58px
  프리플라이트 항목                view()     6 -> 10px
  수치                             없음       의도적 정지

세 깊이가 다 있다. 종이를 원경으로 두지 않은 것은 손에 닿는 거리의
재질이기 때문이다. 한 층(단계명·수치)은 고정이어야 기준이 생긴다.

모바일 390px 에서도 fixed 가 유지된다 — background-attachment 를 안 쓴 덕이다.
prefers-reduced-motion 에서는 게이트 씬이 시작하지 않고, 고정 배경은
absolute 로 돌아가며, view() 애니메이션이 전부 꺼진다.

최종 검사: 토큰 밖 색상 0 · radius 2종 · 100vh 0 · scroll 리스너 0 ·
하드코딩 웨이트 0 · 가로 스크롤 0(390/1440) · 테스트 22개 통과
2026-08-20 16:16:49 +09:00
Yun Chan
51d452e66c 유리 테두리를 conic 에서 linear 로 바꿔 이어지게 한다
헤더 헤어라인이 변마다 끊겨 보였다. conic-gradient 는 중심에서 각도로
도는데, 1180x54 처럼 납작한 형태에서는 각도가 급변해 긴 변에서 색이
거의 안 변하고 짧은 변에서 한꺼번에 바뀐다. 실제로 아래쪽만 절반 밝았다.

linear-gradient(100deg) 로 바꾸니 네 변이 같은 그라디언트를 공유해
테두리를 따라 이어진다. 마스크로 1px 만 남기는 구조는 그대로다.

얕은 글로우를 더했다 — 0 0 22px -8px 에 강조색 42%.
음수 spread 라 형태 밖으로 새지 않는다.
밝은 밴드에서는 흰빛 대신 그림자로 뒤집는다.

스킬 svg-filters.md 에 "유리 테두리를 그릴 때" 추가.
비율을 보고 conic/linear 를 고르라는 기준과 음수 spread 글로우.
2026-08-20 16:14:24 +09:00
Yun Chan
91bb4ae9dc 히어로와 파이프라인이 같은 씬을 쓰던 것을 나눈다
게이트 씬을 파이프라인으로 옮기면서 히어로에도 남겨뒀더니 같은 그림이
페이지에 두 번 나왔다. 역할이 다르면 씬도 달라야 한다.

  히어로     shader-plane 흐름 필드   노이즈를 두 번 접어 빛의 결을 만든다
                                      공간을 만드는 재질. 아무것도 반응 안 한다
  파이프라인 gate-scene 유리판 여섯   통과하면 그 단계가 켜진다. 주인공
  재료       shader-plane 스테이지    필터 타일이 앉는 무대

히어로에는 무거운 씬을 임포트하지 않는다 — three.md §0 대로 셰이더 플레인 하나다.
lib/hero-scene.ts -> lib/gate-scene.ts 로 리네임했다. 이름이 용도와 다르면
다음 사람이 히어로 것인 줄 알고 고친다.

대비 실측: 캔버스 배경 최악 8.46:1(가장 밝은 중앙 여백), h1 자리 14.83:1.
readPixels 로 재려다 전부 0 이 나왔다 — preserveDrawingBuffer: false 면
버퍼가 비어 있다. 스크린샷 픽셀로 쟀고, 글자 없는 영역만 골랐다.

스킬 three.md
  씬을 두 자리에 재사용하지 마라. 역할(주인공/재질/무대)로 나눠라
  파일 이름을 용도와 맞춰라
  WebGL 위 대비는 readPixels 가 아니라 스크린샷 픽셀로 잰다.
  0 을 검정으로 착각하면 대비가 통과한 것처럼 보인다
2026-08-20 16:11:53 +09:00
Yun Chan
af3a08397e 배경을 뷰포트에 고정하고, 게이트 씬을 파이프라인으로 옮긴다
"스크롤할 때 이미지가 고정되어 원경처럼 보이는 게 패럴랙스의 꽃"이라는
지적이 맞았다. 느리게 같이 움직이는 것으로는 그 인상이 안 나온다 —
속도만 다를 뿐 방향이 같기 때문이다.

고정 배경
  background-attachment: fixed 는 iOS Safari 가 GPU 메모리 때문에
  throttle 해서 스크롤 중 배경이 튄다. 쓰지 않았다.
  섹션에 clip-path: inset(0) 을 걸어 position: fixed 의 컨테이닝 블록으로
  만들었다. 배경이 화면에 멈춘 채 그 섹션 안에서만 보인다.
  실측: 스크롤 1000px 구간에서 viewportTop 이 계속 0.

  대가 둘을 받아들였다
    조상에 transform/filter/mask 를 못 쓴다 — 페이드 마스크를 포기했다
    prefers-reduced-motion 에서는 absolute 로 되돌린다

  전부 고정하지는 않았다. 종이는 손에 닿는 거리라 원경으로 두면 거짓말이다
    재료·설치·프리플라이트  fixed  원경
    사례                    drift  두 결과물이 흐르는 자리
    조판                    drift  종이는 가깝다

게이트 씬을 파이프라인으로
  히어로에서는 의미를 판독기가 자막으로 설명해야 했다. 그림이 스스로
  말하지 못한다는 뜻이다. 옆에 여섯 단계가 적힌 자리로 옮기니 자막이
  필요 없어졌다 — 판을 통과하면 그 단계가 켜진다.

  캔버스는 sticky, 진행도는 섹션에서 읽는다.
  travel 을 섹션 높이로 잡았더니 캔버스가 사라진 뒤에도 진행이 남아
  6단계 중 3단계에서 멈췄다. (섹션 높이 - 뷰포트 높이)로 고쳤다.
  캔버스 위 텍스트 대비 14.2:1.

스킬
  motion.md  패럴랙스의 원형은 고정 · clip-path 기법과 그 대가
             sticky 캔버스의 진행도 계산
  three.md   씬의 의미는 옆의 콘텐츠가 반응할 때 성립한다.
             자막이 필요하면 배치를 의심해라
2026-08-20 16:08:11 +09:00
Yun Chan
1ed3c95542 패럴랙스를 실제로 보이게 고치고 격자를 걷어낸다
"티가 안 난다"는 지적이 맞았다. 세 가지를 동시에 잘못하고 있었다.

1. 이동량이 모자랐다
   여유를 28% 잡아놓고 92px 만 움직였다. 스크롤 1800px 구간에서 5%다.
   공식은 이미지를 컨테이너보다 크게 만들고 초과분 전체를 움직이는 것.
   depth 0.45 -> 높이 145%, translateY(-31.03%). 같은 구간에서 374px.
   translateY(%) 는 자기 크기 기준이라 컨테이너 높이를 몰라도 맞는다.

2. overflow: hidden 이 view() 타임라인을 죽이고 있었다
   overflow: hidden 은 스크롤 컨테이너를 만들고, 그러면 자식의 view() 가
   문서가 아니라 그 상자를 기준으로 잡힌다. 이미지는 상자 안에서 늘 같은
   자리라 진행도가 고정된다. 콘솔 에러도 없고 computed 값도 정상이라
   두 스크롤 위치에서 transform 을 재보고서야 이동 0px 인 것을 알았다.
   잘라내는 일은 섹션의 overflow: clip 에 맡겼다.

3. opacity 로 눌러 이미지를 지웠다
   어두운 광원 사진을 opacity 0.5 로 깔면 검정도 빛도 같이 죽는다.
   mix-blend-mode: screen 으로 바꿨다 — 검정은 투명, 밝은 곳만 더해진다.
   그래서 불투명도 0.85~1.0 을 써도 본문 대비가 안전하다.
   생성 프롬프트도 같은 함정이었다. "어둡게"를 너무 강하게 넣어서
   안전한 이미지가 아니라 없는 이미지를 만들었다.

격자 배경 제거
   사진이 들어온 뒤로 배경이 두 겹이라 서로 경쟁했고 표로 읽혔다.
   배경도 한 섹션에 하나다.

스킬 motion.md 에 넷 다 반영
   이동량 공식 · overflow 함정 · screen 합성 · 배경은 한 겹
2026-08-20 15:57:40 +09:00
Yun Chan
5a18806fb6 배경 레이어가 그리드 안에서 561px 로 잘리던 것을 고친다
절대배치인데도 배경이 화면 가운데 561px 띠로만 깔렸다.
.section 은 subgrid 이고 `.section > * { grid-column: main }` 이 걸려 있는데,
절대배치 요소라도 그리드 영역이 잡히면 그 영역이 컨테이닝 블록이 된다.
position: absolute 는 그리드에서 배치를 면제해주지 않는다.

그리고 overflow: visible 이라 위아래 14% 여유분이 이웃 섹션으로 넘쳤다 —
조판 섹션의 종이가 프리플라이트 하단에 밝은 회색 띠로 나타났다.

  .bg-photo { grid-column: full }
  .has-bg   { overflow: clip }

띠 폭 기준으로 잡았던 투명도도 다시 올렸다
  사례 0.3 -> 0.5 · 프리플라이트 0.55 -> 0.8
  재료 0.5 -> 0.6 · 설치 0.4 -> 0.55

1440px/390px 실렌더: 배경 5개 모두 전체 폭, 가로 스크롤 0.
화면 밖 섹션의 배경은 아직 로드되지 않는다 — lazy 가 의도대로 작동한다.
2026-08-20 15:44:15 +09:00
Yun Chan
2bb1da9ce9 섹션마다 다른 배경 사진을 만들어 붙인다
모션에 적용한 규칙("섹션마다 다른 말을 해야 한다")이 배경에서만 면제될
이유가 없다. 같은 그림을 전부에 깔면 그건 벽지다.

gpt-image-2 로 넉 장. 프롬프트마다 그 섹션의 주장을 그림으로 옮겼다
  bg-refraction  겹친 유리판을 통과한 빛     재료·설치   표면은 한 겹이 아니다
  bg-split       한 빔이 둘로 갈라진다       사례        같은 절차, 다른 결과
  bg-align       정렬선 하나가 끊겨 있다     프리플라이트 자기 결과물을 남의 것처럼 본다
  bg-paper       무광 인쇄용지, 섬유와 데클  조판        한글이 종이 위에 있어야 한다

넣지 않은 곳도 결정이다
  파이프라인·수치  격자가 그 섹션의 주장이다
  히어로          three.js 가 있다
  규칙            목록은 담백해야 한다

프롬프트에는 매번 토큰 값을 그대로 넣었다. 광원은 #4FD6C4 와 #3D7BD6 둘뿐,
보라·마젠타·무지개·프리즘 분광 금지. "흰 본문 뒤에 깔리니 활자와 경쟁할
만큼 밝은 곳이 있으면 안 된다" 를 매번 반복했다.

BgPhoto 를 variant 로 확장. 소스 WebP 37/14/11/112KB,
빌드 12개 합계 276KB. 뷰포트에 맞는 하나씩만 lazy 로 받는다.
2026-08-20 14:58:59 +09:00
Yun Chan
54b9628879 배경 사진을 gpt-image-2 로 만들어 기준면을 강화한다
CSS 그라디언트 광원만으로는 패럴랙스의 기준면이 약했다. 굴절과 그레인이
실제로 들어 있는 이미지를 Codex CLI 내장 image_gen(gpt-image-2)으로 생성했다.
스톡이 아니라 이 페이지의 토큰 값을 프롬프트에 그대로 넣어 만든 것이다.

  바탕 #0B0E12, 광원은 좌상단 딥 틸 #4FD6C4 와 우하단 파랑 #3D7BD6 둘뿐
  보라·마젠타·무지개 금지 — 슬롭 지문이다
  "흰 본문 뒤에 깔리니 활자와 경쟁할 만큼 밝은 곳이 있으면 안 된다.
   중앙은 조용하게, 밝기는 모서리 쪽에"
  사물·글자·사람·플레어·보케·비네트 전부 금지

components/BgPhoto.astro
  CSS background-image 가 아니라 <Image loading="lazy"> 를 절대배치한다.
  background-image 로 깔면 항상 받고 최적화도 안 된다
  설치 섹션은 flip 으로 좌우 반전 — 같은 그림으로 읽히지 않게
  섹션 경계에서 끊기지 않게 위아래를 마스크로 지운다
  패럴랙스는 -46/+46px 로 가장 느리다

빌드 결과 WebP 26KB / 14KB / 7KB. 소스도 PNG 2MB 대신 WebP 37KB.
재료·설치 섹션 대비 실패 0건.
2026-08-20 14:41:36 +09:00
Yun Chan
0e03bbafac 패럴랙스에 기준면을 준다 — 배경 격자와 광원 레이어
배경이 단색이면 패럴랙스는 성립하지 않는다. 앞의 것이 몇 px 어긋나도
비교할 대상이 없다. 히어로 말고는 기준면이 없어 시차가 안 보였다.

배경 이미지를 사 오는 대신 이 페이지가 이미 주장하는 것을 그렸다
  .bg-grid  간격 눈금 격자. 셀이 --grid-cell = --space-6
            파이프라인 · 수치. "모든 값이 토큰에서 나온다"가 배경에서도 성립
  .bg-glow  광원 블롭. 섹션마다 --glow-x 로 자리를 옮긴다
            재료 · 설치. 히어로의 빛이 마지막 CTA 에서 돌아온다

실측 3층 (스크롤 1000 -> 1600)
  격자 +14.9px  ·  산출물 -14px  ·  단계명 0
  한 층은 반드시 고정이다. 전부 움직이면 그냥 흔들리는 페이지다

그리드 wide 트랙을 좁은 화면에서 닫는다
  고정 10rem 이라 모바일에서도 좌우 80px 이 살아남아, 390px 에서 본문이
  x=112 부터 시작하고 폭이 278px 로 줄어 제목이 네 줄로 접혔다.
  --wide-pad 를 clamp 로 바꿔 닫았다. 이제 전부 x=32 에서 시작하고 두 줄이다

파이프라인 정렬선을 하나로
  제목 x=371 / 리드 x=435 / 스파인 x=143 이던 것을 전부 x=183 으로.
  제목·리드·브리프를 스파인과 같은 컨테이너에 넣었다

히어로 높이를 콘텐츠에 맞춘다
  모바일에서 콘텐츠 390px 에 736px 을 씌워 여백이 화면의 41% 였고
  다음 섹션이 화면 밖으로 밀려 스크롤 단서까지 사라졌다.
  모바일 min-height 제거(736 -> 554), 데스크톱 88dvh -> 78dvh
  배경 마스크 62% -> 82% 로 늘려 하단의 죽은 띠를 없앴다

스킬 motion.md 에 "패럴랙스에는 기준면이 필요하다" 를 추가.
페이지가 주장하는 것을 배경으로 그리는 표와, 한 층은 고정이라는 규칙.

390px / 1440px 실렌더: 가로 스크롤 0, 대비 실패 0. 테스트 22개 통과
2026-08-20 14:25:02 +09:00