diff --git a/.gitignore b/.gitignore
index 77d0ad2..9a2719e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,5 @@ apps/site/dist/
research/youtube/*.srt
research/youtube/*.vtt
apps/site/tools/.gen/
+.gemini/
+
diff --git a/apps/site/design.md b/apps/site/design.md
index 2ba5544..39edbf2 100644
--- a/apps/site/design.md
+++ b/apps/site/design.md
@@ -7,7 +7,8 @@
| 날짜 | 판 | 무엇이 바뀌었나 |
|---|---|---|
| 2026-08-20 | 초판 | `swiss-minimal` · three.js 미사용 · 라이트 기반 |
-| 2026-08-20 | **2판(현행)** | `dark-instrument` · three.js 채택 · 전체 경로 재주행 |
+| 2026-08-20 | 2판 | `dark-instrument` · three.js 채택 · 전체 경로 재주행 |
+| 2026-08-23 | **현행** | 쇼케이스 히어로 리뉴얼 — Three.js 추상 FBM 셰이더 캔버스(`ShowcaseHeroCanvas`), 한국어 에디토리얼 카피 개편, 15개 라이브 쇼케이스 및 디테일 페이지 서브링크 연동 |
**초판이 거짓말이 됐던 경위를 남긴다.** 사용자가 "글래스모피즘·리퀴드글래스·히어로에 힘"을 요청했을 때
그것은 2단계 방향 재결정이었고, 스킬은 그럴 때 **전체 경로로 올려 1단계부터 다시 돌라**고 씌어 있다.
@@ -849,3 +850,44 @@ v23 스크롤 리셋·단일 헤더에도 사용자 평가가 "여전히 깨져
- **게이트 공백 발견**: 기존 매트릭스는 오른쪽 '오버플로'만 검사하고 왼쪽 인셋의 최소값·정렬 정합은 검사하지 않았다(그래서 기준 갱신에 결함이 그대로 박혔다). e2e에 **M6 왼쪽 인셋** 스위트 신설(60단언): 전 뷰×320/390에서 텍스트 시작점 ≥12px + 헤더-제목 같은 축(±10px). RED 38 실패로 결함 재현 확인 후 수리로 GREEN.
- 측정 하자(瑕) 2건 교정: display:none 요소의 rect=0이 잡히던 것(vis 필터), 전폭 컨테이너 보더박스가 아닌 '텍스트 시작점' 기준.
- 시각 기준 갱신(두레 390px 6화면), verify L0~L6 전 통과(E2E 1,805단언).
+
+## 2026-08-23 — 신규 쇼케이스 3종 구축 (청온 케어 · 캐스크 아카이브 · 마테리아 스튜디오)
+
+`designpaca`, `design-taste-frontend`, `high-end-visual-design` 원칙을 적용하여 신규 쇼케이스 사이트 3종 제작 및 쇼케이스 아카이브에 등록:
+
+1. **청온 케어 (`cheongon-care`)**
+ - **프리셋**: `ledger-console` + `swiss-minimal`
+ - **강조색**: 에메랄드 틸 (`#0D9488`)
+ - **컨셉**: 임상 수준의 6단계 위생 프로토콜과 투명한 정찰제 견적, 실시간 고객 신청-관리자 관제 대시보드 풀-루프 연동 및 32개 QC 체크리스트 모달.
+2. **캐스크 아카이브 (`cask-archive`)**
+ - **프리셋**: `editorial-archival` + `dark-instrument`
+ - **강조색**: 싱글 몰트 앰버 골드 (`#D99B38`)
+ - **컨셉**: Gordon & MacPhail, Signatory, SMWS 독립병입자 공식 수입 인증 보증서, 캐스크 넘버 원장 실시간 검색, 1:1 도슨트 테이스팅 예약 및 이벤트/인스타그램 연동.
+3. **마테리아 스튜디오 (`materia-interior`)**
+ - **프리셋**: `editorial` + `high-end-visual-design`
+ - **강조색**: 브러시드 티타늄 브론즈 (`#9E8062`)
+ - **컨셉**: 트래버틴과 훈증 오크의 물성, 자연광 Day ↔ 야간 조도 Night 앰비언스 실시간 전환, 더블 베젤 프로젝트 갤러리 및 프라이빗 공간 컨설팅 신청.
+
+- **검증**: Astro 빌드 완료, Stylelint 통과, HTML-Validate 통과, 전 계층 QA 게이트(L0~L6) 100% 통과.
+
+## 2026-08-23 — 국소: 쇼케이스 상세 모달 및 카드 방문 액션 전면 재설계
+
+사용자 피드백: "showcase 페이지의 상세 모달 팝업에 스크롤을 해야지 사이트 보기 버튼이 있어서 사람들이 사이트 들어가질 않는 문제" 및 "왼쪽 섬네일이 그냥 cover 형태로 꽉 차는 게 이쁘겠다".
+
+- **문제 진단**:
+ - 기존 단일 열 모달 레이아웃에서 거대한 스크린샷과 설명/리스크/지표 박스 아래에 `.dialog-site` 링크가 묻혀 있어, 사용자가 모달을 열고 스크롤하지 않으면 사이트 진입 버튼의 존재를 알기 어려웠음.
+- **해결책 (2-Tier & Split Full-bleed Architecture)**:
+ - **데스크톱 (≥860px)**:
+ - 좌/우 2열 분할 스플릿 그리드(`dialog-layout`).
+ - 좌측(`dialog-media`): 100% 풀블리드 `object-fit: cover` 스크린샷 뷰어 패널(여백·내부 테두리 제거로 미려하고 꽉 찬 인상).
+ - 우측(`dialog-details`): 상단 헤더 바로 아래에 눈에 띄는 고대비 주 CTA 블록(`dialog-cta-block` + `.dialog-site-btn`)을 배치하여 **0px 스크롤(첫 시야)에서 100% 즉시 노출**.
+ - **모바일 (<860px)**:
+ - 상단 풀블리드 섬네일 커버 + 헤더 직하단 CTA 블록.
+ - 하단 고정 스티키 액션 독(`dialog-sticky-dock` + `.dialog-dock-btn`)을 적용하여 스크롤 중에도 엄지 영역에 상시 '사이트 보기' 버튼 고정.
+ - **카드 그리드 즉시 진입**:
+ - 카드 썸네일 호버 시 우측 하단에 `.card-quick-visit` 알약 버튼 노출.
+ - 타이틀 행에 `.card-direct-link` 아이콘 버튼을 배치하여 모달을 열지 않고도 바로 새 탭에서 데모를 체험할 수 있도록 다이렉트 액션 강화(`z-index: 4`).
+- **글로벌 리셋 보강**:
+ - `base.css`에 글로벌 미디어 리셋(`img, picture, video, canvas, svg { max-width: 100%; display: block; }`) 및 `html { overflow-x: hidden; }` 안전장치 추가.
+- **자동화 검증**:
+ - `tools/test-showcase-dialog.mjs` (1440px / 768px / 390px / 320px 4개 뷰포트 E2E): 데스크톱 0px 스크롤 CTA 노출 확인, 좌측 cover 핏 실측, 모바일 스티키 독 활성화 확인, 모달 닫기/Esc/배경 클릭/카드 액션 등 17개 단언 100% PASS.
diff --git a/apps/site/public/work/cask-archive/assets/aroma-wheel.webp b/apps/site/public/work/cask-archive/assets/aroma-wheel.webp
new file mode 100644
index 0000000..5a4f260
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/aroma-wheel.webp differ
diff --git a/apps/site/public/work/cask-archive/assets/cellar.webp b/apps/site/public/work/cask-archive/assets/cellar.webp
new file mode 100644
index 0000000..b08239d
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/cellar.webp differ
diff --git a/apps/site/public/work/cask-archive/assets/docent-room.webp b/apps/site/public/work/cask-archive/assets/docent-room.webp
new file mode 100644
index 0000000..3139a43
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/docent-room.webp differ
diff --git a/apps/site/public/work/cask-archive/assets/masterclass-lineup.webp b/apps/site/public/work/cask-archive/assets/masterclass-lineup.webp
new file mode 100644
index 0000000..df80e2e
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/masterclass-lineup.webp differ
diff --git a/apps/site/public/work/cask-archive/assets/whisky-hero.webp b/apps/site/public/work/cask-archive/assets/whisky-hero.webp
new file mode 100644
index 0000000..504b864
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/whisky-hero.webp differ
diff --git a/apps/site/public/work/cask-archive/assets/whisky-pairing.webp b/apps/site/public/work/cask-archive/assets/whisky-pairing.webp
new file mode 100644
index 0000000..4cf4234
Binary files /dev/null and b/apps/site/public/work/cask-archive/assets/whisky-pairing.webp differ
diff --git a/apps/site/public/work/cask-archive/docent/index.html b/apps/site/public/work/cask-archive/docent/index.html
new file mode 100644
index 0000000..c636078
--- /dev/null
+++ b/apps/site/public/work/cask-archive/docent/index.html
@@ -0,0 +1,459 @@
+
+
+
+
+
+ 1:1 위스키 도슨트 테이스팅 마스터클래스 — CASK ARCHIVE (樽室)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
BESPOKE TASTING METHODOLOGY
+
1:1 위스키 도슨트 마스터클래스
+
+ 공인 위스키 마스터가 1:1로 당신의 후각과 미각 취향을 해체하고 조립하는 프라이빗 테이스팅 세션.
+
+
+
+
+
Session Type
+ 1:1 Private Tasting
+
+
+
Location
+ Cask Room (한남 27길)
+
+
+
Capacity
+ 최대 2인 (동반 1인 가능)
+
+
+
Glassware
+ Glencairn & Copita
+
+
+
+
+
+
+
+ 한남동 캐스크 룸 · 마스터 전용 1:1 테이스팅 카운터
+
+
+
+
+
+
+
+
+
+
+
+
+
SENSORY ANALYSIS
+
+ 후각과 미각의 지도를 그리는
+ 4축 아로마 분석법
+
+
+
+ 1. Peat Smoke (피트/스모키) : 아일라 섬의 이탄 연기, 해초의 요오드(Iodine), 정향과 재(Ash)의 깊이.
+
+
+ 2. Dried Fruits (건과일 & 셰리) : 올로로소/PX 캐스크에서 추출된 말린 무화과, 건포도, 다크 초콜릿의 농밀함.
+
+
+ 3. Floral & Orchard (에스테르 & 과원) : 장기 발효와 구리 환류에서 비롯된 풋사과, 엘더플라워, 꿀의 산뜻함.
+
+
+ 4. Charred Oak (오크 바닐린) : 1st Fill 버번 배럴의 그을린 탄닌, 토피, 시나몬 스파이스.
+
+
+
+
+
+
+
+
+ CURATED FLIGHT SELECTION
+
+ 도슨트 테이스팅 정규 코스
+
+
+
+
+
+
+
+ COURSE A
+ 65,000원
+
+
+ 독립병입자 입문 탐색 플라이트 (3잔)
+
+
+ 증류소의 기본 공식 라인업(OB)과 독립병입자(IB) 싱글 캐스크의 결정적 차이를 체감하는 코스입니다.
+
+
+
+
+
+ 순서
+ 라인업 구성
+ ABV
+
+
+
+
+ 1
+ 하이랜드 에스테르 (클라인리쉬 14y vs IB 캐스크)
+ 46.0% / 54.2%
+
+
+ 2
+ 스페이사이드 셰리 (글렌토커스 1st Fill 셰리 버트)
+ 56.8% CS
+
+
+ 3
+ 아일라 헤비 피트 (라프로익 2005 SMWS 29.281)
+ 57.8% CS
+
+
+
+
+
+
+
+
+
+
+
+ COURSE B
+ 120,000원
+
+
+ 캐스크 스트렝스 & 빈티지 마스터 플라이트 (5잔)
+
+
+ 물 한 방울 타지 않은 원액(Cask Strength)과 1990년대 올드 빈티지 캐스크 5종을 정밀 비교 테이스팅합니다.
+
+
+
+
+
+ 순서
+ 라인업 구성
+ ABV
+
+
+
+
+ 1
+ 스프링뱅크 1993 (G&M 리필 셰리 호그스헤드 27y)
+ 51.6% CS
+
+
+ 2
+ 모틀락 1989 (사마롤리 22y 싱글 캐스크)
+ 50.4% CS
+
+
+ 3
+ 맥캘란 1993 (카덴헤드 1842 클래식 레인지)
+ 54.8% CS
+
+
+ 4
+ 쿨일라 2008 (PX 피니시 14y)
+ 56.4% CS
+
+
+ 5
+ 벤네비스 1998 (아델피 버번 캐스크 22y)
+ 58.1% CS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
GASTRONOMY PAIRING
+
+ 향과 질감의 증폭을 위한
+ 아티잔 푸드 페어링
+
+
+ 위스키의 높은 알코올 도수와 강렬한 탄닌을 부드럽게 감싸안고, 숨겨진 미세 에스테르를 끌어올리기 위해
+ 스페인산 이베리코 하몽 베요타, 에콰도르 싱글 오리진 70% 다크 카카오 트러플, 터키산 흑무화과를 페어링 플레이트로 함께 서브합니다.
+
+
+
+
+
+
+
+
+
+
+
+
RESERVATION REQUEST
+
+ 도슨트 테이스팅 세션 실시간 예약
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/cask-archive/index.html b/apps/site/public/work/cask-archive/index.html
new file mode 100644
index 0000000..ab746f8
--- /dev/null
+++ b/apps/site/public/work/cask-archive/index.html
@@ -0,0 +1,491 @@
+
+
+
+
+
+ 캐스크 아카이브 (CASK ARCHIVE · 樽室) · 독립병입자 위스키바 & 도슨트 큐레이션
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 본문으로 건너뛰기
+
+
+
+
+
+
+
+
+
+
+ OFFICIALLY CERTIFIED INDEPENDENT BOTTLER BAR
+
+
+ 시간과 배럴이 빚어낸
+ 오직 단 하나의 원액(Single Cask)
+
+
+ 증류소의 기본 공식 라인업에서는 맛볼 수 없는 독립병입자(Independent Bottler)의 희귀 원액들. 공식 정식 수입 필증과 단일 캐스크 일련번호(Single Cask Number)를 보증하며, 전담 도슨트가 취향에 맞춘 위스키를 1:1로 큐레이션합니다.
+
+
+
+
+
+
+
+
+
+
+
+
CASK #8821 · 58.4% ABV
+
Signatory Vintage 1997 · Bottle 42 of 186
+
+
+
+
+
+
+
+
+
+
GORDON & MACPHAIL
+
1895년 설립 · 스코틀랜드 최고 권위 정식 수입
+
+
+
SIGNATORY VINTAGE
+
언칠필터드 / 내추럴 컬러 캐스크 스트렝스 보증
+
+
+
THE SCOTCH MALT WHISKY SOCIETY
+
SMWS 공식 파트너 베뉴 · 넘버링 큐레이션
+
+
+
CADENHEAD'S
+
1842년 스코틀랜드 최고(最古) 독립병입자 라인업
+
+
+
+
+
+
+
+
+
독립병입자 캐스크 실시간 보유 원장
+
모든 병은 단일 캐스크(Single Cask)에서 추출되어 병입 수량이 한정되어 있습니다. 필터로 원하는 향미 스펙을 검색하십시오.
+
+
+
+
+ 전체 원장 (5)
+ 셰리 캐스크
+ 피트 / 스모키
+ 버번 배럴
+ 캐스크 스트렝스 (50%+)
+
+
+
+
+
+
+
+
+ 캐스크 번호
+ 증류소 & 원액 명칭
+ 독립병입자 (Bottler)
+ 숙성 배럴 / 년수
+ 도수 (ABV)
+ 한정 수량
+ 향미 노트 요약
+ 인증 정보
+
+
+
+
+ #6512
+ Clynelish 1996 하이랜드 · 왁시 꿀 & 린덴
+ Signatory Vintage
+ 1st Fill Sherry Butt · 24년
+ 54.2%
+ 180 Btls
+ 말린 무화과, 밀랍, 시나몬, 다크초콜릿
+ 수입인증서
+
+
+ #29.281
+ Laphroaig 2005 아일라 · 약용 피트 & 해풍
+ SMWS (Society)
+ 1st Fill Ex-Bourbon · 15년
+ 57.8%
+ 214 Btls
+ 모닥불 연기, 바다 소금, 레몬 제스트, 피트 45ppm
+ 수입인증서
+
+
+ #93/41
+ Springbank 1993 캠벨타운 · 미네랄 & 오일
+ Gordon & MacPhail
+ Refill Sherry Hogshead · 27년
+ 51.6%
+ 142 Btls
+ 열대 과일 잼, 가죽, 부드러운 유황, 정향
+ 수입인증서
+
+
+ #PX-88
+ Caol Ila 2008 아일라 · 달콤한 훈연
+ Cadenhead's
+ Pedro Ximenez Finish · 14년
+ 56.4%
+ 260 Btls
+ 스모크 베이컨, 건포도, 블랙베리, 훈제 파프리카
+ 수입인증서
+
+
+ #98/12
+ Ben Nevis 1998 서부 하이랜드 · 묵직한 바디
+ Adelphi Distillery
+ Ex-Bourbon Cask · 22년
+ 58.1%
+ 195 Btls
+ 바닐라 커스터드, 구운 코코넛, 청사과, 후추
+ 수입인증서
+
+
+
+
+
+
+
+
+
+
+
+
1:1 맞춤형 위스키 도슨트 테이스팅
+
공인 위스키 마스터가 당신의 향미 선호도를 분석하여 독립병입자 캐스크를 1:1로 해설하고 페어링합니다.
+
+
+
+
+
+
+
+
+ 증류소 오피셜 보틀과 동일 증류소의 독립병입자 싱글 캐스크를 1:1로 비교하며 배럴 숙성의 차이를 체감하는 코스.
+
+
소요 시간: 45분 · 도슨트 1:1 배정 · 하이엔드 글렌캐런 글라스 제공
+
+
+
+
+
+ 1990년대 올드 빈티지 및 55% 이상의 싱글 캐스크 스트렝스 5종을 테이스팅 휠과 함께 정밀 분석하는 심화 코스.
+
+
소요 시간: 75분 · 아티잔 초콜릿 & 샤퀴테리 페어링 포함
+
+
+
+
+
+
+
+
+
+
+
+
+
+
프라이빗 시음회 & 릴리즈 이벤트
+
월별 한정 입고되는 독립병입자 캐스크 오프닝 파티 및 정기 세미나 일정입니다.
+
+
+
+
+
+ 09.12
+ FRI 20:00
+
+
+
+ 1990s 올드 셰리 버트 4종 버티컬 시음회
+
+
+ Signatory & G&M 90년대 빈티지 셰리 캐스크 4종 비교 테이스팅 (정원 8인 한정)
+
+
+
+
+
+
+ 09.26
+ SAT 21:00
+
+
+
+ 아일라 옥토모어 & 피트 심야 도슨트 나이트
+
+
+ 페놀 수치 80ppm 이상의 극한 피트 독립병입자 바틀 블라인드 테이스팅
+
+
+
+
+
+
+
+
EXCLUSIVE CASK NOTIFICATION
+
+ 신규 캐스크 입고 알림 & 인스타그램
+
+
+ 수량이 극소량인 독립병입자 캐스크 오프닝 소식을 이메일 뉴스레터로 가장 먼저 발송해 드립니다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 공식 독립병입자 수입 보증서
+
+ ✕
+
+
+
증류소 및 보틀:
+
독립병입자:
+
숙성 정보:
+
알코올 도수:
+
통관 필증 번호:
+
+
+ 확인 완료
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/cask-archive/styles/main.css b/apps/site/public/work/cask-archive/styles/main.css
new file mode 100644
index 0000000..43458b1
--- /dev/null
+++ b/apps/site/public/work/cask-archive/styles/main.css
@@ -0,0 +1,788 @@
+@import "./tokens.css";
+
+/* ==========================================================================
+ 캐스크 아카이브 (CASK ARCHIVE · 樽室) — 메인 스타일시트 SSOT
+ 프리셋: editorial-archival + dark-instrument + high-end-visual-design
+ ========================================================================== */
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ scroll-behavior: smooth;
+ background-color: var(--surface);
+ color: var(--ink);
+ font-family: var(--font-body);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ overflow-x: hidden;
+}
+
+body {
+ min-height: 100dvh;
+ line-height: var(--leading-normal);
+ overflow-x: hidden;
+ background: var(--surface);
+ color: var(--ink);
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+ transition: color var(--dur-fast);
+}
+
+a:hover {
+ color: var(--accent);
+}
+
+button, input, select, textarea {
+ font: inherit;
+ color: inherit;
+}
+
+button {
+ cursor: pointer;
+ border: none;
+ background: none;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+.mono {
+ font-family: var(--font-mono);
+}
+
+.serif {
+ font-family: var(--font-serif);
+}
+
+.italic {
+ font-family: var(--font-serif);
+ font-style: italic;
+}
+
+.gold {
+ color: var(--accent);
+}
+
+.wrap {
+ width: min(100% - 2 * var(--pad-inline), var(--container));
+ margin-inline: auto;
+}
+
+/* ── 접근성 스킵 링크 ── */
+.skip-link {
+ position: fixed;
+ top: 1rem;
+ left: 1rem;
+ z-index: 1000;
+ padding: 0.5rem 1rem;
+ background: var(--accent);
+ color: var(--accent-ink);
+ border-radius: var(--radius-xs);
+ transform: translateY(-200%);
+ transition: transform var(--dur-fast);
+ text-decoration: none;
+ font-weight: 700;
+}
+.skip-link:focus {
+ transform: translateY(0);
+}
+
+/* ── 헤더 (GNB SSOT) ── */
+.site-head {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: rgba(11, 11, 13, 0.94);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--line);
+ padding-block: var(--space-3);
+}
+
+.head-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-4);
+}
+
+.wordmark {
+ display: inline-flex;
+ align-items: baseline;
+ gap: var(--space-2);
+ font-family: var(--font-serif);
+ font-size: var(--step-1);
+ font-weight: 700;
+ color: var(--ink);
+ text-decoration: none;
+ letter-spacing: 0.04em;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.wordmark-han {
+ font-size: var(--step--1);
+ color: var(--accent);
+ font-weight: 500;
+ letter-spacing: 0.08em;
+}
+
+.nav {
+ display: flex;
+ align-items: center;
+ gap: var(--space-5);
+ list-style: none;
+}
+
+.nav a {
+ text-decoration: none;
+ color: var(--ink-muted);
+ font-size: var(--step--1);
+ font-weight: 500;
+ transition: color var(--dur-fast);
+ white-space: nowrap;
+}
+
+.nav a:hover,
+.nav a.active {
+ color: var(--ink);
+}
+
+.head-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ flex-shrink: 0;
+}
+
+.head-actions .btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--accent);
+ color: #0b0b0d;
+ font-weight: 700;
+ font-size: var(--step--1);
+ padding: 6px 14px;
+ border-radius: var(--radius-xs);
+ transition: opacity var(--dur-fast), transform var(--dur-fast);
+}
+
+.head-actions .btn:hover {
+ opacity: 0.9;
+ transform: translateY(-1px);
+}
+
+/* ── 버튼 시스템 & 배지 ── */
+.btn-amber {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ background: var(--accent);
+ color: #0b0b0d;
+ padding: 10px 20px;
+ border-radius: var(--radius-xs);
+ font-weight: 700;
+ font-size: var(--step--1);
+ text-decoration: none;
+ white-space: nowrap;
+ transition: transform var(--dur-fast), background var(--dur-fast), opacity var(--dur-fast);
+ border: 1px solid var(--accent);
+}
+
+.btn-amber:hover {
+ background: #e6ab48;
+ color: #0b0b0d;
+ transform: translateY(-1px);
+}
+
+.btn-outline {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ background: transparent;
+ color: var(--ink);
+ border: 1px solid var(--line);
+ padding: 10px 18px;
+ border-radius: var(--radius-xs);
+ font-weight: 600;
+ font-size: var(--step--1);
+ text-decoration: none;
+ white-space: nowrap;
+ transition: border-color var(--dur-fast), color var(--dur-fast);
+}
+
+.btn-outline:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+.eyebrow-amber {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 10px;
+ border-radius: var(--radius-xs);
+ background: rgba(217, 155, 56, 0.12);
+ color: var(--accent);
+ font-size: 11px;
+ font-family: var(--font-mono);
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ margin-bottom: var(--space-3);
+ border: 1px solid rgba(217, 155, 56, 0.25);
+ text-transform: uppercase;
+}
+
+/* ── 히어로 섹션 ── */
+.hero-section {
+ padding-block: var(--space-8) var(--space-7);
+ position: relative;
+}
+
+.hero-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.95fr);
+ gap: var(--space-7);
+ align-items: center;
+}
+
+.hero-title {
+ font-family: var(--font-serif);
+ font-size: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem);
+ font-weight: 800;
+ line-height: 1.2;
+ letter-spacing: -0.025em;
+ color: var(--ink);
+ margin-bottom: var(--space-4);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.hero-desc {
+ font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.2rem);
+ color: var(--ink-muted);
+ max-width: 48ch;
+ line-height: 1.75;
+ margin-bottom: var(--space-6);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.hero-ctas {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ flex-wrap: wrap;
+}
+
+.hero-visual {
+ position: relative;
+}
+
+.bezel-outer {
+ background: rgba(217, 155, 56, 0.06);
+ border: 1px solid var(--line);
+ padding: 6px;
+ border-radius: var(--radius-md);
+}
+
+.hero-frame {
+ border-radius: calc(var(--radius-md) - 4px);
+ overflow: hidden;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ box-shadow: 0 20px 48px -12px rgba(0, 0, 0, 0.7);
+ background: #141418;
+}
+
+.hero-frame img {
+ width: 100%;
+ aspect-ratio: 4/3;
+ object-fit: cover;
+ display: block;
+}
+
+.hero-cask-tag {
+ margin-top: var(--space-2);
+ padding: 6px 12px;
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+}
+
+.cask-tag-no {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--accent);
+}
+
+.cask-tag-sub {
+ font-size: 11px;
+ color: var(--ink-muted);
+}
+
+/* ── 공식 독립병입자 수입 인증 스트립 ── */
+.ib-cert-strip {
+ border-block: 1px solid var(--line);
+ background: var(--surface-raised);
+ padding-block: var(--space-5);
+}
+
+.cert-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: var(--space-4);
+}
+
+.cert-item {
+ border-left: 2px solid var(--accent);
+ padding-left: var(--space-3);
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.cert-name {
+ font-family: var(--font-mono);
+ font-size: var(--step--1);
+ font-weight: 700;
+ color: var(--ink);
+ letter-spacing: 0.05em;
+}
+
+.cert-desc {
+ font-size: 12px;
+ color: var(--ink-muted);
+ line-height: 1.4;
+}
+
+/* ── 캐스크 원장 섹션 (Interactive Cask Ledger) ── */
+.ledger-section {
+ padding-block: var(--space-8);
+}
+
+.section-head {
+ text-align: center;
+ max-width: 680px;
+ margin-inline: auto;
+ margin-bottom: var(--space-6);
+}
+
+.section-title {
+ font-family: var(--font-serif);
+ font-size: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem);
+ font-weight: 800;
+ letter-spacing: -0.02em;
+ color: var(--ink);
+ margin-bottom: var(--space-2);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.section-sub {
+ font-size: var(--step-0);
+ color: var(--ink-muted);
+ line-height: 1.6;
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.ledger-filter-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+ margin-bottom: var(--space-4);
+}
+
+.filter-btn-group {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ flex-wrap: wrap;
+}
+
+.filter-btn {
+ padding: 6px 14px;
+ border-radius: var(--radius-xs);
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ color: var(--ink-muted);
+ font-size: 12px;
+ font-weight: 600;
+ transition: all var(--dur-fast);
+}
+
+.filter-btn:hover {
+ border-color: var(--accent);
+ color: var(--ink);
+}
+
+.filter-btn.is-active {
+ background: rgba(217, 155, 56, 0.15);
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+.search-input {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ color: var(--ink);
+ padding: 8px 14px;
+ border-radius: var(--radius-xs);
+ font-size: 13px;
+ min-width: 240px;
+ outline: none;
+ transition: border-color var(--dur-fast);
+}
+
+.search-input:focus {
+ border-color: var(--accent);
+}
+
+.ledger-table-wrap {
+ overflow-x: auto;
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ background: var(--surface-raised);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
+ /* 앰버 골드 커스텀 스크롤바 (Firefox & 표준) */
+ scrollbar-width: thin;
+ scrollbar-color: var(--accent) rgba(255, 255, 255, 0.04);
+}
+
+/* 앰버 골드 커스텀 스크롤바 (WebKit / Chrome / Safari / Edge) */
+.ledger-table-wrap::-webkit-scrollbar {
+ height: 6px;
+ background: var(--surface-raised);
+}
+
+.ledger-table-wrap::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.02);
+ border-radius: 3px;
+}
+
+.ledger-table-wrap::-webkit-scrollbar-thumb {
+ background: var(--accent);
+ border-radius: 3px;
+ border: 1px solid rgba(11, 11, 13, 0.5);
+}
+
+.ledger-table-wrap::-webkit-scrollbar-thumb:hover {
+ background: #e6ab48;
+}
+
+.ledger-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 12.5px;
+ text-align: left;
+ line-height: 1.5;
+}
+
+.ledger-table th {
+ background: rgba(255, 255, 255, 0.03);
+ padding: 12px 10px;
+ color: var(--ink-muted);
+ font-weight: 600;
+ border-bottom: 1px solid var(--line);
+ font-family: var(--font-mono);
+ font-size: 11.5px;
+ letter-spacing: 0.04em;
+ white-space: nowrap;
+}
+
+.ledger-table td {
+ padding: 12px 10px;
+ border-bottom: 1px solid var(--line);
+ vertical-align: middle;
+}
+
+.ledger-table tbody tr {
+ transition: background var(--dur-fast);
+}
+
+.ledger-table tbody tr:hover {
+ background: rgba(217, 155, 56, 0.06);
+}
+
+.ledger-table td:nth-child(1),
+.ledger-table td:nth-child(3),
+.ledger-table td:nth-child(4),
+.ledger-table td:nth-child(5),
+.ledger-table td:nth-child(6),
+.ledger-table td:nth-child(8) {
+ white-space: nowrap;
+}
+
+.ledger-table td:nth-child(7) {
+ white-space: normal;
+ max-width: 240px;
+ font-size: 12px;
+ color: #d1d5db;
+ text-wrap: pretty;
+}
+
+.cask-badge {
+ font-weight: 700;
+ color: var(--accent);
+}
+
+/* ── 1:1 위스키 도슨트 테이스팅 코스 ── */
+.docent-section {
+ padding-block: var(--space-8);
+ border-top: 1px solid var(--line);
+ background: var(--surface-card);
+}
+
+.docent-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
+ gap: var(--space-6);
+ align-items: start;
+}
+
+.course-list {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+}
+
+.course-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-5);
+ transition: border-color var(--dur-fast);
+}
+
+.course-card:hover {
+ border-color: var(--accent);
+}
+
+.course-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ margin-bottom: var(--space-2);
+ gap: var(--space-3);
+}
+
+.course-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-1);
+ font-weight: 700;
+ color: var(--ink);
+ letter-spacing: -0.02em;
+}
+
+.course-price {
+ font-family: var(--font-mono);
+ font-size: var(--step-0);
+ font-weight: 700;
+ color: var(--accent);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.docent-booking-form {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-6);
+}
+
+.form-group {
+ margin-bottom: var(--space-4);
+}
+
+.form-label {
+ display: block;
+ font-size: 12px;
+ font-family: var(--font-mono);
+ font-weight: 600;
+ color: var(--ink-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 6px;
+}
+
+.form-select,
+.form-input {
+ width: 100%;
+ padding: 10px 12px;
+ background: var(--surface);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ color: var(--ink);
+ font-size: 13px;
+ outline: none;
+ transition: border-color var(--dur-fast);
+}
+
+.form-select:focus,
+.form-input:focus {
+ border-color: var(--accent);
+}
+
+/* ── 이벤트 & 뉴스레터 ── */
+.events-section {
+ padding-block: var(--space-8);
+ border-top: 1px solid var(--line);
+}
+
+.event-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--space-4);
+ margin-bottom: var(--space-7);
+}
+
+.event-item {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-5);
+ display: flex;
+ gap: var(--space-4);
+ align-items: center;
+}
+
+.event-date-box {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 14px;
+ background: rgba(217, 155, 56, 0.1);
+ border: 1px solid rgba(217, 155, 56, 0.25);
+ border-radius: var(--radius-xs);
+ min-width: 80px;
+ flex-shrink: 0;
+}
+
+.event-day {
+ font-family: var(--font-mono);
+ font-size: var(--step-1);
+ font-weight: 800;
+ color: var(--accent);
+}
+
+.event-month {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ color: var(--ink-muted);
+ letter-spacing: 0.05em;
+}
+
+.newsletter-card {
+ text-align: center;
+ background: var(--surface-card);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-md);
+ padding: var(--space-7) var(--space-5);
+ max-width: 720px;
+ margin-inline: auto;
+}
+
+.newsletter-form {
+ display: flex;
+ gap: var(--space-2);
+ max-width: 480px;
+ margin: var(--space-5) auto 0;
+}
+
+/* ── 인증서 모달 ── */
+.cert-dialog {
+ position: fixed;
+ inset: 0;
+ margin: auto;
+ width: min(90vw, 480px);
+ background: #141418;
+ color: #f3f3f5;
+ border: 1px solid rgba(217, 155, 56, 0.4);
+ border-radius: var(--radius-sm);
+ padding: var(--space-5);
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.8);
+ outline: none;
+}
+
+.cert-dialog::backdrop {
+ background: rgba(0, 0, 0, 0.75);
+ backdrop-filter: blur(8px);
+}
+
+/* ── 푸터 (SSOT 정본) ── */
+.site-foot {
+ border-top: 1px solid var(--line);
+ padding-block: var(--space-6);
+ background: var(--surface);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+}
+
+.foot-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: var(--space-4);
+}
+
+/* ── 반응형 미디어 쿼리 ── */
+@media (max-width: 860px) {
+ .nav {
+ display: none;
+ }
+ .hero-grid,
+ .docent-grid,
+ .event-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-5);
+ }
+ .cert-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ .newsletter-form {
+ flex-direction: column;
+ }
+}
+
+@media (max-width: 540px) {
+ .cert-grid {
+ grid-template-columns: 1fr;
+ }
+ .wordmark-han {
+ display: none;
+ }
+}
diff --git a/apps/site/public/work/cask-archive/styles/tokens.css b/apps/site/public/work/cask-archive/styles/tokens.css
new file mode 100644
index 0000000..b4d933b
--- /dev/null
+++ b/apps/site/public/work/cask-archive/styles/tokens.css
@@ -0,0 +1,74 @@
+/* ==========================================================================
+ 캐스크 아카이브 (CASK ARCHIVE · 樽室) — 디자인 토큰 SSOT
+ 프리셋: dark-instrument + editorial-archival
+ 컨셉: 독립병입자 희귀 캐스크 원장과 1:1 위스키 도슨트 — 오크와 피트의 시간
+ 단일 강조색: 앰버 골드 (#D99B38)
+ ========================================================================== */
+
+:root {
+ /* 타이포그래피 스케일 */
+ --step--1: 0.8125rem; /* 13px — 캡션·일련번호 */
+ --step-0: 1rem; /* 16px — 본문 */
+ --step-1: 1.1875rem; /* 19px — 소제목 */
+ --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem); /* 30px — 섹션 표제 */
+ --step-3: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem); /* 36px — 서브 표제 */
+ --step-4: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem); /* 60px — 메인 디스플레이 */
+
+ --leading-tight: 1.18;
+ --leading-normal: 1.7;
+ --measure: 54ch;
+
+ --font-serif: "MaruBuri", "Noto Serif KR", serif;
+ --font-display: "MaruBuri", "Noto Serif KR", serif;
+ --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
+ --font-mono: "JetBrains Mono", monospace;
+
+ /* 다크 럭셔리 색상 — 미드나이트 오닉스 + 앰버 골드 1강조색 */
+ --surface: #0b0b0d;
+ --surface-raised: #141418;
+ --surface-card: #1c1c22;
+ --surface-subtle: #24242c;
+ --surface-sunken: #070708;
+ --surface-inverse: #f4f4f0;
+
+ --ink: #f4f4f0;
+ --ink-muted: #9ca3af;
+ --ink-faint: #6b7280;
+ --ink-inverse: #0b0b0d;
+ --ink-inverse-muted: #4b5563;
+
+ --line: #2a2a34;
+ --line-strong: #3f3f4e;
+ --line-subtle: #1e1e26;
+ --line-inverse: #d1d5db;
+
+ /* 단일 강조색 — 오크 숙성 싱글 몰트 앰버 골드 */
+ --accent: #d99b38;
+ --accent-hover: #c28528;
+ --accent-light: rgba(217, 155, 56, 0.12);
+ --accent-surface: rgba(217, 155, 56, 0.2);
+ --accent-ink: #0b0b0d;
+
+ --radius: 2px;
+ --radius-sm: 4px;
+ --radius-md: 8px;
+
+ /* 간격 — SSOT 표준 */
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 1rem;
+ --space-4: 1.5rem;
+ --space-5: 2.5rem;
+ --space-6: 4rem;
+ --space-7: 6rem;
+ --space-8: clamp(4.5rem, 3rem + 6vw, 9rem);
+
+ /* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */
+ --container: 1120px;
+ --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
+
+ /* 모션 */
+ --dur-quick: 180ms;
+ --dur-normal: 420ms;
+ --ease-soft: cubic-bezier(0.25, 1, 0.35, 1);
+}
diff --git a/apps/site/public/work/cheongon-care/assets/atp-tester.webp b/apps/site/public/work/cheongon-care/assets/atp-tester.webp
new file mode 100644
index 0000000..c98966b
Binary files /dev/null and b/apps/site/public/work/cheongon-care/assets/atp-tester.webp differ
diff --git a/apps/site/public/work/cheongon-care/assets/clean-hero.webp b/apps/site/public/work/cheongon-care/assets/clean-hero.webp
new file mode 100644
index 0000000..8d8ddf6
Binary files /dev/null and b/apps/site/public/work/cheongon-care/assets/clean-hero.webp differ
diff --git a/apps/site/public/work/cheongon-care/assets/steam-qc.webp b/apps/site/public/work/cheongon-care/assets/steam-qc.webp
new file mode 100644
index 0000000..7f259ee
Binary files /dev/null and b/apps/site/public/work/cheongon-care/assets/steam-qc.webp differ
diff --git a/apps/site/public/work/cheongon-care/index.html b/apps/site/public/work/cheongon-care/index.html
new file mode 100644
index 0000000..4354ff6
--- /dev/null
+++ b/apps/site/public/work/cheongon-care/index.html
@@ -0,0 +1,608 @@
+
+
+
+
+
+ 청온 케어 (淸溫) · 프리미엄 주거·오피스 전문 위생 케어 & 관제 콘솔
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 본문으로 건너뛰기
+
+
+
+
+
+
+
+
+
+
+
+
+ HOSPITAL-GRADE HYGIENE PROTOCOL
+
+
+ 보이지 않는 미세 오염까지,
+ 공간의 숨결 을 되돌립니다
+
+
+ 단순한 미화가 아닙니다. 독일 카처 4.0bar 고온 스팀과 에코서트(ECOCERT) 인증 친환경 세제로 새집증후군, 미세 분진, 알레르기 유발 물질을 근본적으로 제거합니다.
+
+
+
+
+
+
+
+
+
+
+
+ 32 Point
+ 마스터 클리너 정밀 검수 체크리스트
+
+
+
+
+
+
+
+
+
+
투명한 정찰제 실시간 견적 산정
+
추가금 없는 명확한 기준. 공간 면적과 맞춤 옵션을 선택하시면 즉시 예상 금액이 산출됩니다.
+
+
+
+
+
+
+
+
실시간 견적 명세서
+
+ 선택 공간
+ 주거 입주/이사
+
+
+ 면적 기준
+ 32평 (평당 11,000원)
+
+
+ 기본 케어비
+ 352,000원
+
+
+ 정밀 옵션 합계
+ 80,000원
+
+
+ 총 예상 견적
+ 432,000원
+
+
+
+ 이 견적으로 신청 접수하기
+
+
+ ✓ 접수 즉시 배차 팀장이 연락 후 사전 현장 조율을 진행합니다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STEP 01
+
건식 초미세 집진
+
H14 헤파 필터 흡진기로 벽면, 천장 몰딩, 바닥의 시공 미세분진 및 부유 입자를 99.97% 1차 포집합니다.
+
+
+
+
STEP 02
+
160℃ 고온 스팀 살균
+
배수구, 욕실 타일 틈새, 주방 후드 기름때를 화학 세제 없이 4.0bar 고온 증기로 녹여 살균합니다.
+
+
+
+
STEP 03
+
약알칼리 친환경 분해
+
에코서트(ECOCERT) 인증 천연 식물성 세정제를 도포하여 자재 손상 없이 찌든 때를 안전하게 중화합니다.
+
+
+
+
STEP 04
+
서랍·후드 전면 탈거 세척
+
보이지 않는 싱크대 걸레받이, 전등갓, 수납장 서랍 레일을 전면 탈거하여 내측 분진을 완벽 제거합니다.
+
+
+
+
STEP 05
+
편백 피톤치드 연무 훈증
+
순수 제주 편백 오일 연무로 벽지 속 새집증후군 원인 물질(포름알데히드, VOCs)을 중화하고 탈취합니다.
+
+
+
+
STEP 06
+
32개 항목 마스터 검수
+
현장 팀장과 고객이 함께 32개 체크리스트를 1:1 대조하고, 미흡 지점 발생 시 즉시 재케어 후 인도합니다.
+
+
+
+
+
+
+
+
+
+
전문 위생 장비 및 검수 스펙
+
가정용 장비가 아닌 독일 카처 정품 산업용 위생 기기를 전 현장에 투입합니다.
+
+
+
+
+
+ 고온 스팀 압력
+ Kärcher SG 4/4 (4.0 bar · 160℃)
+
+
+ 미세먼지 집진 등급
+ HEPA H14 (0.3㎛ 99.995% 포집)
+
+
+ 사용 세제 인증
+ ECOCERT 100% 생분해 식물성
+
+
+ 포름알데히드 저감
+ 한국화학융합시험연구원 인증 94.2%
+
+
+
+
+
+
정밀 장비 규격
+
QC 완결 보증
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
오늘 견적 총액
+
2,480,000원
+
+
+
+
+
+
+
+
+
+
+
+ 접수번호
+ 고객명 / 연락처
+ 공간 / 면적
+ 현장 주소
+ 견적액
+ 배정 팀장
+ 상태
+ 조치
+
+
+
+
+ #CO-2026-0819
+ 이서연 010-4821-9930
+ 주거 입주 (32평)
+ 성동구 성수동 아크로포레스트
+ 432,000원
+ 김정온 마스터
+ 신규 접수
+ 배차 확정
+
+
+ #CO-2026-0818
+ 박도현 010-9120-3341
+ 오피스 정기 (45평)
+ 강남구 테헤란로 142 위워크
+ 520,000원
+ 박준호 팀장 (2팀)
+ 스팀 케어 중
+ 실시간 검수
+
+
+ #CO-2026-0817
+ 한지민 010-7712-4091
+ 하이엔드 준공 (58평)
+ 용산구 한남동 나인원한남
+ 841,000원
+ 최수진 닥터 (1팀)
+ 검수 승인 완료
+ 리포트 다운
+
+
+
+
+
+
+
+
+
+
+
+
32개 정밀 위생 검수 체크리스트
+ ✕
+
+
+
+ 닫기
+ 최종 검수 서명 승인
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/cheongon-care/protocol/index.html b/apps/site/public/work/cheongon-care/protocol/index.html
new file mode 100644
index 0000000..ae122d7
--- /dev/null
+++ b/apps/site/public/work/cheongon-care/protocol/index.html
@@ -0,0 +1,346 @@
+
+
+
+
+
+ 임상 6단계 위생 프로토콜 & 한남 케이스 리포트 — 청온 케어 (淸溫)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
HOSPITAL-GRADE SANITATION STANDARDS
+
단순한 청소를 넘어병원급 6단계 임상 위생 프로토콜
+
+ 독일 카처 산업용 스팀과 H14 헤파 필터 흡진, ATP 생체 오염도 계측을 통해 인체에 무해한 안심 공간을 과학적으로 보증합니다.
+
+
+
+
+
Steam Temp
+ 160℃ High-Temp
+
+
+
Filtration
+ 0.3㎛ HEPA H14 (99.995%)
+
+
+
Detergent
+ ECOCERT 100% 식물성
+
+
+
QC Points
+ 32개 정밀 항목
+
+
+
+
+
+
+
+ 나인원한남 86평형 · 6단계 정밀 프로토콜 시공 완료
+
+
+
+
+
+
+
+
+
+
+
+
+
SCIENTIFIC DATA VERIFICATION
+
+ 나인원한남 86평형
+ ATP 생체 오염도 99.1% 저감 리포트
+
+
+ 실제 한남동 나인원한남 입주 청소 현장에서 주방 싱크대 상판 및 욕실 수전 부위의 미생물 오염도(ATP RLU)를 측정한 결과입니다.
+ 160℃ 고온 스팀과 에코서트 효소 분해 세정 후 병원 무균실 기준(30 RLU 이하)을 상회하는 12 RLU 수준으로 완벽하게 저감되었습니다.
+
+
+
+
+
시공 전 (BEFORE)
+
1,420 RLU
+
심각한 미세 세균 번식
+
+
+
시공 후 (AFTER)
+
12 RLU
+
병원 수술실 기준 패스 (99.1% 저감)
+
+
+
+
+
+
+
+
+ STEP-BY-STEP ENGINEERING
+
+ 청온 케어 임상 6단계 시공 체계
+
+
+
+
+
STEP 01
+
+
H14 헤파 필터 건식 초미세 흡진
+
+ 벽면 실크벽지 미세 분진, 천장 우물 몰딩, 조명 레일 내부의 0.3㎛ 부유 입자를 99.995% 집진하여 공기 중 재비산을 완벽 차단합니다.
+
+
+
+
+
+
STEP 02
+
+
160℃ 4.0bar 고온 스팀 살균
+
+ 배수구 유가, 변기 틈새, 주방 후드 필터에 화학 세제 없이 4.0bar 고압 증기를 분사하여 기름때를 녹이고 99.9% 멸균합니다.
+
+
+
+
+
+
STEP 03
+
+
약알칼리 에코서트 친환경 효소 분해
+
+ 원목 마루 및 천연 대리석 자재에 손상을 주지 않는 100% 생분해성 식물 세정제를 도포하여 찌든 산화 피막을 안전하게 중화합니다.
+
+
+
+
+
+
STEP 04
+
+
서랍장·걸레받이 전면 탈거 정밀 세척
+
+ 싱크대 하부 걸레받이와 수납장 레일을 전량 분리 탈거하여, 보이지 않는 시공 톱밥 및 유해 분진을 내측까지 완전 제거합니다.
+
+
+
+
+
+
STEP 05
+
+
제주 편백 피톤치드 연무 훈증
+
+ 새집증후군 유발 물질인 포름알데히드 및 휘발성유기화합물(VOCs)을 중화하고 실내 숲속 공기질을 복원합니다.
+
+
+
+
+
+
STEP 06
+
+
32개 항목 마스터 닥터 1:1 검수 및 디지털 인증
+
+ 배차 현장 팀장과 고객이 함께 32개 정밀 체크리스트를 1:1 대조하고, 모바일 리포트를 발행하여 최종 승인합니다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TRANSPARENT PRICING
+
+ 우리 집 공간의 정찰제 견적을 확인하세요
+
+
+ 추가금 없는 평당 11,000원 명확한 기준. 1분 만에 실시간 견적 산출이 가능합니다.
+
+
+ 실시간 정찰제 견적 계산하기 →
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/cheongon-care/styles/main.css b/apps/site/public/work/cheongon-care/styles/main.css
new file mode 100644
index 0000000..1bf5bd4
--- /dev/null
+++ b/apps/site/public/work/cheongon-care/styles/main.css
@@ -0,0 +1,926 @@
+@import "./tokens.css";
+
+/* ==========================================================================
+ 청온 케어 (CHEONG-ON CARE · 淸溫) — 메인 스타일시트 SSOT
+ 프리셋: clinical-sanitation + transparent-calculator + high-end-visual-design
+ ========================================================================== */
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ scroll-behavior: smooth;
+ background-color: var(--surface);
+ color: var(--ink);
+ font-family: var(--font-body);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ overflow-x: hidden;
+}
+
+body {
+ min-height: 100dvh;
+ line-height: var(--leading-normal);
+ overflow-x: hidden;
+ background: var(--surface);
+ color: var(--ink);
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+ transition: color var(--dur-fast);
+}
+
+a:hover {
+ color: var(--accent);
+}
+
+button, input, select, textarea {
+ font: inherit;
+ color: inherit;
+}
+
+button {
+ cursor: pointer;
+ border: none;
+ background: none;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+.mono {
+ font-family: var(--font-mono);
+}
+
+.wrap {
+ width: min(100% - 2 * var(--pad-inline), var(--container));
+ margin-inline: auto;
+}
+
+/* ── 접근성 스킵 링크 ── */
+.skip-link {
+ position: fixed;
+ top: 1rem;
+ left: 1rem;
+ z-index: 1000;
+ padding: 0.5rem 1rem;
+ background: var(--surface-inverse);
+ color: var(--ink-inverse);
+ border-radius: var(--radius-sm);
+ transform: translateY(-200%);
+ transition: transform var(--dur-fast);
+ text-decoration: none;
+ font-weight: 500;
+}
+.skip-link:focus {
+ transform: translateY(0);
+}
+
+/* ── 헤더 & 뷰 모드 스위처 (GNB SSOT) ── */
+.site-head {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: rgba(248, 250, 249, 0.94);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--line);
+ padding-block: var(--space-3);
+}
+
+.head-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-3);
+ min-width: 0;
+}
+
+.wordmark {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: var(--step-1);
+ font-weight: 800;
+ color: var(--ink);
+ text-decoration: none;
+ letter-spacing: -0.02em;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.wordmark-badge {
+ background: var(--accent);
+ color: #fff;
+ font-size: 11px;
+ font-weight: 700;
+ padding: 2px 6px;
+ border-radius: var(--radius-xs);
+}
+
+.wordmark-han {
+ font-size: var(--step--1);
+ color: var(--accent);
+ font-weight: 600;
+ letter-spacing: 0.05em;
+}
+
+.nav {
+ display: flex;
+ align-items: center;
+ gap: var(--space-5);
+ list-style: none;
+}
+
+.nav a {
+ text-decoration: none;
+ color: var(--ink-muted);
+ font-size: var(--step--1);
+ font-weight: 500;
+ transition: color var(--dur-fast);
+ white-space: nowrap;
+}
+
+.nav a:hover,
+.nav a.active {
+ color: var(--ink);
+}
+
+.head-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ flex-shrink: 0;
+ min-width: 0;
+}
+
+.mode-toggle {
+ display: inline-flex;
+ background: var(--surface-subtle);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: 2px;
+ gap: 2px;
+}
+
+.mode-btn {
+ padding: 4px 10px;
+ border-radius: calc(var(--radius-sm) - 2px);
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--ink-muted);
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ transition: all var(--dur-fast);
+ white-space: nowrap;
+}
+
+.mode-btn.is-active {
+ background: var(--surface-raised);
+ color: var(--ink);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+}
+
+.mode-btn .badge-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #10b981;
+}
+
+.head-actions .btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--accent);
+ color: #fff;
+ font-weight: 700;
+ font-size: var(--step--1);
+ padding: 6px 14px;
+ border-radius: var(--radius-xs);
+ transition: opacity var(--dur-fast);
+}
+
+.head-actions .btn:hover {
+ opacity: 0.9;
+}
+
+/* ── 버튼 시스템 & 뱃지 ── */
+.btn-primary {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ background: var(--accent);
+ color: #ffffff;
+ padding: 10px 20px;
+ border-radius: var(--radius-sm);
+ font-weight: 700;
+ font-size: var(--step--1);
+ text-decoration: none;
+ white-space: nowrap;
+ transition: transform var(--dur-fast), background var(--dur-fast);
+ border: none;
+}
+
+.btn-primary:hover {
+ background: var(--accent-hover);
+ color: #ffffff;
+ transform: translateY(-1px);
+}
+
+.btn-outline {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ background: transparent;
+ color: var(--ink);
+ border: 1px solid var(--line);
+ padding: 10px 18px;
+ border-radius: var(--radius-sm);
+ font-weight: 600;
+ font-size: var(--step--1);
+ text-decoration: none;
+ white-space: nowrap;
+ transition: border-color var(--dur-fast), color var(--dur-fast);
+}
+
+.btn-outline:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+.eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 3px 10px;
+ border-radius: var(--radius-xs);
+ background: var(--accent-light);
+ color: var(--accent);
+ font-size: 11px;
+ font-family: var(--font-mono);
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ margin-bottom: var(--space-3);
+ text-transform: uppercase;
+}
+
+/* ── 히어로 섹션 ── */
+.hero-section {
+ padding-block: var(--space-7);
+ position: relative;
+}
+
+.hero-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.95fr);
+ gap: var(--space-7);
+ align-items: center;
+}
+
+.hero-title {
+ font-size: clamp(2.4rem, 1.8rem + 2.2vw, 3.8rem);
+ font-weight: 800;
+ line-height: 1.18;
+ letter-spacing: -0.035em;
+ color: var(--ink);
+ margin-bottom: var(--space-4);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.hero-title em {
+ font-style: normal;
+ color: var(--accent);
+}
+
+.hero-desc {
+ font-size: clamp(1.05rem, 0.98rem + 0.35vw, 1.2rem);
+ color: var(--ink-muted);
+ max-width: 48ch;
+ line-height: 1.75;
+ margin-bottom: var(--space-5);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.hero-ctas {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ flex-wrap: wrap;
+}
+
+.hero-trust {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+}
+
+.hero-trust-stars {
+ color: #f59e0b;
+ font-size: 13px;
+}
+
+.hero-visual {
+ position: relative;
+}
+
+.bezel-outer {
+ background: rgba(15, 118, 110, 0.05);
+ border: 1px solid var(--line);
+ padding: 6px;
+ border-radius: var(--radius-md);
+}
+
+.hero-frame {
+ border-radius: calc(var(--radius-md) - 4px);
+ overflow: hidden;
+ border: 1px solid var(--line);
+ box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.08);
+ background: #ffffff;
+}
+
+.hero-frame img {
+ width: 100%;
+ aspect-ratio: 4/3;
+ object-fit: cover;
+ display: block;
+}
+
+.hero-stat-floating {
+ position: absolute;
+ bottom: -14px;
+ left: 16px;
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: 8px 14px;
+ box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.08);
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-2);
+}
+
+/* ── 투명 정찰제 실시간 견적 산정 섹션 ── */
+.calculator-section {
+ padding-block: var(--space-8);
+ border-top: 1px solid var(--line);
+}
+
+.section-head {
+ text-align: center;
+ max-width: 680px;
+ margin-inline: auto;
+ margin-bottom: var(--space-6);
+}
+
+.section-title {
+ font-size: clamp(1.8rem, 1.4rem + 1.2vw, 2.25rem);
+ font-weight: 800;
+ letter-spacing: -0.025em;
+ color: var(--ink);
+ margin-bottom: var(--space-2);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.section-sub {
+ font-size: var(--step-0);
+ color: var(--ink-muted);
+ line-height: 1.6;
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.calc-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
+ gap: var(--space-5);
+ align-items: start;
+ min-width: 0;
+}
+
+.calc-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-md);
+ padding: var(--space-5);
+ min-width: 0;
+}
+
+.form-group {
+ margin-bottom: var(--space-4);
+}
+
+.form-label {
+ display: block;
+ font-size: var(--step--1);
+ font-weight: 700;
+ color: var(--ink);
+ margin-bottom: var(--space-2);
+ letter-spacing: -0.01em;
+}
+
+.type-selector {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: var(--space-2);
+ min-width: 0;
+}
+
+.type-btn {
+ padding: 10px 8px;
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ background: var(--surface);
+ color: var(--ink-muted);
+ font-weight: 600;
+ font-size: var(--step--1);
+ text-align: center;
+ transition: all var(--dur-fast);
+ white-space: normal;
+ word-break: keep-all;
+}
+
+.type-btn.is-active {
+ border-color: var(--accent);
+ background: var(--accent-light);
+ color: var(--accent);
+}
+
+.slider-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ margin-bottom: var(--space-2);
+}
+
+.slider-value {
+ font-family: var(--font-mono);
+ font-size: var(--step-1);
+ font-weight: 700;
+ color: var(--accent);
+}
+
+.range-slider {
+ width: 100%;
+ accent-color: var(--accent);
+ height: 6px;
+ cursor: pointer;
+}
+
+.addon-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--space-2);
+ min-width: 0;
+}
+
+.addon-label {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: 10px 12px;
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ background: var(--surface);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+ cursor: pointer;
+ transition: all var(--dur-fast);
+}
+
+.addon-label:has(input:checked) {
+ border-color: var(--accent);
+ background: var(--accent-light);
+ color: var(--ink);
+ font-weight: 600;
+}
+
+/* 실시간 견적 영수증 요약 패널 */
+.calc-summary {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-md);
+ padding: var(--space-5);
+ min-width: 0;
+}
+
+.summary-title {
+ font-size: var(--step-1);
+ font-weight: 700;
+ margin-bottom: var(--space-4);
+ padding-bottom: var(--space-2);
+ border-bottom: 1px solid var(--line);
+}
+
+.summary-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: var(--space-2);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+}
+
+.summary-row .mono {
+ color: var(--ink);
+ font-weight: 600;
+}
+
+.summary-total {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ padding-top: var(--space-3);
+ border-top: 1px solid var(--line);
+ margin-block: var(--space-4) var(--space-5);
+}
+
+.summary-total span:first-child {
+ font-size: var(--step-0);
+ font-weight: 700;
+}
+
+.summary-total .total-amount {
+ font-family: var(--font-mono);
+ font-size: var(--step-2);
+ font-weight: 800;
+ color: var(--accent);
+}
+
+.calc-submit {
+ width: 100%;
+ padding: 14px;
+ background: var(--accent);
+ color: #fff;
+ font-weight: 700;
+ font-size: var(--step-0);
+ border-radius: var(--radius-sm);
+ transition: background var(--dur-fast);
+}
+
+.calc-submit:hover {
+ background: var(--accent-hover);
+}
+
+/* ── 6단계 위생 프로토콜 섹션 ── */
+.protocol-section {
+ padding-block: var(--space-8);
+ border-top: 1px solid var(--line);
+}
+
+.protocol-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: var(--space-4);
+}
+
+.protocol-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-5);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.protocol-num {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: 700;
+ color: var(--accent);
+ letter-spacing: 0.06em;
+}
+
+.protocol-card h3 {
+ font-size: var(--step-0);
+ font-weight: 700;
+ color: var(--ink);
+}
+
+.protocol-card p {
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+ line-height: 1.6;
+}
+
+/* ── 전문 장비 & Before/After 섹션 ── */
+.equipment-section {
+ padding-block: var(--space-8);
+ border-top: 1px solid var(--line);
+ background: var(--surface-card);
+}
+
+.equip-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ gap: var(--space-6);
+ align-items: center;
+}
+
+.equip-specs {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.spec-item {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: 12px 16px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.spec-key {
+ font-size: var(--step--1);
+ font-weight: 600;
+ color: var(--ink-muted);
+}
+
+.spec-val {
+ font-family: var(--font-mono);
+ font-size: var(--step--1);
+ font-weight: 700;
+ color: var(--accent);
+}
+
+.ba-container {
+ position: relative;
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ border: 1px solid var(--line);
+ box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.08);
+}
+
+.ba-container img {
+ width: 100%;
+ aspect-ratio: 4/3;
+ object-fit: cover;
+ display: block;
+}
+
+.ba-badge {
+ position: absolute;
+ bottom: 12px;
+ padding: 4px 10px;
+ border-radius: var(--radius-xs);
+ font-size: 11px;
+ font-family: var(--font-mono);
+ font-weight: 700;
+}
+
+.ba-badge.before {
+ left: 12px;
+ background: rgba(15, 23, 42, 0.85);
+ color: #fff;
+}
+
+.ba-badge.after {
+ right: 12px;
+ background: var(--accent);
+ color: #fff;
+}
+
+/* ===================================================================
+ 2. 관리자 관제 콘솔 뷰 (Admin Console View)
+ =================================================================== */
+.admin-view {
+ display: none;
+ padding-block: var(--space-6) var(--space-8);
+}
+
+[data-view="admin"] .client-view { display: none; }
+[data-view="admin"] .admin-view { display: block; }
+
+.admin-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ margin-bottom: var(--space-5);
+ flex-wrap: wrap;
+ gap: var(--space-3);
+}
+
+.admin-kpis {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: var(--space-3);
+ margin-bottom: var(--space-5);
+}
+
+.kpi-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-4);
+}
+
+.kpi-label {
+ font-size: 12px;
+ color: var(--ink-muted);
+ font-weight: 600;
+}
+
+.kpi-val {
+ font-family: var(--font-mono);
+ font-size: var(--step-2);
+ font-weight: 800;
+ color: var(--ink);
+ margin-top: 4px;
+}
+
+.kpi-val.accent {
+ color: var(--accent);
+}
+
+.admin-table-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ overflow: hidden;
+}
+
+.table-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--line);
+ background: var(--surface-card);
+}
+
+.table-title {
+ font-size: var(--step--1);
+ font-weight: 700;
+ color: var(--ink);
+}
+
+.table-filter-tabs {
+ display: flex;
+ gap: 4px;
+}
+
+.filter-tab {
+ padding: 3px 8px;
+ border-radius: var(--radius-xs);
+ font-size: 11px;
+ font-weight: 600;
+ background: var(--surface);
+ border: 1px solid var(--line);
+ color: var(--ink-muted);
+}
+
+.filter-tab.is-active {
+ background: var(--accent);
+ color: #fff;
+ border-color: var(--accent);
+}
+
+.admin-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 13px;
+ text-align: left;
+ white-space: nowrap;
+}
+
+.admin-table th {
+ background: var(--surface-card);
+ padding: 10px 14px;
+ font-weight: 600;
+ color: var(--ink-muted);
+ border-bottom: 1px solid var(--line);
+ font-family: var(--font-mono);
+ font-size: 12px;
+}
+
+.admin-table td {
+ padding: 12px 14px;
+ border-bottom: 1px solid var(--line);
+}
+
+.status-pill {
+ display: inline-flex;
+ align-items: center;
+ padding: 2px 8px;
+ border-radius: var(--radius-xs);
+ font-size: 11px;
+ font-weight: 700;
+}
+
+.status-pill.new { background: #fef3c7; color: #92400e; }
+.status-pill.progress { background: #ccfbf1; color: #0f766e; }
+.status-pill.done { background: #dbeafe; color: #1e40af; }
+
+.action-btn {
+ padding: 4px 10px;
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ font-size: 12px;
+ font-weight: 600;
+ background: var(--surface);
+ cursor: pointer;
+ transition: all var(--dur-fast);
+}
+
+.action-btn:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+/* ── 32개 QC 모달 다이얼로그 ── */
+.qc-dialog {
+ position: fixed;
+ inset: 0;
+ margin: auto;
+ width: min(90vw, 560px);
+ background: #ffffff;
+ border: 1px solid var(--line);
+ border-radius: var(--radius-md);
+ padding: var(--space-5);
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.15);
+ outline: none;
+}
+
+.qc-dialog::backdrop {
+ background: rgba(0, 0, 0, 0.5);
+ backdrop-filter: blur(4px);
+}
+
+/* ── 푸터 (SSOT 정본) ── */
+.site-foot {
+ border-top: 1px solid var(--line);
+ padding-block: var(--space-6);
+ background: var(--surface);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+}
+
+.foot-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: var(--space-4);
+}
+
+/* ── 반응형 미디어 쿼리 ── */
+@media (max-width: 860px) {
+ .nav {
+ display: none;
+ }
+ .hero-grid, .calc-grid, .equip-grid, .protocol-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-5);
+ }
+ .admin-kpis {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .type-selector, .addon-grid {
+ grid-template-columns: 1fr;
+ }
+ .admin-kpis {
+ grid-template-columns: 1fr;
+ }
+ .wordmark-han {
+ display: none;
+ }
+ .mode-btn {
+ padding: 3px 6px;
+ font-size: 11px;
+ }
+}
diff --git a/apps/site/public/work/cheongon-care/styles/tokens.css b/apps/site/public/work/cheongon-care/styles/tokens.css
new file mode 100644
index 0000000..a9a83cd
--- /dev/null
+++ b/apps/site/public/work/cheongon-care/styles/tokens.css
@@ -0,0 +1,73 @@
+/* ==========================================================================
+ 청온 케어 (淸溫 · CHEONG-ON CARE) — 디자인 토큰 SSOT
+ 프리셋: ledger-console + swiss-minimal
+ 컨셉: 임상 수준의 위생 프로토콜과 정직한 정찰제 — 공간의 숨결을 되돌리는 케어
+ 단일 강조색: 에메랄드 틸 (#0D9488)
+ ========================================================================== */
+
+:root {
+ /* 타이포그래피 스케일 */
+ --step--1: 0.8125rem; /* 13px — 캡션·태그 */
+ --step-0: 1rem; /* 16px — 본문 */
+ --step-1: 1.1875rem; /* 19px — 소제목 */
+ --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem); /* 30px — 섹션 헤딩 */
+ --step-3: clamp(2.25rem, 1.5rem + 3vw, 3.8rem); /* 60px — 메인 디스플레이 */
+
+ --leading-tight: 1.2;
+ --leading-normal: 1.7;
+ --measure: 58ch;
+
+ --font-serif: "MaruBuri", "Noto Serif KR", serif;
+ --font-display: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
+ --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
+ --font-mono: "JetBrains Mono", monospace;
+
+ /* 색상 — 클린 퓨어 화이트 & 딥 슬레이트 + 에메랄드 틸 1강조색 */
+ --surface: #f8faf9;
+ --surface-raised: #ffffff;
+ --surface-card: #f0f4f2;
+ --surface-subtle: #e6ede9;
+ --surface-sunken: #e2e9e5;
+ --surface-inverse: #0f172a;
+
+ --ink: #0f172a;
+ --ink-muted: #475569;
+ --ink-faint: #94a3b8;
+ --ink-inverse: #f8faf9;
+ --ink-inverse-muted: #cbd5e1;
+
+ --line: #e2e8e5;
+ --line-strong: #cbd5e1;
+ --line-subtle: #edf2ef;
+ --line-inverse: #334155;
+
+ /* 단일 강조색 (에메랄드 틸) */
+ --accent: #0d9488;
+ --accent-hover: #0f766e;
+ --accent-light: #f0fdfa;
+ --accent-surface: #ccfbf1;
+ --accent-ink: #ffffff;
+
+ --radius: 2px;
+ --radius-sm: 4px;
+ --radius-md: 8px;
+
+ /* 간격 — SSOT 표준 */
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 1rem;
+ --space-4: 1.5rem;
+ --space-5: 2.5rem;
+ --space-6: 4rem;
+ --space-7: 6rem;
+ --space-8: clamp(4.5rem, 3rem + 6vw, 9rem);
+
+ /* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */
+ --container: 1120px;
+ --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
+
+ /* 모션 */
+ --dur-quick: 180ms;
+ --dur-normal: 420ms;
+ --ease-soft: cubic-bezier(0.25, 1, 0.35, 1);
+}
diff --git a/apps/site/public/work/materia-interior/assets/hannam-01-living-void.webp b/apps/site/public/work/materia-interior/assets/hannam-01-living-void.webp
new file mode 100644
index 0000000..738fd07
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-01-living-void.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-02-blueprint.webp b/apps/site/public/work/materia-interior/assets/hannam-02-blueprint.webp
new file mode 100644
index 0000000..9fa2298
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-02-blueprint.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-03-dining.webp b/apps/site/public/work/materia-interior/assets/hannam-03-dining.webp
new file mode 100644
index 0000000..29d398c
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-03-dining.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-04-kitchen.webp b/apps/site/public/work/materia-interior/assets/hannam-04-kitchen.webp
new file mode 100644
index 0000000..ae40b23
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-04-kitchen.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-05-master-bedroom.webp b/apps/site/public/work/materia-interior/assets/hannam-05-master-bedroom.webp
new file mode 100644
index 0000000..54a621d
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-05-master-bedroom.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-06-spa-bathroom.webp b/apps/site/public/work/materia-interior/assets/hannam-06-spa-bathroom.webp
new file mode 100644
index 0000000..2f06219
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-06-spa-bathroom.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-07-library-audio.webp b/apps/site/public/work/materia-interior/assets/hannam-07-library-audio.webp
new file mode 100644
index 0000000..0581d31
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-07-library-audio.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-08-terrace.webp b/apps/site/public/work/materia-interior/assets/hannam-08-terrace.webp
new file mode 100644
index 0000000..1d6eace
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-08-terrace.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-09-foyer.webp b/apps/site/public/work/materia-interior/assets/hannam-09-foyer.webp
new file mode 100644
index 0000000..7eea160
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-09-foyer.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-10-staircase.webp b/apps/site/public/work/materia-interior/assets/hannam-10-staircase.webp
new file mode 100644
index 0000000..990a9c5
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-10-staircase.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-11-dressing-room.webp b/apps/site/public/work/materia-interior/assets/hannam-11-dressing-room.webp
new file mode 100644
index 0000000..b83dc0d
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-11-dressing-room.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-12-wine-cellar.webp b/apps/site/public/work/materia-interior/assets/hannam-12-wine-cellar.webp
new file mode 100644
index 0000000..c58197e
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-12-wine-cellar.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-13-powder-room.webp b/apps/site/public/work/materia-interior/assets/hannam-13-powder-room.webp
new file mode 100644
index 0000000..f3f4adf
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-13-powder-room.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-14-guest-suite.webp b/apps/site/public/work/materia-interior/assets/hannam-14-guest-suite.webp
new file mode 100644
index 0000000..5a19672
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-14-guest-suite.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-15-mat-travertine.webp b/apps/site/public/work/materia-interior/assets/hannam-15-mat-travertine.webp
new file mode 100644
index 0000000..e1a4b4e
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-15-mat-travertine.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-16-mat-bogoak.webp b/apps/site/public/work/materia-interior/assets/hannam-16-mat-bogoak.webp
new file mode 100644
index 0000000..605dfbb
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-16-mat-bogoak.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-17-mat-linen.webp b/apps/site/public/work/materia-interior/assets/hannam-17-mat-linen.webp
new file mode 100644
index 0000000..9b96783
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-17-mat-linen.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-18-mat-bronze.webp b/apps/site/public/work/materia-interior/assets/hannam-18-mat-bronze.webp
new file mode 100644
index 0000000..01078a3
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-18-mat-bronze.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-19-joinery.webp b/apps/site/public/work/materia-interior/assets/hannam-19-joinery.webp
new file mode 100644
index 0000000..ab91063
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-19-joinery.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-20-tea-room.webp b/apps/site/public/work/materia-interior/assets/hannam-20-tea-room.webp
new file mode 100644
index 0000000..a9c0d77
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-20-tea-room.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-21-art-niche.webp b/apps/site/public/work/materia-interior/assets/hannam-21-art-niche.webp
new file mode 100644
index 0000000..ac22be4
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-21-art-niche.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-22-lightwell.webp b/apps/site/public/work/materia-interior/assets/hannam-22-lightwell.webp
new file mode 100644
index 0000000..98e596a
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-22-lightwell.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-23-light-simulation.webp b/apps/site/public/work/materia-interior/assets/hannam-23-light-simulation.webp
new file mode 100644
index 0000000..b4e8259
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-23-light-simulation.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/hannam-24-timber-joinery.webp b/apps/site/public/work/materia-interior/assets/hannam-24-timber-joinery.webp
new file mode 100644
index 0000000..ca0579e
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/hannam-24-timber-joinery.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/materials-macro.webp b/apps/site/public/work/materia-interior/assets/materials-macro.webp
new file mode 100644
index 0000000..1e98473
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/materials-macro.webp differ
diff --git a/apps/site/public/work/materia-interior/assets/penthouse-hero.webp b/apps/site/public/work/materia-interior/assets/penthouse-hero.webp
new file mode 100644
index 0000000..83248df
Binary files /dev/null and b/apps/site/public/work/materia-interior/assets/penthouse-hero.webp differ
diff --git a/apps/site/public/work/materia-interior/hannam-duplex.html b/apps/site/public/work/materia-interior/hannam-duplex.html
new file mode 100644
index 0000000..55cdb8c
--- /dev/null
+++ b/apps/site/public/work/materia-interior/hannam-duplex.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ 한남 듀플렉스 펜트하우스 이동 중...
+
+
+
+ 한남 듀플렉스 펜트하우스 갤러리로 이동 중입니다... 직접 이동하기
+
+
diff --git a/apps/site/public/work/materia-interior/index.html b/apps/site/public/work/materia-interior/index.html
new file mode 100644
index 0000000..1e4af47
--- /dev/null
+++ b/apps/site/public/work/materia-interior/index.html
@@ -0,0 +1,430 @@
+
+
+
+
+
+ 마테리아 스튜디오 (MATERIA) · 하이엔드 주거 & 상업 공간 인테리어 건축
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 본문으로 건너뛰기
+
+
+
+
+
+
+
+
+
+
+ HIGH-END ARCHITECTURAL INTERIOR
+
+
+ 빛과 트래버틴이 빚어내는
+ 침묵의 공간 건축
+
+
+ 우리는 장식을 덧대지 않습니다. 돌의 거친 결, 훈증 오크의 온기, 리넨을 투과하는 자연광의 궤적을 치밀하게 계산하여 시간이 흘러도 변치 않는 하이엔드 주거와 갤러리 공간을 완성합니다.
+
+
+
+
+
+
+
+
+
+
+
+
한남 펜트하우스 레지던스
+
Hannam · 284㎡ (86평) · 2026.04 준공
+
+
+
+
+
+
+
+
+
+
엄선된 하이엔드 공간 아카이브
+
주거 펜트하우스부터 프라이빗 갤러리까지, 공간의 고유한 결을 살린 마테리아의 대표 프로젝트입니다.
+
+
+
+
+
+
+
+
한남 듀플렉스 펜트하우스
+
Residential · 284㎡ · 로만 트래버틴 & 훈증 오크
+
+ 층고 5.8m의 복층 구조에 거친 트래버틴 석재 벽체와 다크 훈증 오크를 대비시켜 웅장하면서도 고요한 빛의 음영을 연출했습니다.
+
+
+
+
+
+
+
+
+
+
청담 프라이빗 갤러리
+
Commercial / Gallery · 195㎡ · 마이크로토핑 & 브론즈
+
+ 작품이 온전히 숨 쉴 수 있도록 모든 몰딩을 숨긴 무문선·무걸레받이 디테일과 브러시드 브론즈 프레임으로 정갈함을 극대화했습니다.
+
+
+
+ 설계 명세 보기 →
+
+
+
+
+
+
+
+
+
+
성북동 포레스트 레지던스
+
Private Residence · 340㎡ · 자연석 화강석 & 벨기에 리넨
+
+ 북악산 사계절의 숲 풍경을 차경(借景)으로 끌어들이고, 실내에는 자연석 아일랜드와 수공예 패브릭을 배치한 친환경 럭셔리 주거.
+
+
+
+ 설계 명세 보기 →
+
+
+
+
+
+
+
+
+
+
+
+
+
자연이 빚은 물성과 촉각의 조화
+
마테리아가 선별한 최상급 건축 마감재는 인위적인 가공을 최소화하여 시간의 깊이를 머금습니다.
+
+
+
+
+
+
+
+
+
+ 로만 트래버틴
+ 이탈리아 티볼리 채석장의 천연 석회암. 무수한 다공성 질감이 빛을 부드럽게 난반사합니다.
+
+
+ 훈증 오크 (Bog Oak)
+ 수백 년간 수중 숙성된 천연 고재의 깊은 탄화 흑색과 치밀한 나이테 결을 그대로 보존합니다.
+
+
+ 벨기에 천연 리넨
+ 플랑드르 지방의 무표백 아마 섬유로 짠 고밀도 원단. 부드럽게 빛을 거르고 통기성을 제공합니다.
+
+
+ 파티나 브론즈
+ 손길이 닿을수록 자연스러운 에이징 산화 피막이 형성되는 수공예 황동 하드웨어입니다.
+
+
+
+
+
+
+
+
+
+
+
비스포크 공간 건축 4단계 프로세스
+
기획부터 완공 인도까지, 총괄 디렉터 1인이 모든 자재와 조도를 책임지고 총괄합니다.
+
+
+
+
+
PHASE 01
+
일조 및 공간 진단
+
현장의 24시간 태양 궤적, 조망 축, 구조 하중을 분석하여 채광의 최적 배치를 기획합니다.
+
+
+
PHASE 02
+
촉각적 머티리얼 페어링
+
실제 1:1 자재 샘플 보드를 제작하여 거주자의 라이프스타일과 촉감 선호도를 일치시킵니다.
+
+
+
PHASE 03
+
3D 광학 시뮬레이션
+
주광(Daylight)과 2700K 인공 조명의 조도 분포를 가상 렌더링으로 정밀 검증합니다.
+
+
+
PHASE 04
+
장인 직영 디테일 시공
+
30년 경력의 석재·목공 마스터 장인팀이 밀리미터 단위의 오차 없는 시공을 완성합니다.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 프로젝트 설계 아카이브
+
+ ✕
+
+
+
프로젝트명:
+
공간 면적:
+
주요 마감재:
+
+ 설계 의도:
+
+
+
+
+ 닫기
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/materia-interior/projects/hannam-duplex/index.html b/apps/site/public/work/materia-interior/projects/hannam-duplex/index.html
new file mode 100644
index 0000000..5e0d195
--- /dev/null
+++ b/apps/site/public/work/materia-interior/projects/hannam-duplex/index.html
@@ -0,0 +1,952 @@
+
+
+
+
+
+ 한남 듀플렉스 펜트하우스 (86평) — MATERIA Project Archive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CASE STUDY · RESIDENTIAL ARCHITECTURE · 86PY
+
한남 듀플렉스 펜트하우스
+
+ 5.8m 보이드 천장고와 로만 트래버틴, 3,000년 훈증 오크가 빚어낸 침묵의 공간.
+
+
+
+
+
위치
+ 서울시 용산구 한남동
+
+
+
면적
+ 284㎡ (86평형 복층)
+
+
+
천장고
+ 5.8m Void Ceiling
+
+
+
공사기간
+ 14주 (2026.04 준공)
+
+
+
핵심 소재
+ Roman Navona Travertine
+
+
+
목공 마감
+ 3,000Y Smoked Bog Oak
+
+
+
+
+
+
+
+
+ Zone A · 5.8m 보이드 거실 & 20mm 로만 나보나 트래버틴 아트월
+
+
+
+
+
+
+
+
+
+
+
DESIGN PHILOSOPHY
+
+ 비움으로 완성한 빛과 물성의 교감
+
+
+
+
+ 한남 듀플렉스 펜트하우스는 장식적 기교를 배제하고, 자연광의 궤적과 거대한 석재의 물성이 공간의 주인이 되도록 설계된 프로젝트입니다.
+ 남향으로 열린 한강 조망과 5.8미터 복층 보이드 구조를 관통하는 자연광을 수학적으로 계산하여, 시간에 따라 석재 표면의 음영이 시시각각 변화하도록 유도했습니다.
+
+
+ 벽체를 감싸는 20mm 로만 나보나 트래버틴은 이탈리아 티볼리 채석장에서 원판을 엄선하여 이음매의 결을 무지향성으로 연결했으며,
+ 3,000년 세월 동안 탄화된 훈증 오크(Bog Oak)와 수공예 파티나 브론즈를 접합하여 시각적 깊이와 촉각적 정숙함을 부여했습니다.
+
+
+
+
+
+
+
+
+
+
+
SPATIAL EXPLORATION · 24 SHOTS
+
공간별 심층 아카이브 갤러리
+
+ 각 카드를 클릭하시면 고해상도 확대 뷰어와 설계 물성 상세 명세를 확인하실 수 있습니다.
+
+
+
+
+
+ 전체 공간 (24)
+ Zone A: 거실 & 다이닝 (4)
+ Zone B: 마스터 스위트 & 스파 (4)
+ Zone C: 서재 & 와인 & 드레스룸 (4)
+ Zone D: 테라스 & 중정 & 현관 (4)
+ Zone E: 물성 매크로 접사 (4)
+ Zone F: 도면 & 광학 분석 & 디테일 (4)
+
+
+
+
+
+
+
+
+
+
+
Zone A · Living
+
5.8m 보이드 그레이트 룸
+
로만 트래버틴 일체형 벽난로와 2개 층을 관통하는 통창 채광 설계.
+
+
+
+
+
+
+
+
+
+
Zone A · Dining
+
월넛 원목 슬랩 다이닝
+
수제 브러시드 브론즈 펜던트 조명과 한강 파노라마 야경 조망.
+
+
+
+
+
+
+
+
+
+
Zone A · Kitchen
+
석재 통판 셰프 키친
+
그레이 쿼츠자이트 아일랜드와 히든 플러시 훈증 오크 수납 시스템.
+
+
+
+
+
+
+
+
+
+
Zone A · Staircase
+
캔틸레버 부유식 오크 계단
+
트래버틴 벽체에서 돌출된 무지주 솔리드 오크 디딤판과 매립 핸드레일.
+
+
+
+
+
+
+
+
+
+
Zone B · Master Suite
+
마스터 베드룸 스위트
+
천연 리넨 헤드보드 월과 간접 라인 조명으로 완성한 정숙한 침실.
+
+
+
+
+
+
+
+
+
+
Zone B · Spa Bath
+
화산석 통원석 스파 배스
+
자연석을 통째로 깎아낸 원석 욕조와 독립형 파티나 브론즈 수전.
+
+
+
+
+
+
+
+
+
+
Zone B · Powder Room
+
석재 세면대 파우더룸
+
조각적인 통라임스톤 세면대와 수직 슬릿 조명이 빚은 명암.
+
+
+
+
+
+
+
+
+
+
Zone B · Guest Suite
+
게스트 침실 스위트
+
따뜻한 라임워시 도장 벽체와 천연 원목 가구의 안락한 조화.
+
+
+
+
+
+
+
+
+
+
Zone C · Library
+
서재 & 하이파이 오디오 룸
+
음향 반사를 고려한 훈증 오크 책장과 아날로그 턴테이블 석재 콘솔.
+
+
+
+
+
+
+
+
+
+
Zone C · Dressing Room
+
비스포크 워크인 드레스룸
+
플루티드 브론즈 유리장과 시계/주얼리 수납 아일랜드 쇼케이스.
+
+
+
+
+
+
+
+
+
+
Zone C · Wine Cellar
+
항온항습 글래스 와인 셀러
+
백라이트 트래버틴 벽체와 브론즈 프레임 유리 파티션 셀러.
+
+
+
+
+
+
+
+
+
+
Zone C · Tea Alcove
+
명상 다도실 (Tea Room)
+
천연 한지 스크린을 투과하는 부드러운 격자 채광과 원목 다도상.
+
+
+
+
+
+
+
+
+
+
Zone D · Terrace
+
루프탑 프라이빗 테라스
+
대형 트래버틴 페이버와 조형 소나무 분재, 한강 석양 조망.
+
+
+
+
+
+
+
+
+
+
Zone D · Entrance
+
미니멀 입구 현관 (Foyer)
+
거대한 화강석 벤치 원석과 플러시 히든 피벗 도어 시스템.
+
+
+
+
+
+
+
+
+
+
Zone D · Lightwell
+
실내 석경 중정 (Zen Lightwell)
+
천장 보이드에서 쏟아지는 수직 채광과 백자갈 디딤돌 정원.
+
+
+
+
+
+
+
+
+
+
Zone D · Art Niche
+
뮤지엄 갤러리 아트 니치
+
트래버틴 조각 좌대와 정밀 프레이밍 조명으로 연출된 조각품.
+
+
+
+
+
+
+
+
+
+
Zone E · Material
+
로만 나보나 트래버틴 접사
+
자연이 형성한 미세한 다공성 기공과 따뜻한 꿀빛 석재 층위.
+
+
+
+
+
+
+
+
+
+
Zone E · Material
+
3,000년 탄화 훈증 오크
+
시간이 빚어낸 깊은 차콜 에스프레소 나뭇결과 실키 매트 피니시.
+
+
+
+
+
+
+
+
+
+
Zone E · Material
+
벨기에 무표백 천연 리넨
+
불규칙한 천연 실타래 슬럽 조직이 주는 촉각적 안도감과 빛 흡수력.
+
+
+
+
+
+
+
+
+
+
Zone E · Material
+
수공예 파티나 브러시드 브론즈
+
손길이 닿을수록 깊어지는 유기적 산화 피막의 은은한 광채.
+
+
+
+
+
+
+
+
+
+
Zone F · Blueprint
+
86평 복층 건축 평면 설계도
+
동선 효율과 채광축을 정밀하게 구획한 마스터 아키텍처 드래프트.
+
+
+
+
+
+
+
+
+
+
Zone F · Analysis
+
3D 태양광 입사 조도 분석
+
하지 및 춘분 정오 55° 고도각에 따른 Lux 분포 및 음영 계산도.
+
+
+
+
+
+
+
+
+
+
Zone F · Detail
+
5mm 음각 섀도우 갭 조인트
+
석재와 원목 패널 사이의 0.5mm 오차 없는 스위스 규격 결합부.
+
+
+
+
+
+
+
+
+
+
Zone F · Detail
+
전통 장부 맞춤 마스터 목공
+
못 없이 체결되는 장인 직영 정밀 결구 구조의 하드웨어.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TECHNICAL SPECIFICATION
+
+ 수치로 입증하는 하이엔드 시공 규격
+
+
+
+ 단차 허용 공차 : 0.3mm 이내 (레이저 레벨링 및 통석재 현장 가공)
+
+
+ 조명 색온도 제어 : 2200K (석양 캔들) ~ 5500K (자연 주광) DALI 스마트 디밍
+
+
+ 공조 매립 규격 : 무몰딩 트림리스 선형 디퓨저 & 히든 점검구 100% 설계
+
+
+
+ 이 프로젝트 스타일로 상담 신청하기 →
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/site/public/work/materia-interior/styles/main.css b/apps/site/public/work/materia-interior/styles/main.css
new file mode 100644
index 0000000..6d0a712
--- /dev/null
+++ b/apps/site/public/work/materia-interior/styles/main.css
@@ -0,0 +1,684 @@
+@import "./tokens.css";
+
+/* ==========================================================================
+ 마테리아 스튜디오 (MATERIA Spatial Design) — 메인 스타일시트 SSOT
+ 프리셋: editorial + high-end-visual-design
+ ========================================================================== */
+
+*, *::before, *::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html {
+ font-size: 16px;
+ scroll-behavior: smooth;
+ background-color: var(--surface);
+ color: var(--ink);
+ font-family: var(--font-body);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ transition: background-color var(--dur-normal), color var(--dur-normal);
+ overflow-x: hidden;
+}
+
+body {
+ min-height: 100dvh;
+ line-height: var(--leading-normal);
+ overflow-x: hidden;
+ background: var(--surface);
+ transition: background-color var(--dur-normal);
+}
+
+img {
+ display: block;
+ max-width: 100%;
+ height: auto;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+ transition: color var(--dur-fast);
+}
+
+a:hover {
+ color: var(--accent);
+}
+
+button, input, select, textarea {
+ font: inherit;
+ color: inherit;
+}
+
+button {
+ cursor: pointer;
+ border: none;
+ background: none;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
+.mono {
+ font-family: var(--font-mono);
+}
+
+.serif {
+ font-family: var(--font-serif);
+}
+
+.italic {
+ font-family: var(--font-serif);
+ font-style: italic;
+}
+
+.wrap {
+ width: min(100% - 2 * var(--pad-inline), var(--container));
+ margin-inline: auto;
+}
+
+/* ── 접근성 스킵 링크 ── */
+.skip-link {
+ position: fixed;
+ top: 1rem;
+ left: 1rem;
+ z-index: 1000;
+ padding: 0.5rem 1.25rem;
+ background: var(--surface-inverse);
+ color: var(--ink-inverse);
+ border-radius: var(--radius-xs);
+ transform: translateY(-200%);
+ transition: transform var(--dur-fast);
+ text-decoration: none;
+ font-weight: 500;
+}
+.skip-link:focus {
+ transform: translateY(0);
+}
+
+/* ── 상단 헤더 & 조도 스위처 (GNB SSOT) ── */
+.site-header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: rgba(245, 243, 239, 0.94);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ border-bottom: 1px solid var(--line);
+ min-height: 64px;
+ display: flex;
+ align-items: center;
+ transition: background-color var(--dur-normal), border-color var(--dur-normal);
+}
+
+body[data-ambiance="night"] .site-header {
+ background: rgba(20, 19, 18, 0.94);
+}
+
+.header-inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--space-3);
+ width: 100%;
+}
+
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ text-decoration: none;
+ color: var(--ink);
+ font-family: var(--font-serif);
+ font-weight: 700;
+ font-size: var(--step-1);
+ letter-spacing: 0.08em;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.brand-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2px 6px;
+ border-radius: var(--radius-xs);
+ background: var(--surface-card);
+ border: 1px solid var(--line);
+ color: var(--accent);
+ font-family: var(--font-serif);
+ font-size: 11px;
+ font-weight: 600;
+}
+
+.brand-han {
+ font-size: var(--step--1);
+ color: var(--accent);
+ margin-left: 4px;
+ font-weight: 500;
+}
+
+.nav-links {
+ display: flex;
+ align-items: center;
+ gap: var(--space-4);
+ list-style: none;
+}
+
+.nav-links a {
+ text-decoration: none;
+ color: var(--ink-muted);
+ font-size: var(--step--1);
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ transition: color var(--dur-fast);
+ white-space: nowrap;
+}
+
+.nav-links a:hover,
+.nav-links a.active {
+ color: var(--accent);
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ flex-shrink: 0;
+}
+
+/* 조도 앰비언스 모드 스위처 (Day vs Night) */
+.ambiance-switcher,
+.ambiance-toggle {
+ display: inline-flex;
+ align-items: center;
+ background: var(--surface-card);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ padding: 2px;
+ gap: 2px;
+ flex-shrink: 0;
+}
+
+.ambiance-btn {
+ padding: 4px 10px;
+ border-radius: 2px;
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--ink-muted);
+ transition: all var(--dur-fast);
+ white-space: nowrap;
+}
+
+.ambiance-btn.is-active,
+.ambiance-btn.active {
+ background: var(--surface-raised);
+ color: var(--ink);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+/* ── 버튼 시스템 & 더블 베젤 ── */
+.btn-materia {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ background: var(--surface-inverse);
+ color: var(--ink-inverse);
+ padding: 8px 16px;
+ border-radius: var(--radius-xs);
+ font-weight: 600;
+ font-size: var(--step--1);
+ text-decoration: none;
+ white-space: nowrap;
+ flex-shrink: 0;
+ transition: transform var(--dur-fast), background var(--dur-fast), color var(--dur-fast);
+}
+
+.btn-materia:hover {
+ background: var(--accent);
+ color: var(--accent-ink);
+ transform: translateY(-1px);
+}
+
+.btn-materia:active {
+ transform: scale(0.98);
+}
+
+/* 더블 베젤 프레임 (Double-Bezel) */
+.bezel-outer,
+.frame.double-bezel {
+ background: rgba(158, 128, 98, 0.06);
+ border: 1px solid var(--line);
+ padding: 6px;
+ border-radius: var(--radius-md);
+}
+
+.bezel-inner {
+ background: var(--surface-raised);
+ border: 1px solid var(--line-subtle);
+ border-radius: calc(var(--radius-md) - 4px);
+ padding: var(--space-6);
+}
+
+/* ── 히어로 섹션 ── */
+.hero-section {
+ padding-block: var(--space-6) var(--space-7);
+ position: relative;
+}
+
+.hero-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
+ gap: var(--space-6);
+ align-items: center;
+}
+
+.eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 3px 10px;
+ border-radius: var(--radius-xs);
+ background: var(--accent-light);
+ color: var(--accent);
+ font-size: var(--step--1);
+ font-family: var(--font-mono);
+ font-weight: 600;
+ margin-bottom: var(--space-3);
+ border: 1px solid var(--accent-surface);
+}
+
+.hero-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-3);
+ font-weight: 700;
+ line-height: var(--leading-tight);
+ letter-spacing: -0.01em;
+ color: var(--ink);
+ margin-bottom: var(--space-3);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.hero-desc {
+ font-size: var(--step-0);
+ color: var(--ink-muted);
+ max-width: var(--measure);
+ line-height: var(--leading-normal);
+ margin-bottom: var(--space-5);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.hero-ctas {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ flex-wrap: wrap;
+}
+
+.hero-visual {
+ position: relative;
+}
+
+.hero-frame {
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ border: 1px solid var(--line);
+ box-shadow: 0 12px 32px -10px rgba(0, 0, 0, 0.06);
+}
+
+.hero-frame img {
+ width: 100%;
+ aspect-ratio: 4/3;
+ object-fit: cover;
+ transition: transform var(--dur-slow);
+}
+
+.hero-spec-tag {
+ position: absolute;
+ bottom: -14px;
+ left: 16px;
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ padding: 8px 14px;
+ box-shadow: 0 8px 20px -4px rgba(0, 0, 0, 0.06);
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-2);
+}
+
+.spec-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-0);
+ font-weight: 700;
+ color: var(--ink);
+}
+
+.spec-sub {
+ font-family: var(--font-mono);
+ font-size: var(--step--1);
+ color: var(--accent);
+}
+
+/* ── 엄선 프로젝트 섹션 ── */
+.works-section {
+ padding-block: var(--space-7);
+ border-top: 1px solid var(--line);
+}
+
+.section-head {
+ text-align: center;
+ max-width: 640px;
+ margin-inline: auto;
+ margin-bottom: var(--space-6);
+}
+
+.section-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-2);
+ font-weight: 700;
+ color: var(--ink);
+ margin-bottom: var(--space-2);
+ text-wrap: balance;
+ word-break: keep-all;
+}
+
+.section-sub {
+ font-size: var(--step-0);
+ color: var(--ink-muted);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.works-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: var(--space-5);
+}
+
+.work-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ transition: transform var(--dur-normal), box-shadow var(--dur-normal), border-color var(--dur-normal);
+}
+
+.work-card:hover {
+ transform: translateY(-4px);
+ border-color: var(--accent);
+ box-shadow: 0 16px 36px -10px rgba(0, 0, 0, 0.08);
+}
+
+.work-card-media {
+ width: 100%;
+ aspect-ratio: 16 / 11;
+ overflow: hidden;
+ background: var(--surface-subtle);
+ position: relative;
+}
+
+.work-card-media img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+.work-card:hover .work-card-media img {
+ transform: scale(1.04);
+}
+
+/* 🚀 텍스트 영역 내부 패딩을 24px로 넉넉하게 부여하여 0px 패딩 버그 완전 해결 */
+.work-card-body {
+ padding: var(--space-5);
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ gap: var(--space-2);
+}
+
+.work-card-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-1);
+ font-weight: 700;
+ color: var(--ink);
+ letter-spacing: -0.02em;
+}
+
+.work-card-type {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ color: var(--accent);
+ font-weight: 600;
+}
+
+.work-card-desc {
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+ line-height: 1.65;
+ margin-block: var(--space-1) var(--space-4);
+ flex-grow: 1;
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+.work-card-actions {
+ margin-top: auto;
+}
+
+.work-card-actions .btn-materia {
+ width: 100%;
+ text-align: center;
+ justify-content: center;
+ padding: 10px 16px;
+ font-size: var(--step--1);
+}
+
+/* ── 물성 아카이브 섹션 ── */
+.material-section {
+ padding-block: var(--space-7);
+ background: var(--surface-card);
+ border-top: 1px solid var(--line);
+ border-bottom: 1px solid var(--line);
+}
+
+.material-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
+ gap: var(--space-6);
+ align-items: center;
+}
+
+.mat-list {
+ display: grid;
+ gap: var(--space-3);
+}
+
+.mat-item {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ padding: var(--space-4);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+}
+
+.mat-item-name {
+ font-family: var(--font-serif);
+ font-size: var(--step-0);
+ font-weight: 700;
+ color: var(--ink);
+}
+
+.mat-item-desc {
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+ line-height: var(--leading-normal);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+/* ── 설계 프로세스 섹션 ── */
+.process-section {
+ padding-block: var(--space-7);
+}
+
+.process-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: var(--space-3);
+}
+
+.process-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ padding: var(--space-4);
+}
+
+.process-num {
+ font-family: var(--font-mono);
+ font-size: var(--step--1);
+ color: var(--accent);
+ font-weight: 700;
+ margin-bottom: var(--space-2);
+}
+
+.process-title {
+ font-family: var(--font-serif);
+ font-size: var(--step-0);
+ font-weight: 700;
+ color: var(--ink);
+ margin-bottom: var(--space-1);
+}
+
+.process-desc {
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+ line-height: var(--leading-normal);
+ text-wrap: pretty;
+ word-break: keep-all;
+}
+
+/* ── 프라이빗 상담 섹션 ── */
+.contact-section {
+ padding-block: var(--space-7);
+ background: var(--surface-card);
+ border-top: 1px solid var(--line);
+}
+
+.contact-card {
+ background: var(--surface-raised);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-sm);
+ padding: var(--space-5);
+ max-width: 680px;
+ margin-inline: auto;
+}
+
+.form-group {
+ margin-bottom: var(--space-4);
+}
+
+.form-label {
+ display: block;
+ font-size: var(--step--1);
+ font-weight: 600;
+ color: var(--ink);
+ margin-bottom: var(--space-2);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+}
+
+.form-select, .form-input, .form-textarea {
+ width: 100%;
+ background: var(--surface);
+ border: 1px solid var(--line);
+ border-radius: var(--radius-xs);
+ padding: 10px 14px;
+ color: var(--ink);
+ font-size: var(--step-0);
+ transition: border-color var(--dur-fast);
+}
+
+.form-select:focus, .form-input:focus, .form-textarea:focus {
+ outline: none;
+ border-color: var(--accent);
+}
+
+/* ── 푸터 ── */
+.site-footer {
+ border-top: 1px solid var(--line);
+ padding-block: var(--space-5);
+ background: var(--surface);
+ font-size: var(--step--1);
+ color: var(--ink-muted);
+}
+
+.footer-inner {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+}
+
+/* ── 반응형 미디어 쿼리 ── */
+@media (max-width: 860px) {
+ .nav-links {
+ display: none;
+ }
+ .hero-grid, .works-grid, .material-grid {
+ grid-template-columns: 1fr;
+ gap: var(--space-5);
+ }
+ .process-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+ .brand-han {
+ display: none;
+ }
+ .header-actions .btn-materia {
+ display: none;
+ }
+ .site-header {
+ min-height: 56px;
+ }
+}
+
+@media (max-width: 600px) {
+ .process-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* SSOT Aliases */
+.site-head { position: sticky; top: 0; z-index: 50; background: rgba(245, 243, 239, 0.94); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-bottom: 1px solid var(--line); padding-block: var(--space-3); }
+body[data-ambiance="night"] .site-head { background: rgba(20, 19, 18, 0.94); }
+.head-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
+.wordmark { display: inline-flex; align-items: baseline; gap: var(--space-2); font-family: var(--font-serif); font-size: var(--step-1); font-weight: 700; color: var(--ink); text-decoration: none; }
+.wordmark-han { font-size: var(--step--1); color: var(--accent); font-weight: 500; }
+.nav { display: flex; align-items: center; gap: var(--space-5); }
+.nav a { font-size: var(--step--1); font-weight: 500; color: var(--ink-muted); text-decoration: none; }
+.nav a:hover, .nav a.active { color: var(--ink); }
+.head-actions { display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }
+.site-foot { border-top: 1px solid var(--line); padding-block: var(--space-6); background: var(--surface); font-size: var(--step--1); color: var(--ink-muted); }
+.foot-inner { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-4); }
diff --git a/apps/site/public/work/materia-interior/styles/tokens.css b/apps/site/public/work/materia-interior/styles/tokens.css
new file mode 100644
index 0000000..6f37c46
--- /dev/null
+++ b/apps/site/public/work/materia-interior/styles/tokens.css
@@ -0,0 +1,94 @@
+/* ==========================================================================
+ 마테리아 스튜디오 (MATERIA Spatial Design) — 디자인 토큰 SSOT
+ 프리셋: editorial + quiet-architecture
+ 컨셉: 트래버틴과 빛의 건축 — 물성과 침묵이 빚어내는 하이엔드 공간
+ ========================================================================== */
+
+:root {
+ /* 타이포그래피 스케일 */
+ --step--1: 0.8125rem; /* 13px — 캡션·치수 */
+ --step-0: 1rem; /* 16px — 본문 */
+ --step-1: 1.1875rem; /* 19px — 소제목 */
+ --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem); /* 30px — 섹션 제목 */
+ --step-3: clamp(2.25rem, 1.5rem + 3vw, 3.8rem); /* 60px — 메인 표제 */
+
+ --leading-tight: 1.18;
+ --leading-normal: 1.75;
+ --measure: 54ch;
+
+ --font-serif: "MaruBuri", "Noto Serif KR", serif;
+ --font-display: "MaruBuri", "Noto Serif KR", serif;
+ --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
+ --font-mono: "JetBrains Mono", monospace;
+
+ /* 웜 트래버틴 스톤 색상 (Day 기본) */
+ --surface: #f5f3ef;
+ --surface-raised: #faf9f6;
+ --surface-card: #ece8e1;
+ --surface-sunken: #e4dfd6;
+ --surface-inverse: #161514;
+
+ --ink: #161514;
+ --ink-muted: #524f4b;
+ --ink-faint: #8c8780;
+ --ink-inverse: #f5f3ef;
+ --ink-inverse-muted: #a39e96;
+
+ --line: #ddd8cf;
+ --line-strong: #bfb8ab;
+ --line-subtle: #eae5dc;
+ --line-inverse: #33312f;
+
+ /* 단일 강조색 — 브러시드 브론즈 */
+ --accent: #9e8062;
+ --accent-hover: #856a4f;
+ --accent-light: rgba(158, 128, 98, 0.12);
+ --accent-surface: rgba(158, 128, 98, 0.2);
+ --accent-ink: #ffffff;
+
+ --radius: 2px;
+ --radius-sm: 4px;
+ --radius-md: 8px;
+
+ /* 간격 — SSOT 표준 */
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 1rem;
+ --space-4: 1.5rem;
+ --space-5: 2.5rem;
+ --space-6: 4rem;
+ --space-7: 6rem;
+ --space-8: clamp(4.5rem, 3rem + 6vw, 9rem);
+
+ /* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */
+ --container: 1120px;
+ --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
+
+ /* 모션 */
+ --dur-quick: 180ms;
+ --dur-normal: 420ms;
+ --ease-soft: cubic-bezier(0.25, 1, 0.35, 1);
+}
+
+/* ── 나이트 무드 앰비언스 ── */
+body[data-ambiance="night"] {
+ --surface: #141312;
+ --surface-raised: #1c1b19;
+ --surface-card: #22201e;
+ --surface-sunken: #2b2926;
+ --surface-inverse: #f5f3ef;
+
+ --ink: #f5f3ef;
+ --ink-muted: #a39e96;
+ --ink-faint: #736e67;
+ --ink-inverse: #141312;
+
+ --line: #2e2b27;
+ --line-strong: #45413c;
+ --line-subtle: #242220;
+
+ --accent: #c4a482;
+ --accent-hover: #dbbe9d;
+ --accent-light: rgba(196, 164, 130, 0.15);
+ --accent-ink: #141312;
+}
diff --git a/apps/site/src/assets/shots/work-cask-archive-2.png b/apps/site/src/assets/shots/work-cask-archive-2.png
new file mode 100644
index 0000000..ced06b9
Binary files /dev/null and b/apps/site/src/assets/shots/work-cask-archive-2.png differ
diff --git a/apps/site/src/assets/shots/work-cask-archive.png b/apps/site/src/assets/shots/work-cask-archive.png
new file mode 100644
index 0000000..8e3268b
Binary files /dev/null and b/apps/site/src/assets/shots/work-cask-archive.png differ
diff --git a/apps/site/src/assets/shots/work-cheongon-care-2.png b/apps/site/src/assets/shots/work-cheongon-care-2.png
new file mode 100644
index 0000000..0720a9c
Binary files /dev/null and b/apps/site/src/assets/shots/work-cheongon-care-2.png differ
diff --git a/apps/site/src/assets/shots/work-cheongon-care.png b/apps/site/src/assets/shots/work-cheongon-care.png
new file mode 100644
index 0000000..d98040d
Binary files /dev/null and b/apps/site/src/assets/shots/work-cheongon-care.png differ
diff --git a/apps/site/src/assets/shots/work-materia-interior-2.png b/apps/site/src/assets/shots/work-materia-interior-2.png
new file mode 100644
index 0000000..ead1c88
Binary files /dev/null and b/apps/site/src/assets/shots/work-materia-interior-2.png differ
diff --git a/apps/site/src/assets/shots/work-materia-interior.png b/apps/site/src/assets/shots/work-materia-interior.png
new file mode 100644
index 0000000..94f0163
Binary files /dev/null and b/apps/site/src/assets/shots/work-materia-interior.png differ
diff --git a/apps/site/src/components/ShowcaseHeroCanvas.astro b/apps/site/src/components/ShowcaseHeroCanvas.astro
new file mode 100644
index 0000000..6e0bce1
--- /dev/null
+++ b/apps/site/src/components/ShowcaseHeroCanvas.astro
@@ -0,0 +1,64 @@
+---
+interface Props {
+ class?: string;
+}
+const { class: className } = Astro.props;
+---
+
+
+
+
+
+
diff --git a/apps/site/src/data/work.ts b/apps/site/src/data/work.ts
index c5f6e77..15b247b 100644
--- a/apps/site/src/data/work.ts
+++ b/apps/site/src/data/work.ts
@@ -34,6 +34,12 @@ import gaonLms from "../assets/shots/work-gaon-lms.png";
import gaonLms2 from "../assets/shots/work-gaon-lms-2.png";
import dureEnrollment from "../assets/shots/work-dure-enrollment.png";
import dureEnrollment2 from "../assets/shots/work-dure-enrollment-2.png";
+import cheongonCare from "../assets/shots/work-cheongon-care.png";
+import cheongonCare2 from "../assets/shots/work-cheongon-care-2.png";
+import caskArchive from "../assets/shots/work-cask-archive.png";
+import caskArchive2 from "../assets/shots/work-cask-archive-2.png";
+import materiaInterior from "../assets/shots/work-materia-interior.png";
+import materiaInterior2 from "../assets/shots/work-materia-interior-2.png";
export interface WorkItem {
slug: string;
@@ -45,6 +51,7 @@ export interface WorkItem {
date: string;
metrics: string[];
href: string;
+ subLinks?: { label: string; href: string }[];
shot: ImageMetadata;
shotAlt?: ImageMetadata;
alt: string;
@@ -258,4 +265,60 @@ export const WORK: WorkItem[] = [
alt: "두레대학 수강신청 — 과목 원장 목록과 주홍 도장 블록이 놓이는 주간 시간표 격자",
size: "md",
},
+
+ /* ── 7행: 신규 쇼케이스 3종 (2 + 2 + 2) ── */
+ {
+ slug: "cheongon-care",
+ name: "청온 케어 (淸溫)",
+ kind: "위생 케어 & 관제 콘솔 · 2모드",
+ concept: "임상 수준의 6단계 위생 프로토콜 — 정찰제 견적과 실시간 배차 관제 콘솔",
+ risk: "고객용 견적 신청 폼과 관리자용 관제 대시보드가 단일 웹앱에서 실시간 상태를 공유하는 풀-루프 인터랙션",
+ preset: "ledger-console",
+ date: "2026-08",
+ metrics: ["고객·관리자 2모드", "실시간 정찰제 견적기", "32개 QC 체크리스트", "에메랄드 1색", "하드 게이트 14/14"],
+ href: "/work/cheongon-care/",
+ subLinks: [
+ { label: "임상 6단계 위생 프로토콜 & ATP 리포트", href: "/work/cheongon-care/protocol/" }
+ ],
+ shot: cheongonCare,
+ shotAlt: cheongonCare2,
+ alt: "청온 케어 — 정밀 입주 케어가 완료된 거실 전경과 독일 카처 고온 스팀 위생 장비",
+ size: "sm",
+ },
+ {
+ slug: "cask-archive",
+ name: "캐스크 아카이브 (樽室)",
+ kind: "독립병입자 위스키바 & 도슨트",
+ concept: "시간과 배럴이 빚어낸 단 하나의 원액 — 독립병입자 공식 인증 원장과 1:1 도슨트",
+ risk: "일반 메뉴판 대신 단일 캐스크 넘버와 통관 필증이 기록된 캐스크 원장 및 향미 필터를 첫 화면에 배치",
+ preset: "editorial-archival",
+ date: "2026-08",
+ metrics: ["독립병입자 4종 인증", "실시간 캐스크 원장 검색", "1:1 도슨트 예약", "앰버 골드 1색", "하드 게이트 14/14"],
+ href: "/work/cask-archive/",
+ subLinks: [
+ { label: "1:1 위스키 도슨트 테이스팅 마스터클래스", href: "/work/cask-archive/docent/" }
+ ],
+ shot: caskArchive,
+ shotAlt: caskArchive2,
+ alt: "캐스크 아카이브 — 오크 배럴 상판 위 싱글 캐스크 위스키 보틀과 스코틀랜드 숙성고 전경",
+ size: "sm",
+ },
+ {
+ slug: "materia-interior",
+ name: "마테리아 스튜디오 (空間素材)",
+ kind: "하이엔드 공간 인테리어 건축",
+ concept: "빛과 트래버틴이 빚어내는 침묵 — 물성과 자연광의 궤적을 설계하는 아틀리에",
+ risk: "썸네일 그리드를 버리고 트래버틴·오크·리넨의 실측 물성 스펙과 자연광/야간 조도 앰비언스 실시간 전환을 제공",
+ preset: "editorial",
+ date: "2026-08",
+ metrics: ["자연광·야간 조도 전환", "트래버틴 물성 스펙", "더블 베젤 갤러리", "브론즈 1색", "하드 게이트 14/14"],
+ href: "/work/materia-interior/",
+ subLinks: [
+ { label: "24개 공간 심층 아카이브 갤러리 (한남 듀플렉스)", href: "/work/materia-interior/projects/hannam-duplex/" }
+ ],
+ shot: materiaInterior,
+ shotAlt: materiaInterior2,
+ alt: "마테리아 스튜디오 — 트래버틴 벽체 펜트하우스 거실 전경과 천연 마감재 플랫레이",
+ size: "sm",
+ },
];
diff --git a/apps/site/src/gl/boot.ts b/apps/site/src/gl/boot.ts
new file mode 100644
index 0000000..528993b
--- /dev/null
+++ b/apps/site/src/gl/boot.ts
@@ -0,0 +1,71 @@
+/**
+ * Three.js 지연 로드 부트스트랩
+ * designpaca/references/three.md 코드 A 준수
+ */
+export async function boot(
+ canvas: HTMLCanvasElement | null,
+ loader: () => Promise<{ default: (canvas: HTMLCanvasElement, opts: any) => () => void }>
+) {
+ if (!canvas || !gate()) {
+ canvas?.remove();
+ return null;
+ }
+
+ await inViewport(canvas);
+
+ try {
+ const { default: mount } = await loader();
+ const css = getComputedStyle(document.documentElement);
+ const dispose = mount(canvas, { css });
+
+ const dur = css.getPropertyValue("--dur-normal").trim() || "350ms";
+ const ease = css.getPropertyValue("--ease-out").trim() || "ease";
+ canvas.style.transition = `opacity ${dur} ${ease}`;
+ requestAnimationFrame(() => {
+ canvas.style.opacity = "1";
+ });
+
+ window.addEventListener("pagehide", dispose, { once: true });
+ return dispose;
+ } catch (err) {
+ console.warn("[designpaca:three] WebGL init fallback:", err);
+ canvas.remove();
+ return null;
+ }
+}
+
+function gate(): boolean {
+ if (typeof window === "undefined") return false;
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
+ if ((navigator as any).connection?.saveData) return false;
+ if (((navigator as any).deviceMemory ?? 8) < 4 || (navigator.hardwareConcurrency ?? 8) < 4) return false;
+
+ try {
+ const probe = document.createElement("canvas");
+ const gl = probe.getContext("webgl2") || probe.getContext("webgl");
+ if (!gl) return false;
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+function inViewport(el: HTMLElement): Promise {
+ return new Promise((resolve) => {
+ if (typeof IntersectionObserver === "undefined") {
+ resolve();
+ return;
+ }
+ const io = new IntersectionObserver(
+ ([entry]) => {
+ if (entry.isIntersecting) {
+ io.disconnect();
+ resolve();
+ }
+ },
+ { rootMargin: "200px" }
+ );
+ io.observe(el);
+ });
+}
diff --git a/apps/site/src/gl/scenes/showcase-hero.ts b/apps/site/src/gl/scenes/showcase-hero.ts
new file mode 100644
index 0000000..fb3e739
--- /dev/null
+++ b/apps/site/src/gl/scenes/showcase-hero.ts
@@ -0,0 +1,166 @@
+import * as THREE from "three";
+import { createStage } from "../stage.ts";
+
+const VERT = /* glsl */ `
+ void main() {
+ gl_Position = vec4(position.xy, 0.0, 1.0);
+ }
+`;
+
+const FRAG = /* glsl */ `
+ precision highp float;
+ uniform float uTime;
+ uniform float uScroll;
+ uniform vec2 uResolution;
+ uniform vec2 uPointer;
+ uniform vec3 uSurface;
+ uniform vec3 uRaised;
+ uniform vec3 uAccent;
+ uniform vec3 uAccent2;
+
+ // Simplex 2D noise
+ vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
+ vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
+ vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
+
+ float snoise(vec2 v) {
+ const vec4 C = vec4(0.211324865405187,
+ 0.366025403784439,
+ -0.577350269189626,
+ 0.024390243902439);
+ vec2 i = floor(v + dot(v, C.yy));
+ vec2 x0 = v - i + dot(i, C.xx);
+ vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
+ vec4 x12 = x0.xyxy + C.xxzz;
+ x12.xy -= i1;
+ i = mod289(i);
+ vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
+ vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
+ m = m*m;
+ m = m*m;
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
+ vec3 h = abs(x) - 0.5;
+ vec3 ox = floor(x + 0.5);
+ vec3 a0 = x - ox;
+ m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
+ vec3 g;
+ g.x = a0.x * x0.x + h.x * x0.y;
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
+ return 130.0 * dot(m, g);
+ }
+
+ float fbm(vec2 p) {
+ float v = 0.0;
+ float a = 0.5;
+ mat2 rot = mat2(cos(0.35), sin(0.35), -sin(0.35), cos(0.35));
+ for (int i = 0; i < 3; ++i) {
+ v += a * snoise(p);
+ p = rot * p * 1.75 + vec2(10.0);
+ a *= 0.5;
+ }
+ return v;
+ }
+
+ void main() {
+ // Normalise coordinates to center, maintaining aspect ratio
+ vec2 st = (gl_FragCoord.xy * 2.0 - uResolution) / max(uResolution.x, uResolution.y);
+
+ // Very gentle, slow breathing tempo (ambient silk light wave, no storm vortex)
+ float t = uTime * 0.035;
+
+ // Gentle pointer glow displacement
+ vec2 pc = (uPointer - 0.5) * 1.5;
+ float pDist = length(st - pc);
+ vec2 pDrift = normalize(st - pc + 1e-4) * exp(-pDist * 2.8) * 0.06;
+
+ // Broad, calm rolling ambient wave
+ vec2 waveCoords = st * 1.1 + pDrift;
+ float n1 = fbm(waveCoords + vec2(t * 0.1, t * 0.07));
+ float n2 = fbm(waveCoords * 1.3 - vec2(t * 0.05, n1 * 0.35));
+
+ float lightField = fbm(waveCoords + vec2(n2 * 0.5, t * 0.04));
+
+ // Base background colors
+ vec3 col = mix(uSurface, uRaised, clamp(lightField * 0.65 + 0.35, 0.0, 1.0));
+
+ // Subtle, elegant emerald & amber mineral glows in soft ambient pockets
+ float accentGlow = smoothstep(-0.25, 0.65, n1) * 0.28;
+ float goldGlow = smoothstep(-0.15, 0.7, n2) * 0.14;
+
+ col = mix(col, uAccent, accentGlow);
+ col = mix(col, uAccent2, goldGlow);
+
+ // Soft vignette
+ float vignette = smoothstep(1.35, 0.35, length(st));
+ col *= (0.75 + 0.25 * vignette);
+
+ // Grain dither to eliminate banding
+ float grain = (fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) - 0.5) * (1.2 / 255.0);
+ col += grain;
+
+ gl_FragColor = vec4(col, 0.92);
+ }
+`;
+
+export default function mount(canvas: HTMLCanvasElement, { css }: { css: CSSStyleDeclaration }) {
+ const stage = createStage(canvas, { dprMax: Math.min(window.devicePixelRatio || 1, 1.5), alpha: true });
+
+ const getColor = (token: string, fallback: string) => {
+ const val = css.getPropertyValue(token).trim();
+ return new THREE.Color(val || fallback);
+ };
+
+ // Fullscreen Triangle
+ const geometry = new THREE.BufferGeometry();
+ geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]), 3));
+
+ const uniforms = {
+ uTime: { value: 0 },
+ uScroll: { value: 0 },
+ uResolution: { value: new THREE.Vector2(1, 1) },
+ uPointer: { value: new THREE.Vector2(0.5, 0.5) },
+ uSurface: { value: getColor("--surface-raised", "#121820") },
+ uRaised: { value: getColor("--surface-sunken", "#080c11") },
+ uAccent: { value: getColor("--accent", "#3ee5b3") },
+ uAccent2: { value: new THREE.Color("#d99b38") }, // Warm gold mineral glow
+ };
+
+ const mesh = new THREE.Mesh(
+ geometry,
+ new THREE.ShaderMaterial({
+ vertexShader: VERT,
+ fragmentShader: FRAG,
+ uniforms,
+ depthTest: false,
+ depthWrite: false,
+ transparent: true,
+ })
+ );
+ mesh.frustumCulled = false;
+ stage.scene.add(mesh);
+
+ stage.onResize = (w: number, h: number, dpr: number) => {
+ uniforms.uResolution.value.set(w * dpr, h * dpr);
+ };
+ stage.onResize(stage.size.w, stage.size.h, stage.size.dpr);
+
+ const targetPointer = new THREE.Vector2(0.5, 0.5);
+ const onPointerMove = (e: MouseEvent) => {
+ targetPointer.set(e.clientX / window.innerWidth, 1 - e.clientY / window.innerHeight);
+ };
+ window.addEventListener("pointermove", onPointerMove, { passive: true });
+
+ stage.add((t: number, dt: number) => {
+ uniforms.uTime.value = t;
+ uniforms.uScroll.value = window.scrollY || 0;
+
+ // Smooth pointer lerp
+ uniforms.uPointer.value.lerp(targetPointer, Math.min(1, dt * 2.5));
+ });
+
+ return () => {
+ window.removeEventListener("pointermove", onPointerMove);
+ geometry.dispose();
+ (mesh.material as THREE.Material).dispose();
+ };
+}
diff --git a/apps/site/src/gl/stage.ts b/apps/site/src/gl/stage.ts
new file mode 100644
index 0000000..03dc018
--- /dev/null
+++ b/apps/site/src/gl/stage.ts
@@ -0,0 +1,133 @@
+import * as THREE from "three";
+
+export interface StageOptions {
+ dprMax?: number;
+ alpha?: boolean;
+}
+
+export function createStage(canvas: HTMLCanvasElement, { dprMax = 1.5, alpha = true }: StageOptions = {}) {
+ const renderer = new THREE.WebGLRenderer({
+ canvas,
+ alpha,
+ antialias: false,
+ stencil: false,
+ powerPreference: "high-performance",
+ });
+
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
+ renderer.toneMapping = THREE.NoToneMapping;
+
+ const scene = new THREE.Scene();
+ const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
+ camera.position.z = 5;
+
+ const size = { w: 0, h: 0, dpr: 1 };
+ const updaters = new Set<(t: number, dt: number) => void>();
+ let running = false;
+ let last = 0;
+ const t0 = performance.now();
+
+ const draw = (t: number, dt: number) => {
+ for (const f of updaters) f(t, dt);
+ renderer.render(scene, camera);
+ };
+
+ const tick = (now: number) => {
+ const dt = Math.min((now - last) / 1000, 1 / 20);
+ last = now;
+ draw((now - t0) / 1000, dt);
+ };
+
+ const start = () => {
+ if (!running) {
+ running = true;
+ last = performance.now();
+ renderer.setAnimationLoop(tick);
+ }
+ };
+
+ const stop = () => {
+ running = false;
+ renderer.setAnimationLoop(null);
+ };
+
+ function resize() {
+ const r = canvas.getBoundingClientRect();
+ const w = Math.max(1, Math.round(r.width));
+ const h = Math.max(1, Math.round(r.height));
+ const dpr = Math.min(window.devicePixelRatio || 1, dprMax);
+
+ if (w === size.w && h === size.h && dpr === size.dpr) return;
+ Object.assign(size, { w, h, dpr });
+
+ renderer.setPixelRatio(dpr);
+ renderer.setSize(w, h, false);
+ camera.aspect = w / h;
+ camera.updateProjectionMatrix();
+
+ stage.onResize?.(w, h, dpr);
+ if (!running) draw(0, 0);
+ }
+
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(resize) : null;
+ ro?.observe(canvas);
+
+ const io =
+ typeof IntersectionObserver !== "undefined"
+ ? new IntersectionObserver(([e]) => (e.isIntersecting ? start() : stop()), { rootMargin: "10%" })
+ : null;
+ io?.observe(canvas);
+
+ const onVis = () => (document.hidden ? stop() : start());
+ document.addEventListener("visibilitychange", onVis);
+
+ const onLost = (e: Event) => {
+ e.preventDefault();
+ stop();
+ };
+ canvas.addEventListener("webglcontextlost", onLost);
+
+ const stage = {
+ renderer,
+ scene,
+ camera,
+ size,
+ start,
+ stop,
+ onResize: null as ((w: number, h: number, dpr: number) => void) | null,
+ renderOnce: () => draw(0, 0),
+ add(f: (t: number, dt: number) => void) {
+ updaters.add(f);
+ return () => updaters.delete(f);
+ },
+ setDprMax(v: number) {
+ dprMax = v;
+ size.dpr = -1;
+ resize();
+ },
+ dispose() {
+ stop();
+ ro?.disconnect();
+ io?.disconnect();
+ document.removeEventListener("visibilitychange", onVis);
+ canvas.removeEventListener("webglcontextlost", onLost);
+ scene.traverse((o: any) => {
+ o.geometry?.dispose();
+ const mats = [o.material].flat().filter(Boolean);
+ for (const m of mats) {
+ for (const u of Object.values(m.uniforms ?? {}) as any[]) {
+ if (u?.value?.isTexture) u.value.dispose();
+ }
+ m.dispose();
+ }
+ });
+ scene.clear();
+ renderer.dispose();
+ renderer.forceContextLoss();
+ updaters.clear();
+ },
+ };
+
+ resize();
+ return stage;
+}
diff --git a/apps/site/src/pages/showcase.astro b/apps/site/src/pages/showcase.astro
index 0a97ade..72b808e 100644
--- a/apps/site/src/pages/showcase.astro
+++ b/apps/site/src/pages/showcase.astro
@@ -1,43 +1,81 @@
---
import { Image } from "astro:assets";
import Base from "../layouts/Base.astro";
-import BgPhoto from "../components/BgPhoto.astro";
+import ShowcaseHeroCanvas from "../components/ShowcaseHeroCanvas.astro";
+import CopyCommand from "../components/CopyCommand.astro";
import { WORK } from "../data/work.ts";
/*
- 쇼케이스. 카드는 간단한 정보만 싣고(1′ 레퍼런스 tailwindcss.com/showcase:
- 이미지 + 이름 + 한 줄 종류 + 태그), 상세는 네이티브 모달로.
- showModal() 이 Esc·포커스 트랩·aria를 브라우저가 처리한다 —
- 메뉴와 같은 결정이다. 사이트 자체는 모달의 큰 CTA로 새 탭.
-
- 그리드는 균일하지 않다: 첫 사례(목요일의 화원)만 2열 폭으로 features,
- 나머지는 3열. 중요도가 다른데 같은 크기로 두면 그 차이가 사라진다.
+ 쇼케이스 아카이브.
+ 카드는 필수 정보만 싣고, 상세 설계와 지표는 네이티브 모달로 열린다.
*/
-const title = "사례 · designpaca";
+const title = "사례 아카이브 · designpaca";
const description =
- "designpaca 파이프라인으로 만든 사이트들. 카드를 누르면 컨셉과 리스크, 실측값이 담긴 상세가 열립니다.";
+ "템플릿을 거부한 자리에 남는 열다섯 개의 고유한 인터페이스. 물성과 타이포그래피, 60fps 인터랙티브 셰이더로 완성된 라이브 쇼케이스를 탐색하세요.";
const ariaClose = "닫기";
const ariaDetail = "자세히 보기";
const siteLabel = "사이트 보기";
---
-
-
- 사례 아카이브
- 파이프라인을 돌려서 나온 것들
-
- 모두 0단계부터 6단계까지 그대로 돌려서 나온 결과물입니다. 각 사례마다
- 감수한 리스크 를 함께 적었습니다 — 그것이 없으면 안전하지만 잊히는 디자인이 됩니다.
-
-
- 카드를 누르면 상세 화면이 열리고, 사이트는 새 탭에서 열립니다. 목업이 아니라
- 동작하는 페이지입니다.
-
+
+
+
+
+
+
+
+ PROVEN ARCHIVE · 15 LIVE SHOWCASES
+
+
+
+ 타협 없는 태도가 빚어낸
+ 살아 숨 쉬는 열다섯 개의 결
+
+
+
+ AI가 양산하는 흔한 템플릿과 얄팍한 클리셰를 걷어내고,
+ 고유한 물성과 타이포그래피, 60fps 인터랙티브 셰이더 로 완성한 실사례 아카이브입니다.
+ 브라우저 위에서 실제로 작동하는 15가지 웹 공간을 직접 경험해 보세요.
+
+
+
+
+ {/* 실측 지표 & 특징 뱃지 스트립 */}
+
+
+
+ 15
+ 라이브 쇼케이스
+
+
+ 0%
+ AI 템플릿 슬롭
+
+
+ 60fps
+ WebGL2 셰이더 반응
+
+
+ A11y
+ 웹 표준 & 접근성 보증
+
+
+
+
+ {/* 탐색 가이드 바 */}
+
+
EXPLORATION GUIDE
+
+ 카드를 누르면 설계 철학 & 실측 지표 모달 이 열리며, [사이트 보기 ↗] 를 통해 새 탭에서 실제 라이브 웹앱을 조작할 수 있습니다.
+
+
+
+
-
-
+
+
{
WORK.map((w, i) => (
@@ -62,24 +100,58 @@ const siteLabel = "사이트 보기";
/>
)}
{w.preset}
+
+ {/* 카드 이미지 호버 시 나타나는 사이트 바로가기 퀵 액션 */}
+
+ {siteLabel}
+
+
+
+
+
+
-
{w.name}
+
{w.kind}
{w.concept}
-
- {ariaDetail}
-
-
-
-
+
+ {/* 카드 전체 클릭: 모달 상세 열기 (직접 링크는 z-index가 더 높아 독립 동작) */}
- {/* 상세 모달. 네이티브 : Esc·포커스·inert 를 브라우저가 처리 */}
+ {/* 상세 모달: 네이티브 (2열 스플릿 + 첫 화면 상단 CTA + 모바일 스티키 독) */}
@@ -88,34 +160,75 @@ const siteLabel = "사이트 보기";
-
-
-
-
-
-
{w.kind} · {w.preset}
-
{w.name}
-
{w.concept}
-
-
-
감수한 리스크
-
{w.risk}
+
+ {/* 좌측: 스크린샷 뷰어 */}
+
+
+
+
-
- {w.metrics.map((m) => (
- {m}
- ))}
-
+ {/* 우측: 정보 & 상단 주 CTA (스크롤 없이 즉시 노출) */}
+
+
+
+ {/* 모바일 뷰포트용 스티키 바텀 독: 스크롤 중에도 언제든 엄지 영역에 고정 */}
+
+
+ {siteLabel}
@@ -130,40 +243,212 @@ const siteLabel = "사이트 보기";
}
+
+
+
diff --git a/apps/site/src/styles/base.css b/apps/site/src/styles/base.css
index 52724f8..323b4ee 100644
--- a/apps/site/src/styles/base.css
+++ b/apps/site/src/styles/base.css
@@ -7,10 +7,21 @@
*::before,
*::after { box-sizing: border-box; }
+img,
+picture,
+video,
+canvas,
+svg {
+ max-width: 100%;
+ height: auto;
+ display: block;
+}
+
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
background: var(--surface);
+ overflow-x: hidden;
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
diff --git a/apps/site/tools/.diag3.mjs b/apps/site/tools/.diag3.mjs
deleted file mode 100644
index f3da0db..0000000
--- a/apps/site/tools/.diag3.mjs
+++ /dev/null
@@ -1,36 +0,0 @@
-import puppeteer from "puppeteer-core";
-import path from "node:path";
-import url from "node:url";
-
-const here = path.dirname(url.fileURLToPath(import.meta.url));
-const CHROME = "C:/Program Files/Google/Chrome/Application/chrome.exe";
-
-const browser = await puppeteer.launch({ executablePath: CHROME, headless: "new", args: ["--force-device-scale-factor=1"] });
-const page = await browser.newPage();
-await page.setCacheEnabled(false);
-
-// 라이브 배포 URL — 사용자가 보는 그대로
-for (const w of [360, 390, 412]) {
- await page.setViewport({ width: w, height: 844, isMobile: true, hasTouch: true });
- await page.goto("https://designpaca.chanpaca.net/work/dure-enrollment/", { waitUntil: "networkidle0" });
- await page.evaluate(() => document.fonts.ready);
- await new Promise((r) => setTimeout(r, 250));
- const m = await page.evaluate(() => {
- const layout = document.querySelector(".layout");
- const h2 = document.querySelector("#cat-h");
- const card = document.querySelector(".course-row");
- const appbar = document.querySelector(".appbar-inner");
- const rl = (el) => (el ? Math.round(el.getBoundingClientRect().left) : null);
- return {
- cssV: (document.querySelector('link[rel="stylesheet"]')?.getAttribute("href") || "").match(/v=\d+/)?.[0],
- layoutPad: getComputedStyle(layout).padding,
- h2Left: rl(h2),
- cardLeft: rl(card),
- appbarPadLeft: getComputedStyle(appbar).paddingLeft,
- over: document.documentElement.scrollWidth - document.documentElement.clientWidth,
- };
- });
- console.log(`[${w}] css=${m.cssV} layoutPad="${m.layoutPad}" h2Left=${m.h2Left} cardLeft=${m.cardLeft} appbarPadL=${m.appbarPadLeft} over=+${m.over}`);
- if (w === 390) await page.screenshot({ path: path.join(here, "__shots__", "diag3-dure-live-390.png") });
-}
-await browser.close();
diff --git a/apps/site/tools/__scan.mjs b/apps/site/tools/__scan.mjs
deleted file mode 100644
index e9a7e9c..0000000
--- a/apps/site/tools/__scan.mjs
+++ /dev/null
@@ -1,30 +0,0 @@
-// 임시 구문 스캐너 — 괄호·템플릿 리터럴 균형 줄 단위 추적
-import fs from "node:fs";
-const src = fs.readFileSync(process.argv[2], "utf8");
-let inTpl = false, inStr = null, inInterp = 0, line = 1;
-const stack = [];
-const push = (t) => stack.push(t + "@" + line);
-for (let i = 0; i < src.length; i++) {
- const c = src[i], n = src[i + 1];
- if (c === "\n") line++;
- if (inStr) {
- if (c === "\\") { i++; continue; }
- if (c === inStr) inStr = null;
- continue;
- }
- if (inTpl) {
- if (c === "\\") { i++; continue; }
- if (c === "$" && n === "{") { push("${"); inInterp++; i++; continue; }
- if (c === "`") { inTpl = false; const m = stack.pop(); }
- continue;
- }
- if (c === "`") { inTpl = true; push("tpl"); continue; }
- if (c === "'" || c === '"') { inStr = c; continue; }
- if (c === "/" && n === "/") { while (i < src.length && src[i] !== "\n") i++; continue; }
- if (c === "(" || c === "[" || c === "{") push(c);
- else if (c === ")" || c === "]" || c === "}") {
- const m = stack.pop();
- if (!m) { console.log("여분의 닫기", c, "줄", line); process.exit(0); }
- }
-}
-console.log("미닫힘:", stack.slice(-8).join(", ") || "없음");
diff --git a/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png b/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png
index ccec4a3..81d2548 100644
Binary files a/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png and b/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png differ
diff --git a/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png b/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png
index 1107468..7e03748 100644
Binary files a/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png and b/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png differ
diff --git a/apps/site/tools/e2e-report.md b/apps/site/tools/e2e-report.md
index 62d1d2a..8311087 100644
--- a/apps/site/tools/e2e-report.md
+++ b/apps/site/tools/e2e-report.md
@@ -1,4 +1,4 @@
-# E2E 시나리오 게이트 — 2026-08-23 06:30
+# E2E 시나리오 게이트 — 2026-08-23 12:48
단언 1805 · 통과 1805 · 실패 0 — **GREEN — 전 단언 통과**
diff --git a/apps/site/tools/verify-report.md b/apps/site/tools/verify-report.md
index 5763c46..6632a77 100644
--- a/apps/site/tools/verify-report.md
+++ b/apps/site/tools/verify-report.md
@@ -1,15 +1,15 @@
-# 검증 리포트 — 2026-08-23 06:30
+# 검증 리포트 — 2026-08-23 12:48
-게이트: **전 계층 통과 — 배포 가능** · 총 103.6초
+게이트: **전 계층 통과 — 배포 가능** · 총 96.5초
| 계층 | 결과 | 항목 | 소요 |
|---|---|---|---|
-| L0-정적/stylelint | PASS | PASS — | 8.0s |
+| L0-정적/stylelint | PASS | PASS — | 1.6s |
| L0-정식/html-validate | PASS | PASS — | 1.3s |
| L1-단위/계산로직 | PASS | PASS 41 | 0.1s |
-| L2-기능/감사 | PASS | PASS 72 | 9.7s |
-| L2-불변식/렌더 | PASS | PASS 10 | 17.2s |
-| L3-시각회귀/diff | PASS | PASS 30 | 11.7s |
-| L4-WebKit | PASS | PASS 8 | 3.9s |
-| L5-탐색/키보드 | PASS | PASS 14 | 6.3s |
-| L6-시나리오/E2E | PASS | PASS — | 45.4s |
+| L2-기능/감사 | PASS | PASS 72 | 10.2s |
+| L2-불변식/렌더 | PASS | PASS 10 | 17.5s |
+| L3-시각회귀/diff | PASS | PASS 30 | 12.2s |
+| L4-WebKit | PASS | PASS 8 | 4.7s |
+| L5-탐색/키보드 | PASS | PASS 14 | 6.4s |
+| L6-시나리오/E2E | PASS | PASS — | 42.6s |
diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md
index c262acb..a052e38 100644
--- a/packages/cli/CHANGELOG.md
+++ b/packages/cli/CHANGELOG.md
@@ -1,5 +1,14 @@
# designpaca
+## 0.9.1
+
+### Patch Changes
+
+- CSS 변수 및 토큰 안전 폴백 의무화 (`var(--step-4, clamp(...))`) — 토큰 누락 시 16px 축소 및 위계 역전 방지
+- 서브페이지 및 에셋 경로 무결성 게이트 — 404 브로큰 링크 차단 및 안전 리다이렉트 동봉
+- 다단 데이터 테이블 1120px 컨테이너 핏 및 테마 맞춤 앰버/다크 커스텀 스크롤바 표준화
+- 완료 선언 전 헤드리스 렌더링 실측 게이트 (`getComputedStyle` 폰트/패딩/오버플로) 강화
+
## 0.9.0
### Minor Changes
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 51657fe..09d9769 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "designpaca",
- "version": "0.9.0",
+ "version": "0.9.1",
"description": "웹 디자인 파이프라인 스킬 — Claude Code · Codex · Cursor 에 한 줄로 설치한다",
"keywords": [
"design",
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 6969ba2..3487a38 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,11 @@
# @designpaca/core
+## 0.9.1
+
+### Patch Changes
+
+- Internal core engine synchronization for 0.9.1 release.
+
## 0.9.0
### Minor Changes
diff --git a/packages/core/package.json b/packages/core/package.json
index 709a4a0..9bf21a6 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@designpaca/core",
- "version": "0.9.0",
+ "version": "0.9.1",
"private": true,
"description": "designpaca 설치 엔진 — 타깃 어댑터, 매니페스트, 드리프트 감지",
"type": "module",
diff --git a/packages/skill/CHANGELOG.md b/packages/skill/CHANGELOG.md
index f59acec..ab21a5e 100644
--- a/packages/skill/CHANGELOG.md
+++ b/packages/skill/CHANGELOG.md
@@ -1,5 +1,14 @@
# CHANGELOG
+## 0.9.1
+
+### Patch Changes
+
+- CSS 변수 및 토큰 안전 폴백 의무화 (`var(--step-4, clamp(...))`) — 토큰 누락 시 16px 축소 및 위계 역전 방지
+- 서브페이지 및 에셋 경로 무결성 게이트 — 404 브로큰 링크 차단 및 안전 리다이렉트 동봉
+- 다단 데이터 테이블 1120px 컨테이너 핏 및 테마 맞춤 앰버/다크 커스텀 스크롤바 표준화
+- 완료 선언 전 헤드리스 렌더링 실측 게이트 (`getComputedStyle` 폰트/패딩/오버플로) 강화
+
## 0.9.0
### Minor Changes
diff --git a/packages/skill/package.json b/packages/skill/package.json
index 474ad03..b8043d9 100644
--- a/packages/skill/package.json
+++ b/packages/skill/package.json
@@ -1,6 +1,6 @@
{
"name": "@designpaca/skill",
- "version": "0.9.0",
+ "version": "0.9.1",
"private": true,
"description": "designpaca 스킬 원본 — SKILL.md 와 참조 문서",
"scripts": {