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 (樽室) + + + + + + + + + + + + + + +
+
+ + 樽 + 캐스크 아카이브 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
+
+
+ 소요 시간: 45분 + 코스 A 예약하기 +
+
+ + +
+
+
+ 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
+
+
+ 소요 시간: 75분 · 페어링 포함 + 코스 B 예약하기 +
+
+
+
+ + +
+
+ 도슨트 마스터클래스 5종 단일 캐스크 위스키 라인업과 테이스팅 카드 +
+
+ + +
+
+
+
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 · 樽室) · 독립병입자 위스키바 & 도슨트 큐레이션 + + + + + + + + + + + + + + + + + + + + +
+
+ + 樽 캐스크 아카이브 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)에서 추출되어 병입 수량이 한정되어 있습니다. 필터로 원하는 향미 스펙을 검색하십시오.

+
+ +
+
+ + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
캐스크 번호증류소 & 원액 명칭독립병입자 (Bottler)숙성 배럴 / 년수도수 (ABV)한정 수량향미 노트 요약인증 정보
#6512Clynelish 1996
하이랜드 · 왁시 꿀 & 린덴
Signatory Vintage1st Fill Sherry Butt · 24년54.2%180 Btls말린 무화과, 밀랍, 시나몬, 다크초콜릿
#29.281Laphroaig 2005
아일라 · 약용 피트 & 해풍
SMWS (Society)1st Fill Ex-Bourbon · 15년57.8%214 Btls모닥불 연기, 바다 소금, 레몬 제스트, 피트 45ppm
#93/41Springbank 1993
캠벨타운 · 미네랄 & 오일
Gordon & MacPhailRefill Sherry Hogshead · 27년51.6%142 Btls열대 과일 잼, 가죽, 부드러운 유황, 정향
#PX-88Caol Ila 2008
아일라 · 달콤한 훈연
Cadenhead'sPedro Ximenez Finish · 14년56.4%260 Btls스모크 베이컨, 건포도, 블랙베리, 훈제 파프리카
#98/12Ben Nevis 1998
서부 하이랜드 · 묵직한 바디
Adelphi DistilleryEx-Bourbon Cask · 22년58.1%195 Btls바닐라 커스터드, 구운 코코넛, 청사과, 후추
+
+
+
+ + +
+
+
+

1:1 맞춤형 위스키 도슨트 테이스팅

+

공인 위스키 마스터가 당신의 향미 선호도를 분석하여 독립병입자 캐스크를 1:1로 해설하고 페어링합니다.

+
+ +
+ +
+
+
+

코스 A. 독립병입자 입문 탐색 플라이트 (3잔)

+ 65,000원 +
+

+ 증류소 오피셜 보틀과 동일 증류소의 독립병입자 싱글 캐스크를 1:1로 비교하며 배럴 숙성의 차이를 체감하는 코스. +

+
소요 시간: 45분 · 도슨트 1:1 배정 · 하이엔드 글렌캐런 글라스 제공
+
+ +
+
+

코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (5잔)

+ 120,000원 +
+

+ 1990년대 올드 빈티지 및 55% 이상의 싱글 캐스크 스트렝스 5종을 테이스팅 휠과 함께 정밀 분석하는 심화 코스. +

+
소요 시간: 75분 · 아티잔 초콜릿 & 샤퀴테리 페어링 포함
+
+
+ + +
+

+ 도슨트 프라이빗 세션 예약 신청 +

+
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ + +
+
+
+
+
+ + +
+
+
+

프라이빗 시음회 & 릴리즈 이벤트

+

월별 한정 입고되는 독립병입자 캐스크 오프닝 파티 및 정기 세미나 일정입니다.

+
+ +
+
+
+ 09.12 + FRI 20:00 +
+
+

+ 1990s 올드 셰리 버트 4종 버티컬 시음회 +

+

+ Signatory & G&M 90년대 빈티지 셰리 캐스크 4종 비교 테이스팅 (정원 8인 한정) +

+
+
+ +
+
+ 09.26 + SAT 21:00 +
+
+

+ 아일라 옥토모어 & 피트 심야 도슨트 나이트 +

+

+ 페놀 수치 80ppm 이상의 극한 피트 독립병입자 바틀 블라인드 테이스팅 +

+
+
+
+ + + +
+
+
+ + + +
+

+ 공식 독립병입자 수입 보증서 +

+ +
+
+
증류소 및 보틀:
+
독립병입자:
+
숙성 정보:
+
알코올 도수:
+
통관 필증 번호:
+
+
+ +
+
+ + + + + + + + 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) 인증 친환경 세제로 새집증후군, 미세 분진, 알레르기 유발 물질을 근본적으로 제거합니다. +

+
+ + 실시간 정찰제 견적 계산하기 + + +
+ ★★★★★ + 누적 케어 4,820세대 · 만족도 99.4% +
+
+
+ +
+
+
+ 정밀 입주 케어가 완료된 미니멀한 프리미엄 주거 거실 전경 +
+
+
+ 32 Point + 마스터 클리너
정밀 검수 체크리스트
+
+
+
+
+ + +
+
+
+

투명한 정찰제 실시간 견적 산정

+

추가금 없는 명확한 기준. 공간 면적과 맞춤 옵션을 선택하시면 즉시 예상 금액이 산출됩니다.

+
+ +
+
+
+ +
+ +
+ + + +
+
+ + +
+
+ + 32 평 (약 105㎡) +
+ +
+ + +
+ 정밀 특수 옵션 +
+ + + + +
+
+ + +
+ 신청자 정보 +
+ + +
+ +
+
+
+ + +
+

실시간 견적 명세서

+
+ 선택 공간 + 주거 입주/이사 +
+
+ 면적 기준 + 32평 (평당 11,000원) +
+
+ 기본 케어비 + 352,000원 +
+
+ 정밀 옵션 합계 + 80,000원 +
+
+ 총 예상 견적 + 432,000원 +
+ + +

+ ✓ 접수 즉시 배차 팀장이 연락 후 사전 현장 조율을 진행합니다. +

+
+
+
+
+ + +
+
+
+
+

임상 수준의 6단계 청온 케어 프로토콜

+

표면만 닦는 청소가 아닌, 미세 분진과 세균 번식의 원인을 제거하는 체계적인 과정입니다.

+
+ + 나인원한남 ATP 측정 리포트 보기 → + +
+ +
+
+
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 완결 보증 +
+
+
+
+
+ + +
+
+
+
OPERATIONAL CONTROL CENTER
+

실시간 배차 및 현장 관제 현황

+
+ +
+ + +
+
+
오늘 신규 접수
+
6건
+
+
+
현재 현장 진행 중
+
4개 조
+
+
+
검수 대기 / 완료
+
2건
+
+
+
오늘 견적 총액
+
2,480,000원
+
+
+ + +
+
+ 실시간 예약 및 케어 원장 (Live Ledger) +
+ + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
접수번호고객명 / 연락처공간 / 면적현장 주소견적액배정 팀장상태조치
#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단계 정밀 프로토콜 시공 완료 +
+
+
+
+ + +
+
+
+ ATP 생체 오염도 측정기 계측 화면 (12 RLU) +
+
+
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 대조하고, 모바일 리포트를 발행하여 최종 승인합니다. +

+
+
+
+
+ + +
+
+ 독일 카처 4.0bar 정밀 스팀 위생 장비와 멸균 키트 +
+
+ + +
+
+
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) · 하이엔드 주거 & 상업 공간 인테리어 건축 + + + + + + + + + + + + + + + + + + + + +
+
+ + 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년 경력의 석재·목공 마스터 장인팀이 밀리미터 단위의 오차 없는 시공을 완성합니다.

+
+
+
+
+ + +
+
+
+
+
BESPOKE CONSULTATION
+

+ 프라이빗 공간 컨설팅 신청 +

+

+ 월 최대 3건의 프로젝트만을 엄선하여 타협 없는 완성도를 보증합니다. +

+
+ +
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ + +
+
+
+
+
+ + + +
+

+ 프로젝트 설계 아카이브 +

+ +
+
+
프로젝트명:
+
공간 면적:
+
주요 마감재:
+
+ 설계 의도:
+ +
+
+
+ +
+
+ + + + + + + + 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 + + + + + + + + + + + + + + +
+
+ + MATERIA 空間素材 + + +
+
+ + +
+ + 상담 신청 + +
+
+
+ +
+ +
+
+ + +
+

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
+
+
+
+ + +
+ 한남 듀플렉스 펜트하우스 5.8m 보이드 거실 전경과 트래버틴 벽체 +
+ Zone A · 5.8m 보이드 거실 & 20mm 로만 나보나 트래버틴 아트월 +
+
+
+
+ + +
+
+
+
+
DESIGN PHILOSOPHY
+

+ 비움으로 완성한
빛과 물성의 교감 +

+
+
+

+ 한남 듀플렉스 펜트하우스는 장식적 기교를 배제하고, 자연광의 궤적과 거대한 석재의 물성이 공간의 주인이 되도록 설계된 프로젝트입니다. + 남향으로 열린 한강 조망과 5.8미터 복층 보이드 구조를 관통하는 자연광을 수학적으로 계산하여, 시간에 따라 석재 표면의 음영이 시시각각 변화하도록 유도했습니다. +

+

+ 벽체를 감싸는 20mm 로만 나보나 트래버틴은 이탈리아 티볼리 채석장에서 원판을 엄선하여 이음매의 결을 무지향성으로 연결했으며, + 3,000년 세월 동안 탄화된 훈증 오크(Bog Oak)와 수공예 파티나 브론즈를 접합하여 시각적 깊이와 촉각적 정숙함을 부여했습니다. +

+
+
+
+
+ + + + + +
+
+
+
+ 한남 듀플렉스 펜트하우스 평면도 +
+
+
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.name}

      + + + +

      {w.kind}

      {w.concept}

      - +
      + {/* 카드 전체 클릭: 모달 상세 열기 (직접 링크는 z-index가 더 높아 독립 동작) */} -
      - {w.alt} -
      - -
      -

      {w.kind} · {w.preset}

      -

      {w.name}

      -

      {w.concept}

      - -
      - 감수한 리스크 -

      {w.risk}

      +
      + {/* 좌측: 스크린샷 뷰어 */} +
      +
      + {w.alt} +
      -
        - {w.metrics.map((m) => ( -
      • {m}
      • - ))} -
      + {/* 우측: 정보 & 상단 주 CTA (스크롤 없이 즉시 노출) */} +
      +
      +
      {w.preset}
      +

      {w.kind}

      +

      {w.name}

      +
      - - {siteLabel} + {/* 🚀 상단 주 CTA — 모달 열자마자 스크롤 0px로 즉각 클릭 가능 */} +
      + + {siteLabel} + + + {w.subLinks && w.subLinks.length > 0 && ( + + )} + 새 탭에서 라이브 데모 열기 +
      + +
      +

      {w.concept}

      + +
      + 감수한 리스크 +

      {w.risk}

      +
      + +
      + 검증 지표 +
        + {w.metrics.map((m) => ( +
      • {m}
      • + ))} +
      +
      +
      +
      +
      + + {/* 모바일 뷰포트용 스티키 바텀 독: 스크롤 중에도 언제든 엄지 영역에 고정 */} +
    + + + 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": {