release: v0.9.1 (신규 쇼케이스 3종 정식 출시, CSS 변수 및 경로 무결성 게이트 강화)
Some checks failed
ci / build (push) Failing after 37s
release / release (push) Successful in 1m30s

This commit is contained in:
Yun Chan 2026-08-23 23:17:55 +09:00
parent e26b00a825
commit 225c1d9f42
75 changed files with 7406 additions and 224 deletions

2
.gitignore vendored
View file

@ -11,3 +11,5 @@ apps/site/dist/
research/youtube/*.srt research/youtube/*.srt
research/youtube/*.vtt research/youtube/*.vtt
apps/site/tools/.gen/ apps/site/tools/.gen/
.gemini/

View file

@ -7,7 +7,8 @@
| 날짜 | 판 | 무엇이 바뀌었나 | | 날짜 | 판 | 무엇이 바뀌었나 |
|---|---|---| |---|---|---|
| 2026-08-20 | 초판 | `swiss-minimal` · three.js 미사용 · 라이트 기반 | | 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단계부터 다시 돌라**고 씌어 있다. 그것은 2단계 방향 재결정이었고, 스킬은 그럴 때 **전체 경로로 올려 1단계부터 다시 돌라**고 씌어 있다.
@ -849,3 +850,44 @@ v23 스크롤 리셋·단일 헤더에도 사용자 평가가 "여전히 깨져
- **게이트 공백 발견**: 기존 매트릭스는 오른쪽 '오버플로'만 검사하고 왼쪽 인셋의 최소값·정렬 정합은 검사하지 않았다(그래서 기준 갱신에 결함이 그대로 박혔다). e2e에 **M6 왼쪽 인셋** 스위트 신설(60단언): 전 뷰×320/390에서 텍스트 시작점 ≥12px + 헤더-제목 같은 축(±10px). RED 38 실패로 결함 재현 확인 후 수리로 GREEN. - **게이트 공백 발견**: 기존 매트릭스는 오른쪽 '오버플로'만 검사하고 왼쪽 인셋의 최소값·정렬 정합은 검사하지 않았다(그래서 기준 갱신에 결함이 그대로 박혔다). e2e에 **M6 왼쪽 인셋** 스위트 신설(60단언): 전 뷰×320/390에서 텍스트 시작점 ≥12px + 헤더-제목 같은 축(±10px). RED 38 실패로 결함 재현 확인 후 수리로 GREEN.
- 측정 하자(瑕) 2건 교정: display:none 요소의 rect=0이 잡히던 것(vis 필터), 전폭 컨테이너 보더박스가 아닌 '텍스트 시작점' 기준. - 측정 하자(瑕) 2건 교정: display:none 요소의 rect=0이 잡히던 것(vis 필터), 전폭 컨테이너 보더박스가 아닌 '텍스트 시작점' 기준.
- 시각 기준 갱신(두레 390px 6화면), verify L0~L6 전 통과(E2E 1,805단언). - 시각 기준 갱신(두레 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View file

@ -0,0 +1,459 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>1:1 위스키 도슨트 테이스팅 마스터클래스 — CASK ARCHIVE (樽室)</title>
<meta name="description" content="캐스크 아카이브의 1:1 위스키 도슨트 테이스팅 마스터클래스 안내. 코피타 글래스웨어학, 감각 아로마 휠 분석, 독립병입자 싱글 캐스크 플라이트 및 가스트로노미 페어링." />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="../styles/tokens.css" />
<link rel="stylesheet" href="../styles/main.css" />
<style>
.breadcrumb-bar {
padding-top: var(--space-4);
padding-bottom: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
border-bottom: 1px solid var(--line);
}
.breadcrumb-bar a {
color: var(--ink-muted);
text-decoration: none;
transition: color 0.15s ease;
}
.breadcrumb-bar a:hover {
color: var(--accent);
}
.breadcrumb-bar .sep {
opacity: 0.4;
}
.breadcrumb-bar .current {
color: var(--ink);
font-weight: 600;
}
.docent-hero {
padding: var(--space-7) 0 var(--space-5);
}
.docent-meta-strip {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-3);
margin-top: var(--space-5);
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-4);
}
.docent-meta-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.docent-meta-item .label {
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
text-transform: uppercase;
}
.docent-meta-item .value {
font-size: var(--step-0);
font-weight: 700;
color: var(--accent);
}
.course-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: var(--space-5);
margin-top: var(--space-5);
}
.course-deep-card {
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-5);
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--space-4);
position: relative;
overflow: hidden;
}
.course-deep-card.highlight {
border-color: var(--accent);
box-shadow: 0 0 32px rgba(217, 155, 56, 0.12);
}
.course-deep-card.highlight::before {
content: 'RECOMMENDED';
position: absolute;
top: 14px;
right: -32px;
transform: rotate(45deg);
background: var(--accent);
color: #000;
font-family: var(--font-mono);
font-size: 10px;
font-weight: 800;
padding: 4px 36px;
letter-spacing: 0.05em;
}
.lineup-table {
width: 100%;
border-collapse: collapse;
font-size: var(--step--1);
margin-top: var(--space-3);
}
.lineup-table th, .lineup-table td {
padding: 10px 12px;
text-align: left;
border-bottom: 1px solid var(--line);
}
.lineup-table th {
color: var(--ink-muted);
font-family: var(--font-mono);
font-weight: 600;
}
</style>
</head>
<body>
<!-- Header (GNB SSOT) -->
<header class="site-head">
<div class="wrap head-inner">
<a href="../index.html" class="wordmark" aria-label="캐스크 아카이브 홈">
<span class="wordmark-han" style="font-size: var(--step-0); color: var(--accent);">樽</span>
캐스크 아카이브 <span class="wordmark-han">CASK ARCHIVE</span>
</a>
<nav class="nav" aria-label="메인 내비게이션">
<a href="./index.html" class="active" style="color: var(--accent); font-weight: 600;">도슨트 마스터클래스</a>
<a href="../index.html#ledger">캐스크 원장</a>
<a href="../index.html#curator">향미 큐레이션</a>
<a href="../index.html#events">릴리즈</a>
<a href="../index.html#subscribe">뉴스레터</a>
</nav>
<div class="head-actions">
<a href="#reservation-section" class="btn">도슨트 예약</a>
</div>
</div>
</header>
<main id="main-content">
<!-- Hero Header -->
<section class="section docent-hero" style="border-top: none; padding-top: var(--space-5);">
<div class="wrap">
<div class="breadcrumb-bar" style="margin-bottom: var(--space-4); display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); letter-spacing: 0.04em;">
<a href="../index.html">CASK ARCHIVE</a>
<span class="sep">/</span>
<span class="current" style="color: var(--ink); font-weight: 600;">1:1 WHISKY DOCENT MASTERCLASS</span>
</div>
<div class="docent-hero-head">
<p class="hero-label">BESPOKE TASTING METHODOLOGY</p>
<h1 class="hero-title" style="margin-bottom: var(--space-2);">1:1 위스키 도슨트 마스터클래스</h1>
<p class="hero-lead" style="font-size: var(--step-0); color: var(--ink-muted); max-width: var(--measure); margin-bottom: var(--space-5);">
공인 위스키 마스터가 1:1로 당신의 후각과 미각 취향을 해체하고 조립하는 프라이빗 테이스팅 세션.
</p>
<dl class="docent-specs" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6);">
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Session Type</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">1:1 Private Tasting</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Location</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">Cask Room (한남 27길)</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Capacity</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">최대 2인 (동반 1인 가능)</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Glassware</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">Glencairn &amp; Copita</dd>
</div>
</dl>
</div>
<figure class="hero-photo" style="border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-card);">
<img
src="../assets/docent-room.webp"
alt="어두운 오크 바 카운터에 세팅된 코피타 테이스팅 글래스와 도슨트 노트"
width="1600"
height="900"
style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
/>
<figcaption style="padding: 10px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); border-top: 1px solid var(--line); background: var(--surface-raised);">
한남동 캐스크 룸 · 마스터 전용 1:1 테이스팅 카운터
</figcaption>
</figure>
</div>
</section>
<!-- Sensory Wheel & Flavor Science -->
<section class="wrap" style="padding-bottom: var(--space-8);">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
<div class="frame">
<img
src="../assets/aroma-wheel.webp"
alt="위스키 감각 테이스팅 아로마 휠 다이어그램"
style="width: 100%; display: block;"
/>
</div>
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
<div class="eyebrow">SENSORY ANALYSIS</div>
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
후각과 미각의 지도를 그리는<br />
<span class="accent-text">4축 아로마 분석법</span>
</h2>
<div style="display: flex; flex-direction: column; gap: var(--space-3); font-size: var(--step--1); color: var(--ink-muted); line-height: 1.7;">
<p>
<strong style="color: var(--ink);">1. Peat Smoke (피트/스모키)</strong>: 아일라 섬의 이탄 연기, 해초의 요오드(Iodine), 정향과 재(Ash)의 깊이.
</p>
<p>
<strong style="color: var(--ink);">2. Dried Fruits (건과일 & 셰리)</strong>: 올로로소/PX 캐스크에서 추출된 말린 무화과, 건포도, 다크 초콜릿의 농밀함.
</p>
<p>
<strong style="color: var(--ink);">3. Floral & Orchard (에스테르 & 과원)</strong>: 장기 발효와 구리 환류에서 비롯된 풋사과, 엘더플라워, 꿀의 산뜻함.
</p>
<p>
<strong style="color: var(--ink);">4. Charred Oak (오크 바닐린)</strong>: 1st Fill 버번 배럴의 그을린 탄닌, 토피, 시나몬 스파이스.
</p>
</div>
</div>
</div>
</section>
<!-- Course Comparison Deep Cards -->
<section class="wrap" style="padding-bottom: var(--space-8);" id="courses">
<div class="eyebrow" style="text-align: center;">CURATED FLIGHT SELECTION</div>
<h2 style="font-size: var(--step-3); font-weight: 800; text-align: center; margin-top: 6px;">
도슨트 테이스팅 정규 코스
</h2>
<div class="course-card-grid">
<!-- Course A -->
<div class="course-deep-card">
<div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<span class="eyebrow" style="color: var(--ink-muted);">COURSE A</span>
<span style="font-size: var(--step-2); font-weight: 800; color: var(--accent);">65,000원</span>
</div>
<h3 style="font-size: var(--step-1); font-weight: 700; margin-top: 8px;">
독립병입자 입문 탐색 플라이트 (3잔)
</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
증류소의 기본 공식 라인업(OB)과 독립병입자(IB) 싱글 캐스크의 결정적 차이를 체감하는 코스입니다.
</p>
<table class="lineup-table">
<thead>
<tr>
<th>순서</th>
<th>라인업 구성</th>
<th>ABV</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>하이랜드 에스테르 (클라인리쉬 14y vs IB 캐스크)</td>
<td>46.0% / 54.2%</td>
</tr>
<tr>
<td>2</td>
<td>스페이사이드 셰리 (글렌토커스 1st Fill 셰리 버트)</td>
<td>56.8% CS</td>
</tr>
<tr>
<td>3</td>
<td>아일라 헤비 피트 (라프로익 2005 SMWS 29.281)</td>
<td>57.8% CS</td>
</tr>
</tbody>
</table>
</div>
<div style="border-top: 1px solid var(--line); padding-top: var(--space-4); display: flex; justify-content: space-between; align-items: center;">
<span style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted);">소요 시간: 45분</span>
<a href="#reservation-section" class="btn-outline" style="text-decoration: none;">코스 A 예약하기</a>
</div>
</div>
<!-- Course B -->
<div class="course-deep-card highlight">
<div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<span class="eyebrow" style="color: var(--accent);">COURSE B</span>
<span style="font-size: var(--step-2); font-weight: 800; color: var(--accent);">120,000원</span>
</div>
<h3 style="font-size: var(--step-1); font-weight: 700; margin-top: 8px;">
캐스크 스트렝스 & 빈티지 마스터 플라이트 (5잔)
</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
물 한 방울 타지 않은 원액(Cask Strength)과 1990년대 올드 빈티지 캐스크 5종을 정밀 비교 테이스팅합니다.
</p>
<table class="lineup-table">
<thead>
<tr>
<th>순서</th>
<th>라인업 구성</th>
<th>ABV</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>스프링뱅크 1993 (G&amp;M 리필 셰리 호그스헤드 27y)</td>
<td>51.6% CS</td>
</tr>
<tr>
<td>2</td>
<td>모틀락 1989 (사마롤리 22y 싱글 캐스크)</td>
<td>50.4% CS</td>
</tr>
<tr>
<td>3</td>
<td>맥캘란 1993 (카덴헤드 1842 클래식 레인지)</td>
<td>54.8% CS</td>
</tr>
<tr>
<td>4</td>
<td>쿨일라 2008 (PX 피니시 14y)</td>
<td>56.4% CS</td>
</tr>
<tr>
<td>5</td>
<td>벤네비스 1998 (아델피 버번 캐스크 22y)</td>
<td>58.1% CS</td>
</tr>
</tbody>
</table>
</div>
<div style="border-top: 1px solid var(--line); padding-top: var(--space-4); display: flex; justify-content: space-between; align-items: center;">
<span style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--accent);">소요 시간: 75분 · 페어링 포함</span>
<a href="#reservation-section" class="btn-primary" style="text-decoration: none;">코스 B 예약하기</a>
</div>
</div>
</div>
</section>
<!-- Masterclass Lineup Showcase Visual -->
<div class="wrap" style="margin-bottom: var(--space-8);">
<div class="frame">
<img
src="../assets/masterclass-lineup.webp"
alt="도슨트 마스터클래스 5종 단일 캐스크 위스키 라인업과 테이스팅 카드"
style="width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover;"
/>
</div>
</div>
<!-- Gastronomy Pairing Atelier -->
<section class="wrap" style="padding-bottom: var(--space-8);">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
<div class="eyebrow">GASTRONOMY PAIRING</div>
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
향과 질감의 증폭을 위한<br />
<span class="accent-text">아티잔 푸드 페어링</span>
</h2>
<p style="font-size: var(--step--1); color: var(--ink-muted); line-height: 1.8;">
위스키의 높은 알코올 도수와 강렬한 탄닌을 부드럽게 감싸안고, 숨겨진 미세 에스테르를 끌어올리기 위해
스페인산 이베리코 하몽 베요타, 에콰도르 싱글 오리진 70% 다크 카카오 트러플, 터키산 흑무화과를 페어링 플레이트로 함께 서브합니다.
</p>
</div>
<div class="frame">
<img
src="../assets/whisky-pairing.webp"
alt="위스키 푸드 페어링 플레이트 (이베리코 하몽, 다크 초콜릿 트러플, 무화과)"
style="width: 100%; display: block;"
/>
</div>
</div>
</section>
<!-- Live Reservation Form Section -->
<section class="wrap" style="padding-bottom: var(--space-8);" id="reservation-section">
<div class="card" style="max-width: 720px; margin: 0 auto;">
<div class="eyebrow" style="color: var(--accent); margin-bottom: 4px;">RESERVATION REQUEST</div>
<h2 style="font-size: var(--step-2); font-weight: 800; margin-bottom: var(--space-4);">
도슨트 테이스팅 세션 실시간 예약
</h2>
<form onsubmit="event.preventDefault(); alert('도슨트 예약 신청이 완료되었습니다! 24시간 이내에 전담 마스터가 유선 안내를 드립니다.');" style="display: flex; flex-direction: column; gap: var(--space-4);">
<div class="form-group">
<label class="form-label" for="courseSelect">선택 코스</label>
<select class="form-input" id="courseSelect" required>
<option value="courseB">코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (120,000원)</option>
<option value="courseA">코스 A. 독립병입자 입문 탐색 플라이트 (65,000원)</option>
</select>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);">
<div class="form-group">
<label class="form-label" for="resDate">예약 일자</label>
<input type="date" class="form-input" id="resDate" value="2026-08-28" required />
</div>
<div class="form-group">
<label class="form-label" for="resTime">예약 시간대</label>
<select class="form-input" id="resTime" required>
<option>19:00 (1부)</option>
<option selected>20:30 (2부)</option>
<option>22:00 (3부 - 심야 세션)</option>
</select>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);">
<div class="form-group">
<label class="form-label" for="userName">예약자 성함</label>
<input type="text" class="form-input" id="userName" value="정재훈" required />
</div>
<div class="form-group">
<label class="form-label" for="userPhone">연락처</label>
<input type="tel" class="form-input" id="userPhone" value="010-8831-2094" required />
</div>
</div>
<div class="form-group">
<label class="form-label" for="flavorNote">평소 선호하시는 위스키 취향 (선택)</label>
<input type="text" class="form-input" id="flavorNote" placeholder="예: 아일라 피트 위스키, 셰리 버트 숙성 선호" value="셰리 버트 숙성 & 80ppm 이상 헤비 피트 선호" />
</div>
<button type="submit" class="btn-primary" style="margin-top: var(--space-2); padding: 14px; font-size: var(--step-0);">
위스키 도슨트 세션 예약 확정 →
</button>
</form>
</div>
</section>
</main>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>캐스크 아카이브 · 서울 용산구 한남대로 27길 19 지하 1층</p>
<p class="muted">매일 18:00 – 02:00 (사전 예약제)</p>
<p class="muted">bottling@caskarchive.kr</p>
</div>
</footer>
</body>
</html>

View file

@ -0,0 +1,491 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>캐스크 아카이브 (CASK ARCHIVE · 樽室) · 독립병입자 위스키바 & 도슨트 큐레이션</title>
<meta
name="description"
content="Gordon & MacPhail, Signatory, SMWS 등 독립병입자(IB) 희귀 캐스크 공식 수입 인증 위스키바. 캐스크 넘버 원장과 1:1 위스키 도슨트 테이스팅 코스 예약."
/>
<meta property="og:title" content="캐스크 아카이브 (CASK ARCHIVE) · 독립병입자 위스키바" />
<meta property="og:description" content="시간과 배럴이 빚어낸 오직 단 하나의 원액. 공식 수입 인증 독립병입자 전문 아카이브." />
<meta property="og:type" content="website" />
<meta name="theme-color" content="#0b0b0d" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="./styles/main.css?v=4" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230B0B0D'/%3E%3Ccircle cx='16' cy='16' r='10' stroke='%23D99B38' stroke-width='2' fill='none'/%3E%3Cpath d='M16 8v16M8 16h16' stroke='%23D99B38' stroke-width='1.5'/%3E%3C/svg%3E"
/>
</head>
<body>
<a class="skip-link" href="#mainContent">본문으로 건너뛰기</a>
<!-- 상단 헤더 (.site-head SSOT) -->
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="./index.html">
樽 캐스크 아카이브 <span class="wordmark-han">CASK ARCHIVE</span>
</a>
<nav class="nav" aria-label="메인 메뉴">
<a href="#ledger">캐스크 원장</a>
<a href="./docent/index.html" style="color: var(--accent); font-weight: 600;">도슨트 마스터클래스</a>
<a href="#curator">향미 큐레이션</a>
<a href="#events">릴리즈</a>
<a href="#subscribe">뉴스레터</a>
</nav>
<div class="head-actions">
<a href="#docent" class="btn">
도슨트 예약
</a>
</div>
</div>
</header>
<main id="mainContent">
<!-- 히어로 섹션 -->
<section class="hero-section" aria-labelledby="heroHeading">
<div class="wrap hero-grid">
<div class="hero-content">
<div class="eyebrow-amber">
<span>OFFICIALLY CERTIFIED INDEPENDENT BOTTLER BAR</span>
</div>
<h1 id="heroHeading" class="hero-title">
시간과 배럴이 빚어낸<br />
오직 단 하나의 <span class="gold">원액(Single Cask)</span>
</h1>
<p class="hero-desc">
증류소의 기본 공식 라인업에서는 맛볼 수 없는 독립병입자(Independent Bottler)의 희귀 원액들. 공식 정식 수입 필증과 단일 캐스크 일련번호(Single Cask Number)를 보증하며, 전담 도슨트가 취향에 맞춘 위스키를 1:1로 큐레이션합니다.
</p>
<div class="hero-ctas">
<a href="#ledger" class="btn-amber">
실시간 캐스크 원장 확인
<span aria-hidden="true">↗</span>
</a>
<a href="./docent/index.html" class="btn-outline">
1:1 도슨트 마스터클래스 상세 보기
</a>
</div>
</div>
<div class="hero-visual" style="position: relative;">
<div class="bezel-outer">
<div class="hero-frame">
<img
src="./assets/whisky-hero.webp"
alt="차링된 오크 배럴 상판 위 싱글 캐스크 위스키 보틀과 테이스팅 글라스"
width="1200"
height="1600"
loading="eager"
/>
</div>
</div>
<div class="hero-cask-tag">
<div class="cask-tag-no">CASK #8821 · 58.4% ABV</div>
<div class="cask-tag-sub">Signatory Vintage 1997 · Bottle 42 of 186</div>
</div>
</div>
</div>
</section>
<!-- 공식 독립병입자 수입 인증 스트립 -->
<section class="ib-cert-strip" aria-label="독립병입자 공식 인증">
<div class="wrap cert-grid">
<div class="cert-item">
<div class="cert-name">GORDON & MACPHAIL</div>
<div class="cert-desc">1895년 설립 · 스코틀랜드 최고 권위 정식 수입</div>
</div>
<div class="cert-item">
<div class="cert-name">SIGNATORY VINTAGE</div>
<div class="cert-desc">언칠필터드 / 내추럴 컬러 캐스크 스트렝스 보증</div>
</div>
<div class="cert-item">
<div class="cert-name">THE SCOTCH MALT WHISKY SOCIETY</div>
<div class="cert-desc">SMWS 공식 파트너 베뉴 · 넘버링 큐레이션</div>
</div>
<div class="cert-item">
<div class="cert-name">CADENHEAD'S</div>
<div class="cert-desc">1842년 스코틀랜드 최고(最古) 독립병입자 라인업</div>
</div>
</div>
</section>
<!-- 캐스크 원장 (Interactive Cask Ledger) -->
<section class="ledger-section" id="ledger" aria-labelledby="ledgerHeading">
<div class="wrap">
<div class="section-head">
<h2 id="ledgerHeading" class="section-title">독립병입자 캐스크 실시간 보유 원장</h2>
<p class="section-sub">모든 병은 단일 캐스크(Single Cask)에서 추출되어 병입 수량이 한정되어 있습니다. 필터로 원하는 향미 스펙을 검색하십시오.</p>
</div>
<div class="ledger-filter-bar">
<div class="filter-btn-group" role="group" aria-label="캐스크 분류">
<button class="filter-btn is-active" data-filter="all">전체 원장 (5)</button>
<button class="filter-btn" data-filter="sherry">셰리 캐스크</button>
<button class="filter-btn" data-filter="peat">피트 / 스모키</button>
<button class="filter-btn" data-filter="bourbon">버번 배럴</button>
<button class="filter-btn" data-filter="cs">캐스크 스트렝스 (50%+)</button>
</div>
<input
type="search"
id="caskSearch"
class="search-input"
placeholder="증류소명 or 캐스크 번호 검색..."
aria-label="캐스크 검색"
/>
</div>
<div class="ledger-table-wrap">
<table class="ledger-table">
<thead>
<tr>
<th>캐스크 번호</th>
<th>증류소 & 원액 명칭</th>
<th>독립병입자 (Bottler)</th>
<th>숙성 배럴 / 년수</th>
<th>도수 (ABV)</th>
<th>한정 수량</th>
<th>향미 노트 요약</th>
<th>인증 정보</th>
</tr>
</thead>
<tbody id="caskTableBody">
<tr data-cat="sherry cs">
<td class="mono cask-badge">#6512</td>
<td><strong>Clynelish 1996</strong><br /><span style="color:var(--ink-muted);font-size:11px;">하이랜드 · 왁시 꿀 & 린덴</span></td>
<td>Signatory Vintage</td>
<td>1st Fill Sherry Butt · 24년</td>
<td class="mono" style="color:var(--accent);font-weight:700;">54.2%</td>
<td class="mono">180 Btls</td>
<td>말린 무화과, 밀랍, 시나몬, 다크초콜릿</td>
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Clynelish 1996 #6512', 'Signatory Vintage', '24 Years (1996 Distilled / 2020 Bottled)', '54.2% Cask Strength', '식약처 정식통관 제2024-IB-0982호');">수입인증서</button></td>
</tr>
<tr data-cat="peat cs">
<td class="mono cask-badge">#29.281</td>
<td><strong>Laphroaig 2005</strong><br /><span style="color:var(--ink-muted);font-size:11px;">아일라 · 약용 피트 & 해풍</span></td>
<td>SMWS (Society)</td>
<td>1st Fill Ex-Bourbon · 15년</td>
<td class="mono" style="color:var(--accent);font-weight:700;">57.8%</td>
<td class="mono">214 Btls</td>
<td>모닥불 연기, 바다 소금, 레몬 제스트, 피트 45ppm</td>
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Laphroaig 2005 (SMWS 29.281)', 'The SMWS Partner', '15 Years (2005 Distilled)', '57.8% Cask Strength', '식약처 정식통관 제2025-IB-1102호');">수입인증서</button></td>
</tr>
<tr data-cat="sherry">
<td class="mono cask-badge">#93/41</td>
<td><strong>Springbank 1993</strong><br /><span style="color:var(--ink-muted);font-size:11px;">캠벨타운 · 미네랄 & 오일</span></td>
<td>Gordon & MacPhail</td>
<td>Refill Sherry Hogshead · 27년</td>
<td class="mono" style="color:var(--accent);font-weight:700;">51.6%</td>
<td class="mono">142 Btls</td>
<td>열대 과일 잼, 가죽, 부드러운 유황, 정향</td>
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Springbank 1993 #93/41', 'Gordon & MacPhail Reserve', '27 Years (1993 Distilled)', '51.6%', '식약처 정식통관 제2024-IB-0419호');">수입인증서</button></td>
</tr>
<tr data-cat="peat cs">
<td class="mono cask-badge">#PX-88</td>
<td><strong>Caol Ila 2008</strong><br /><span style="color:var(--ink-muted);font-size:11px;">아일라 · 달콤한 훈연</span></td>
<td>Cadenhead's</td>
<td>Pedro Ximenez Finish · 14년</td>
<td class="mono" style="color:var(--accent);font-weight:700;">56.4%</td>
<td class="mono">260 Btls</td>
<td>스모크 베이컨, 건포도, 블랙베리, 훈제 파프리카</td>
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Caol Ila 2008 #PX-88', 'Cadenheads Authentic Collection', '14 Years', '56.4% Cask Strength', '식약처 정식통관 제2025-IB-0831호');">수입인증서</button></td>
</tr>
<tr data-cat="bourbon cs">
<td class="mono cask-badge">#98/12</td>
<td><strong>Ben Nevis 1998</strong><br /><span style="color:var(--ink-muted);font-size:11px;">서부 하이랜드 · 묵직한 바디</span></td>
<td>Adelphi Distillery</td>
<td>Ex-Bourbon Cask · 22년</td>
<td class="mono" style="color:var(--accent);font-weight:700;">58.1%</td>
<td class="mono">195 Btls</td>
<td>바닐라 커스터드, 구운 코코넛, 청사과, 후추</td>
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Ben Nevis 1998 #98/12', 'Adelphi Selection', '22 Years', '58.1%', '식약처 정식통관 제2024-IB-0771호');">수입인증서</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 1:1 위스키 도슨트 테이스팅 코스 예약 -->
<section class="docent-section" id="docent" aria-labelledby="docentHeading">
<div class="wrap">
<div class="section-head">
<h2 id="docentHeading" class="section-title">1:1 맞춤형 위스키 도슨트 테이스팅</h2>
<p class="section-sub">공인 위스키 마스터가 당신의 향미 선호도를 분석하여 독립병입자 캐스크를 1:1로 해설하고 페어링합니다.</p>
</div>
<div class="docent-grid">
<!-- 코스 설명 카드 -->
<div class="course-list">
<div class="course-card">
<div class="course-header">
<h3 class="course-title">코스 A. 독립병입자 입문 탐색 플라이트 (3잔)</h3>
<span class="course-price">65,000원</span>
</div>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 12px;">
증류소 오피셜 보틀과 동일 증류소의 독립병입자 싱글 캐스크를 1:1로 비교하며 배럴 숙성의 차이를 체감하는 코스.
</p>
<div style="font-size: 12px; color: var(--accent);" class="mono">소요 시간: 45분 · 도슨트 1:1 배정 · 하이엔드 글렌캐런 글라스 제공</div>
</div>
<div class="course-card">
<div class="course-header">
<h3 class="course-title">코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (5잔)</h3>
<span class="course-price">120,000원</span>
</div>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 12px;">
1990년대 올드 빈티지 및 55% 이상의 싱글 캐스크 스트렝스 5종을 테이스팅 휠과 함께 정밀 분석하는 심화 코스.
</p>
<div style="font-size: 12px; color: var(--accent);" class="mono">소요 시간: 75분 · 아티잔 초콜릿 & 샤퀴테리 페어링 포함</div>
</div>
</div>
<!-- 예약 입력 폼 -->
<div class="docent-booking-form">
<h3 style="font-family: var(--font-serif); font-size: var(--step-1); margin-bottom: var(--space-4); color: var(--ink);">
도슨트 프라이빗 세션 예약 신청
</h3>
<form id="docentForm" onsubmit="return false;">
<div class="form-group">
<label class="form-label" for="bookingCourse">선택 코스</label>
<select id="bookingCourse" class="form-select">
<option value="A">코스 A. 독립병입자 입문 탐색 플라이트 (65,000원)</option>
<option value="B" selected>코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (120,000원)</option>
</select>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">
<div class="form-group">
<label class="form-label" for="bookingDate">예약 일자</label>
<input type="date" id="bookingDate" class="form-input" value="2026-08-28" />
</div>
<div class="form-group">
<label class="form-label" for="bookingTime">예약 시간대</label>
<select id="bookingTime" class="form-select">
<option>19:00 (1부)</option>
<option selected>20:30 (2부)</option>
<option>22:00 (3부 심야)</option>
</select>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">
<div class="form-group">
<label class="form-label" for="guestName">예약자 성함</label>
<input type="text" id="guestName" class="form-input" placeholder="홍길동" value="정재훈" required />
</div>
<div class="form-group">
<label class="form-label" for="guestPhone">연락처</label>
<input type="tel" id="guestPhone" class="form-input" placeholder="010-0000-0000" value="010-8831-2094" required />
</div>
</div>
<div class="form-group">
<label class="form-label" for="guestPref">향미 선호도 메모</label>
<input type="text" id="guestPref" class="form-input" placeholder="예: 셰리 밤 선호, 스모키 피트 선호 등" value="강렬한 셰리 버트와 헤비 피트 선호" />
</div>
<button type="button" class="btn-amber" id="btnBookDocent" style="width: 100%; padding: 14px; margin-top: 10px;">
위스키 도슨트 세션 예약 확정
</button>
</form>
</div>
</div>
</div>
</section>
<!-- 이벤트 & 뉴스레터 & 인스타그램 -->
<section class="events-section" id="events" aria-labelledby="eventHeading">
<div class="wrap">
<div class="section-head">
<h2 id="eventHeading" class="section-title">프라이빗 시음회 & 릴리즈 이벤트</h2>
<p class="section-sub">월별 한정 입고되는 독립병입자 캐스크 오프닝 파티 및 정기 세미나 일정입니다.</p>
</div>
<div class="event-grid">
<div class="event-item">
<div class="event-date-box">
<span class="event-day">09.12</span>
<span class="event-month">FRI 20:00</span>
</div>
<div>
<h3 style="font-size: var(--step-0); font-weight: 700; color: var(--ink); margin-bottom: 4px;">
1990s 올드 셰리 버트 4종 버티컬 시음회
</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted);">
Signatory &amp; G&amp;M 90년대 빈티지 셰리 캐스크 4종 비교 테이스팅 (정원 8인 한정)
</p>
</div>
</div>
<div class="event-item">
<div class="event-date-box">
<span class="event-day">09.26</span>
<span class="event-month">SAT 21:00</span>
</div>
<div>
<h3 style="font-size: var(--step-0); font-weight: 700; color: var(--ink); margin-bottom: 4px;">
아일라 옥토모어 & 피트 심야 도슨트 나이트
</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted);">
페놀 수치 80ppm 이상의 극한 피트 독립병입자 바틀 블라인드 테이스팅
</p>
</div>
</div>
</div>
<!-- 뉴스레터 & 인스타그램 카드 -->
<div class="newsletter-card" id="subscribe">
<div class="eyebrow-amber">EXCLUSIVE CASK NOTIFICATION</div>
<h3 style="font-family: var(--font-serif); font-size: var(--step-2); margin-block: 8px 12px; color: var(--ink);">
신규 캐스크 입고 알림 & 인스타그램
</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); max-width: 500px; margin-inline: auto;">
수량이 극소량인 독립병입자 캐스크 오프닝 소식을 이메일 뉴스레터로 가장 먼저 발송해 드립니다.
</p>
<form class="newsletter-form" onsubmit="return false;">
<input
type="email"
id="newsEmail"
class="form-input"
placeholder="귀하의 이메일 주소를 입력하세요"
required
/>
<button type="button" class="btn-amber" id="btnSubscribe">
구독 신청
</button>
</form>
<div style="margin-top: var(--space-5); display: inline-flex; align-items: center; gap: 8px;">
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" class="btn-outline" style="font-size: 12px; padding: 6px 16px;">
📸 인스타그램 @caskarchive.official 바로가기 ↗
</a>
</div>
</div>
</div>
</section>
</main>
<!-- 수입 인증서 모달 다이얼로그 -->
<dialog id="certModal" class="cert-dialog" aria-labelledby="certTitle">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; border-bottom: 1px solid var(--line); padding-bottom: 0.75rem;">
<h3 id="certTitle" style="font-family: var(--font-serif); font-size: var(--step-1); color: var(--accent);">
공식 독립병입자 수입 보증서
</h3>
<button type="button" onclick="document.getElementById('certModal').close();" aria-label="닫기" style="font-size: 18px; font-weight: 700; color: var(--ink-muted);">✕</button>
</div>
<div style="display: grid; gap: 0.75rem; font-size: var(--step--1); line-height: 1.6;">
<div><strong>증류소 및 보틀:</strong> <span id="mWhiskyName" class="mono" style="color:var(--ink);"></span></div>
<div><strong>독립병입자:</strong> <span id="mBottler" class="mono" style="color:var(--ink);"></span></div>
<div><strong>숙성 정보:</strong> <span id="mAge" class="mono" style="color:var(--ink);"></span></div>
<div><strong>알코올 도수:</strong> <span id="mAbv" class="mono" style="color:var(--accent);"></span></div>
<div><strong>통관 필증 번호:</strong> <span id="mCustoms" class="mono" style="color:#34d399;"></span></div>
</div>
<div style="margin-top: 1.5rem; text-align: right;">
<button type="button" class="btn-amber" style="padding: 6px 18px; font-size: 13px;" onclick="document.getElementById('certModal').close();">확인 완료</button>
</div>
</dialog>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>캐스크 아카이브 · 서울 용산구 한남대로 27길 19 지하 1층</p>
<p class="muted">매일 18:00 – 02:00 (사전 예약제)</p>
<p class="muted">bottling@caskarchive.kr</p>
</div>
</footer>
<!-- 인터랙티브 스크립트 -->
<script>
(function () {
// 1. 캐스크 원장 검색 및 필터링
const filterBtns = document.querySelectorAll(".filter-btn");
const searchInput = document.getElementById("caskSearch");
const rows = document.querySelectorAll("#caskTableBody tr");
function filterLedger() {
const activeFilter = document.querySelector(".filter-btn.is-active")?.getAttribute("data-filter") || "all";
const query = searchInput.value.toLowerCase().trim();
rows.forEach((row) => {
const cat = row.getAttribute("data-cat") || "";
const text = row.textContent.toLowerCase();
const matchCat = activeFilter === "all" || cat.includes(activeFilter);
const matchQuery = !query || text.includes(query);
row.style.display = matchCat && matchQuery ? "" : "none";
});
}
filterBtns.forEach((btn) => {
btn.addEventListener("click", () => {
filterBtns.forEach((b) => b.classList.remove("is-active"));
btn.classList.add("is-active");
filterLedger();
});
});
if (searchInput) {
searchInput.addEventListener("input", filterLedger);
}
// 2. 도슨트 예약 버튼
const btnBook = document.getElementById("btnBookDocent");
if (btnBook) {
btnBook.addEventListener("click", () => {
const name = document.getElementById("guestName").value || "고객님";
const date = document.getElementById("bookingDate").value;
const time = document.getElementById("bookingTime").value;
const course = document.getElementById("bookingCourse").value;
alert(`[캐스크 아카이브] ${name}님의 1:1 위스키 도슨트 세션(코스 ${course})이 예약 접수되었습니다.\n일정: ${date} ${time}\n담당 도슨트가 당일 사전 안내 연락을 드립니다.`);
});
}
// 3. 뉴스레터 구독
const btnSub = document.getElementById("btnSubscribe");
if (btnSub) {
btnSub.addEventListener("click", () => {
const email = document.getElementById("newsEmail").value;
if (!email || !email.includes("@")) {
alert("유효한 이메일 주소를 입력해 주십시오.");
return;
}
alert(`[캐스크 아카이브] ${email} 주소로 신규 독립병입자 캐스크 릴리즈 알림이 등록되었습니다.`);
document.getElementById("newsEmail").value = "";
});
}
})();
// 인증서 모달 팝업 함수
function openCertModal(name, bottler, age, abv, customs) {
document.getElementById("mWhiskyName").textContent = name;
document.getElementById("mBottler").textContent = bottler;
document.getElementById("mAge").textContent = age;
document.getElementById("mAbv").textContent = abv;
document.getElementById("mCustoms").textContent = customs;
document.getElementById("certModal").showModal();
}
</script>
</body>
</html>

View file

@ -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;
}
}

View file

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View file

@ -0,0 +1,608 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>청온 케어 (淸溫) · 프리미엄 주거·오피스 전문 위생 케어 & 관제 콘솔</title>
<meta
name="description"
content="임상 수준의 6단계 위생 프로토콜과 100% 정찰제 견적. 독일 카처 4.0bar 고온스팀과 에코서트 인증 천연 세제. 실시간 견적 신청 및 관제 콘솔 지원."
/>
<meta property="og:title" content="청온 케어 (淸溫) · 프리미엄 위생 케어" />
<meta property="og:description" content="공간의 숨결을 되돌리는 6단계 프로토콜과 투명한 정찰제 견적." />
<meta property="og:type" content="website" />
<meta name="theme-color" content="#f8faf9" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="./styles/main.css?v=1" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230D9488'/%3E%3Cpath d='M9 16.5l5 5 9-10' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E"
/>
</head>
<body data-view="client">
<a class="skip-link" href="#mainContent">본문으로 건너뛰기</a>
<!-- 최상단 헤더 (.site-head SSOT) -->
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="#" id="brandLogo">
<span class="wordmark-badge">淸</span>
청온 케어 <span class="wordmark-han">淸溫</span>
</a>
<nav class="nav" aria-label="메인 메뉴">
<a href="#services">케어 솔루션</a>
<a href="#calculator">정찰제 견적</a>
<a href="./protocol/index.html" style="color: var(--accent); font-weight: 600;">6단계 프로토콜 (상세)</a>
<a href="#equipment">전문 장비</a>
</nav>
<div class="head-actions">
<div class="mode-toggle" role="group" aria-label="뷰 모드 전환">
<button type="button" class="mode-btn is-active" id="btnModeClient">
고객 소개 뷰
</button>
<button type="button" class="mode-btn" id="btnModeAdmin" title="단축키: Alt + A">
<span class="badge-dot" aria-hidden="true"></span>
관리자 콘솔
</button>
</div>
<a href="#calculator" class="btn client-only-btn">
간편 견적
</a>
</div>
</div>
</header>
<main id="mainContent">
<!-- ===================================================================
1. 고객 소개 및 간편 신청 뷰 (Client View)
=================================================================== -->
<div class="client-view">
<!-- 히어로 섹션 -->
<section class="hero-section" aria-labelledby="heroHeading">
<div class="wrap hero-grid">
<div class="hero-content">
<div class="eyebrow">
<span>HOSPITAL-GRADE HYGIENE PROTOCOL</span>
</div>
<h1 id="heroHeading" class="hero-title">
보이지 않는 미세 오염까지,<br />
<em>공간의 숨결</em>을 되돌립니다
</h1>
<p class="hero-desc">
단순한 미화가 아닙니다. 독일 카처 4.0bar 고온 스팀과 에코서트(ECOCERT) 인증 친환경 세제로 새집증후군, 미세 분진, 알레르기 유발 물질을 근본적으로 제거합니다.
</p>
<div class="hero-ctas">
<a href="#calculator" class="btn-primary">
실시간 정찰제 견적 계산하기
<span class="btn-icon-circle" aria-hidden="true">↗</span>
</a>
<div class="hero-trust">
<span class="hero-trust-stars">★★★★★</span>
<span>누적 케어 4,820세대 · 만족도 99.4%</span>
</div>
</div>
</div>
<div class="hero-visual">
<div class="bezel-outer">
<div class="hero-frame">
<img
src="./assets/clean-hero.webp"
alt="정밀 입주 케어가 완료된 미니멀한 프리미엄 주거 거실 전경"
width="1200"
height="900"
loading="eager"
/>
</div>
</div>
<div class="hero-stat-floating">
<span class="stat-val">32 Point</span>
<span class="stat-label">마스터 클리너<br />정밀 검수 체크리스트</span>
</div>
</div>
</div>
</section>
<!-- 실시간 정찰제 견적 계산기 -->
<section class="calc-section" id="calculator" aria-labelledby="calcHeading">
<div class="wrap">
<div class="section-head">
<h2 id="calcHeading" class="section-title">투명한 정찰제 실시간 견적 산정</h2>
<p class="section-sub">추가금 없는 명확한 기준. 공간 면적과 맞춤 옵션을 선택하시면 즉시 예상 금액이 산출됩니다.</p>
</div>
<div class="calc-grid">
<div class="calc-card">
<form id="quoteForm" onsubmit="return false;">
<!-- 공간 유형 -->
<div class="form-group">
<label class="form-label" id="spaceTypeLabel">공간 유형 선택</label>
<div class="type-selector" role="radiogroup" aria-labelledby="spaceTypeLabel">
<button type="button" class="type-btn is-active" data-type="apt" data-base="11000">
주거 입주/이사
</button>
<button type="button" class="type-btn" data-type="office" data-base="9500">
오피스 정기 케어
</button>
<button type="button" class="type-btn" data-type="premium" data-base="14500">
하이엔드 준공 청소
</button>
</div>
</div>
<!-- 평수 슬라이더 -->
<div class="form-group">
<div class="slider-header">
<label class="form-label" for="pyeongSlider">공간 공급면적 (평형)</label>
<span class="slider-val" id="pyeongValue">32 평 (약 105㎡)</span>
</div>
<input
type="range"
id="pyeongSlider"
class="range-input"
min="12"
max="75"
value="32"
step="1"
/>
</div>
<!-- 특수 케어 옵션 -->
<div class="form-group">
<span class="form-label">정밀 특수 옵션</span>
<div class="addon-grid">
<label class="addon-label">
<input type="checkbox" name="addon" value="steam" data-price="45000" checked />
<span>160℃ 고온 살균 스팀 (+45,000원)</span>
</label>
<label class="addon-label">
<input type="checkbox" name="addon" value="phytoncide" data-price="35000" checked />
<span>편백 피톤치드 훈증 (+35,000원)</span>
</label>
<label class="addon-label">
<input type="checkbox" name="addon" value="aircon" data-price="80000" />
<span>시스템 에어컨 분해 (+80,000원)</span>
</label>
<label class="addon-label">
<input type="checkbox" name="addon" value="glass" data-price="50000" />
<span>외부 유리창 특수 발수 (+50,000원)</span>
</label>
</div>
</div>
<!-- 예약 정보 입력 -->
<div class="form-group" style="margin-top: 1.5rem;">
<span class="form-label">신청자 정보</span>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.75rem;">
<input
type="text"
id="clientName"
placeholder="성함 / 고객명"
style="width: 100%; min-width: 0; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
value="이서연"
required
/>
<input
type="tel"
id="clientPhone"
placeholder="연락처 (010-0000-0000)"
style="width: 100%; min-width: 0; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
value="010-4821-9930"
required
/>
</div>
<input
type="text"
id="clientAddress"
placeholder="케어 현장 주소 (예: 서울시 마포구 공덕동 120)"
style="width: 100%; min-width: 0; margin-top: 0.5rem; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
value="서울시 성동구 성수동2가 아크로포레스트 102동"
required
/>
</div>
</form>
</div>
<!-- 요약 및 접수 CTA -->
<div class="calc-summary">
<h3 class="summary-title">실시간 견적 명세서</h3>
<div class="summary-row">
<span>선택 공간</span>
<span id="sumSpaceType" class="mono">주거 입주/이사</span>
</div>
<div class="summary-row">
<span>면적 기준</span>
<span id="sumPyeong" class="mono">32평 (평당 11,000원)</span>
</div>
<div class="summary-row">
<span>기본 케어비</span>
<span id="sumBasePrice" class="mono">352,000원</span>
</div>
<div class="summary-row">
<span>정밀 옵션 합계</span>
<span id="sumAddonPrice" class="mono">80,000원</span>
</div>
<div class="summary-total">
<span>총 예상 견적</span>
<span class="total-amount" id="sumTotal">432,000원</span>
</div>
<button type="button" class="calc-submit" id="btnSubmitQuote">
이 견적으로 신청 접수하기
</button>
<p style="font-size: 12px; color: var(--ink-inverse-muted); margin-top: 10px; text-align: center;">
✓ 접수 즉시 배차 팀장이 연락 후 사전 현장 조율을 진행합니다.
</p>
</div>
</div>
</div>
</section>
<!-- 6단계 위생 프로토콜 -->
<section class="protocol-section" id="protocol" aria-labelledby="protoHeading">
<div class="wrap">
<div class="section-head" style="display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 12px;">
<div>
<h2 id="protoHeading" class="section-title">임상 수준의 6단계 청온 케어 프로토콜</h2>
<p class="section-sub">표면만 닦는 청소가 아닌, 미세 분진과 세균 번식의 원인을 제거하는 체계적인 과정입니다.</p>
</div>
<a href="./protocol/index.html" class="action-btn" style="text-decoration: none; padding: 10px 20px; font-size: 14px; background: var(--surface-card); color: var(--accent); border: 1px solid var(--accent);">
나인원한남 ATP 측정 리포트 보기 →
</a>
</div>
<div class="protocol-grid">
<div class="protocol-card">
<div class="protocol-num">STEP 01</div>
<h3>건식 초미세 집진</h3>
<p>H14 헤파 필터 흡진기로 벽면, 천장 몰딩, 바닥의 시공 미세분진 및 부유 입자를 99.97% 1차 포집합니다.</p>
</div>
<div class="protocol-card">
<div class="protocol-num">STEP 02</div>
<h3>160℃ 고온 스팀 살균</h3>
<p>배수구, 욕실 타일 틈새, 주방 후드 기름때를 화학 세제 없이 4.0bar 고온 증기로 녹여 살균합니다.</p>
</div>
<div class="protocol-card">
<div class="protocol-num">STEP 03</div>
<h3>약알칼리 친환경 분해</h3>
<p>에코서트(ECOCERT) 인증 천연 식물성 세정제를 도포하여 자재 손상 없이 찌든 때를 안전하게 중화합니다.</p>
</div>
<div class="protocol-card">
<div class="protocol-num">STEP 04</div>
<h3>서랍·후드 전면 탈거 세척</h3>
<p>보이지 않는 싱크대 걸레받이, 전등갓, 수납장 서랍 레일을 전면 탈거하여 내측 분진을 완벽 제거합니다.</p>
</div>
<div class="protocol-card">
<div class="protocol-num">STEP 05</div>
<h3>편백 피톤치드 연무 훈증</h3>
<p>순수 제주 편백 오일 연무로 벽지 속 새집증후군 원인 물질(포름알데히드, VOCs)을 중화하고 탈취합니다.</p>
</div>
<div class="protocol-card">
<div class="protocol-num">STEP 06</div>
<h3>32개 항목 마스터 검수</h3>
<p>현장 팀장과 고객이 함께 32개 체크리스트를 1:1 대조하고, 미흡 지점 발생 시 즉시 재케어 후 인도합니다.</p>
</div>
</div>
</div>
</section>
<!-- 전문 장비 & Before/After -->
<section class="equipment-section" id="equipment" aria-labelledby="equipHeading">
<div class="wrap">
<div class="section-head">
<h2 id="equipHeading" class="section-title">전문 위생 장비 및 검수 스펙</h2>
<p class="section-sub">가정용 장비가 아닌 독일 카처 정품 산업용 위생 기기를 전 현장에 투입합니다.</p>
</div>
<div class="equip-grid">
<div class="equip-specs">
<div class="spec-item">
<span class="spec-key">고온 스팀 압력</span>
<span class="spec-val">Kärcher SG 4/4 (4.0 bar · 160℃)</span>
</div>
<div class="spec-item">
<span class="spec-key">미세먼지 집진 등급</span>
<span class="spec-val">HEPA H14 (0.3㎛ 99.995% 포집)</span>
</div>
<div class="spec-item">
<span class="spec-key">사용 세제 인증</span>
<span class="spec-val">ECOCERT 100% 생분해 식물성</span>
</div>
<div class="spec-item">
<span class="spec-key">포름알데히드 저감</span>
<span class="spec-val">한국화학융합시험연구원 인증 94.2%</span>
</div>
</div>
<div class="ba-container">
<img
src="./assets/steam-qc.webp"
alt="청온 케어 마스터 위생 장비와 정밀 세척 노즐"
width="800"
height="600"
loading="lazy"
/>
<span class="ba-badge before">정밀 장비 규격</span>
<span class="ba-badge after">QC 완결 보증</span>
</div>
</div>
</div>
</section>
</div>
<!-- ===================================================================
2. 관리자 관제 콘솔 모드 (Admin Dashboard Mockup)
=================================================================== -->
<section class="admin-view wrap" aria-label="청온 케어 관리자 콘솔">
<div class="admin-header">
<div>
<div class="eyebrow" style="margin-bottom: 4px;">OPERATIONAL CONTROL CENTER</div>
<h2 style="font-size: var(--step-2); font-weight: 800;">실시간 배차 및 현장 관제 현황</h2>
</div>
<button type="button" class="btn-primary" id="btnOpenQcModal">
32개 현장 검수표 열기
</button>
</div>
<!-- KPI 통계 카드 -->
<div class="admin-kpis">
<div class="kpi-card">
<div class="kpi-label">오늘 신규 접수</div>
<div class="kpi-val accent" id="kpiNewCount">6건</div>
</div>
<div class="kpi-card">
<div class="kpi-label">현재 현장 진행 중</div>
<div class="kpi-val">4개 조</div>
</div>
<div class="kpi-card">
<div class="kpi-label">검수 대기 / 완료</div>
<div class="kpi-val">2건</div>
</div>
<div class="kpi-card">
<div class="kpi-label">오늘 견적 총액</div>
<div class="kpi-val mono" id="kpiTotalRevenue">2,480,000원</div>
</div>
</div>
<!-- 실시간 접수 및 배차 원장 테이블 -->
<div class="admin-table-card">
<div class="table-header">
<span class="table-title">실시간 예약 및 케어 원장 (Live Ledger)</span>
<div class="table-filter-tabs">
<button class="filter-tab is-active" data-filter="all">전체</button>
<button class="filter-tab" data-filter="new">신규접수</button>
<button class="filter-tab" data-filter="progress">진행중</button>
<button class="filter-tab" data-filter="done">완료</button>
</div>
</div>
<div style="overflow-x: auto;">
<table class="admin-table">
<thead>
<tr>
<th>접수번호</th>
<th>고객명 / 연락처</th>
<th>공간 / 면적</th>
<th>현장 주소</th>
<th>견적액</th>
<th>배정 팀장</th>
<th>상태</th>
<th>조치</th>
</tr>
</thead>
<tbody id="adminTableBody">
<tr>
<td class="mono">#CO-2026-0819</td>
<td><strong>이서연</strong><br /><span style="color:var(--ink-muted);font-size:11px;">010-4821-9930</span></td>
<td>주거 입주 (32평)</td>
<td>성동구 성수동 아크로포레스트</td>
<td class="mono" style="font-weight:700;">432,000원</td>
<td>김정온 마스터</td>
<td><span class="status-pill new">신규 접수</span></td>
<td><button class="action-btn" onclick="alert('배차 확인 알림톡이 고객님께 전송되었습니다.');">배차 확정</button></td>
</tr>
<tr>
<td class="mono">#CO-2026-0818</td>
<td><strong>박도현</strong><br /><span style="color:var(--ink-muted);font-size:11px;">010-9120-3341</span></td>
<td>오피스 정기 (45평)</td>
<td>강남구 테헤란로 142 위워크</td>
<td class="mono" style="font-weight:700;">520,000원</td>
<td>박준호 팀장 (2팀)</td>
<td><span class="status-pill progress">스팀 케어 중</span></td>
<td><button class="action-btn" onclick="document.getElementById('qcModal').showModal();">실시간 검수</button></td>
</tr>
<tr>
<td class="mono">#CO-2026-0817</td>
<td><strong>한지민</strong><br /><span style="color:var(--ink-muted);font-size:11px;">010-7712-4091</span></td>
<td>하이엔드 준공 (58평)</td>
<td>용산구 한남동 나인원한남</td>
<td class="mono" style="font-weight:700;">841,000원</td>
<td>최수진 닥터 (1팀)</td>
<td><span class="status-pill done">검수 승인 완료</span></td>
<td><button class="action-btn" onclick="alert('완료 검수 리포트가 PDF로 발행되었습니다.');">리포트 다운</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</main>
<!-- 32개 항목 QC 검수 모달 다이얼로그 -->
<dialog id="qcModal" class="qc-dialog" aria-labelledby="qcTitle">
<div class="qc-head">
<h3 id="qcTitle" style="font-size: var(--step-1); font-weight: 700;">32개 정밀 위생 검수 체크리스트</h3>
<button type="button" onclick="document.getElementById('qcModal').close();" aria-label="닫기" style="font-size: 18px; font-weight: 700;">✕</button>
</div>
<ul class="qc-list">
<li class="qc-item"><input type="checkbox" checked /> <span>01. 천장 몰딩 및 전등갓 상단 분진 흡진</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>02. 창틀 상하좌우 틈새 시멘트 가루 및 찌든때 제거</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>03. 싱크대 하부 걸레받이 전면 탈거 및 분진 집진</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>04. 가스레인지/인덕션 상판 및 후드 필터 기름때 고온 스팀 분해</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>05. 욕실 벽타일 메지 및 배수구 트랩 고온 살균 세척</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>06. 수납장 모든 서랍 완전 탈거 후 레일 틈새 분진 청소</span></li>
<li class="qc-item"><input type="checkbox" checked /> <span>07. 베란다 및 다용도실 바닥 타일 약알칼리 세정</span></li>
<li class="qc-item"><input type="checkbox" /> <span>08. 전 실 편백 피톤치드 훈증 및 공기질 측정기 수치 확인</span></li>
</ul>
<div style="display: flex; justify-content: flex-end; gap: 8px;">
<button type="button" class="action-btn" onclick="document.getElementById('qcModal').close();">닫기</button>
<button type="button" class="btn-primary" style="padding: 6px 14px; font-size: 13px;" onclick="alert('검수 승인이 완료되었습니다.'); document.getElementById('qcModal').close();">최종 검수 서명 승인</button>
</div>
</dialog>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>청온 케어 · 서울 마포구 마포대로 109 청온랩 4층</p>
<p class="muted">연중무휴 08:00 – 20:00 (사전 예약제)</p>
<p class="muted">care@cheongon.kr</p>
</div>
</footer>
<!-- 인터랙티브 스크립트 -->
<script>
(function () {
// 1. 뷰 모드 전환 (고객 뷰 vs 관리자 뷰)
const btnClient = document.getElementById("btnModeClient");
const btnAdmin = document.getElementById("btnModeAdmin");
function setViewMode(mode) {
document.body.setAttribute("data-view", mode);
if (mode === "admin") {
btnAdmin.classList.add("is-active");
btnClient.classList.remove("is-active");
} else {
btnClient.classList.add("is-active");
btnAdmin.classList.remove("is-active");
}
}
btnClient.addEventListener("click", () => setViewMode("client"));
btnAdmin.addEventListener("click", () => setViewMode("admin"));
// 단축키 Alt + A 로 관리자 모드 토글
window.addEventListener("keydown", (e) => {
if (e.altKey && (e.key === "a" || e.key === "A" || e.key === "ㅁ")) {
const cur = document.body.getAttribute("data-view");
setViewMode(cur === "admin" ? "client" : "admin");
}
});
// 2. 견적 계산기 로직
const pyeongSlider = document.getElementById("pyeongSlider");
const pyeongValue = document.getElementById("pyeongValue");
const sumPyeong = document.getElementById("sumPyeong");
const sumBasePrice = document.getElementById("sumBasePrice");
const sumAddonPrice = document.getElementById("sumAddonPrice");
const sumTotal = document.getElementById("sumTotal");
const sumSpaceType = document.getElementById("sumSpaceType");
const typeBtns = document.querySelectorAll(".type-btn");
const addonCheckboxes = document.querySelectorAll("input[name='addon']");
let currentBaseRate = 11000;
let currentTypeLabel = "주거 입주/이사";
function updateQuote() {
const pyeong = parseInt(pyeongSlider.value, 10);
const sqm = Math.round(pyeong * 3.30578);
pyeongValue.textContent = `${pyeong} 평 (약 ${sqm}㎡)`;
sumPyeong.textContent = `${pyeong}평 (평당 ${currentBaseRate.toLocaleString()}원)`;
sumSpaceType.textContent = currentTypeLabel;
const baseCost = pyeong * currentBaseRate;
sumBasePrice.textContent = `${baseCost.toLocaleString()}원`;
let addonCost = 0;
addonCheckboxes.forEach((cb) => {
if (cb.checked) {
addonCost += parseInt(cb.getAttribute("data-price"), 10);
}
});
sumAddonPrice.textContent = `${addonCost.toLocaleString()}원`;
const total = baseCost + addonCost;
sumTotal.textContent = `${total.toLocaleString()}원`;
}
pyeongSlider.addEventListener("input", updateQuote);
typeBtns.forEach((btn) => {
btn.addEventListener("click", () => {
typeBtns.forEach((b) => b.classList.remove("is-active"));
btn.classList.add("is-active");
currentBaseRate = parseInt(btn.getAttribute("data-base"), 10);
currentTypeLabel = btn.textContent.trim();
updateQuote();
});
});
addonCheckboxes.forEach((cb) => {
cb.addEventListener("change", updateQuote);
});
// 초기 계산 1회 실행
updateQuote();
// 3. 견적 신청 버튼 클릭 -> 관리자 원장에 즉시 추가되는 풀-루프 인터랙션!
const btnSubmit = document.getElementById("btnSubmitQuote");
const tableBody = document.getElementById("adminTableBody");
const kpiNewCount = document.getElementById("kpiNewCount");
let submissionCount = 6;
btnSubmit.addEventListener("click", () => {
const name = document.getElementById("clientName").value || "고객님";
const phone = document.getElementById("clientPhone").value || "010-0000-0000";
const address = document.getElementById("clientAddress").value || "서울시 현장 주소";
const pyeong = pyeongSlider.value;
const totalStr = sumTotal.textContent;
submissionCount++;
kpiNewCount.textContent = `${submissionCount}건`;
// 새 행을 관리자 테이블 최상단에 삽입
const newRow = document.createElement("tr");
newRow.style.backgroundColor = "var(--accent-light)";
newRow.innerHTML = `
<td class="mono">#CO-2026-082${submissionCount}</td>
<td><strong>${name}</strong><br /><span style="color:var(--ink-muted);font-size:11px;">${phone}</span></td>
<td>${currentTypeLabel} (${pyeong}평)</td>
<td>${address}</td>
<td class="mono" style="font-weight:700;color:var(--accent);">${totalStr}</td>
<td>배차 지정 대기</td>
<td><span class="status-pill new">방금 접수됨</span></td>
<td><button class="action-btn" onclick="alert('배차 팀장을 매칭 중입니다.');">배차 지정</button></td>
`;
tableBody.insertBefore(newRow, tableBody.firstChild);
alert(`[청온 케어] ${name}님의 견적(${totalStr}) 신청이 성공적으로 접수되었습니다.\n관리자 콘솔에 실시간 등록되었습니다.`);
});
// 모달 열기
const btnOpenQc = document.getElementById("btnOpenQcModal");
const qcModal = document.getElementById("qcModal");
if (btnOpenQc && qcModal) {
btnOpenQc.addEventListener("click", () => qcModal.showModal());
}
})();
</script>
</body>
</html>

View file

@ -0,0 +1,346 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>임상 6단계 위생 프로토콜 & 한남 케이스 리포트 — 청온 케어 (淸溫)</title>
<meta name="description" content="청온 케어의 병원급 6단계 정밀 위생 프로토콜 및 ATP 생체 오염도 측정 검증 리포트. 나인원한남 86평형 준공 청소 케이스 스터디 및 32개 정밀 QC 체크리스트." />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="../styles/tokens.css" />
<link rel="stylesheet" href="../styles/main.css" />
<style>
.breadcrumb-bar {
padding-top: var(--space-4);
padding-bottom: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
border-bottom: 1px solid var(--line);
}
.breadcrumb-bar a {
color: var(--ink-muted);
text-decoration: none;
transition: color 0.15s ease;
}
.breadcrumb-bar a:hover {
color: var(--accent);
}
.breadcrumb-bar .sep {
opacity: 0.4;
}
.breadcrumb-bar .current {
color: var(--ink);
font-weight: 600;
}
.protocol-hero {
padding: var(--space-7) 0 var(--space-5);
}
.protocol-meta-strip {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--space-3);
margin-top: var(--space-5);
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-4);
}
.protocol-meta-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.protocol-meta-item .label {
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
text-transform: uppercase;
}
.protocol-meta-item .value {
font-size: var(--step-0);
font-weight: 700;
color: var(--accent);
}
.atp-compare-box {
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-5);
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-4);
text-align: center;
}
.atp-val {
font-family: var(--font-mono);
font-size: var(--step-3);
font-weight: 800;
}
.atp-val.before {
color: #ef4444;
}
.atp-val.after {
color: var(--accent);
}
.step-deep-list {
display: flex;
flex-direction: column;
gap: var(--space-4);
margin-top: var(--space-5);
}
.step-deep-item {
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-5);
display: grid;
grid-template-columns: 120px 1fr;
gap: var(--space-5);
align-items: start;
}
@media (max-width: 680px) {
.step-deep-item {
grid-template-columns: 1fr;
gap: var(--space-3);
}
}
</style>
</head>
<body>
<!-- Top Nav (GNB SSOT) -->
<header class="site-head">
<div class="wrap head-inner">
<a href="../index.html" class="wordmark" aria-label="청온 케어 홈">
<span class="wordmark-badge">淸</span>
청온 케어 <span class="wordmark-han">淸溫</span>
</a>
<nav class="nav" aria-label="메인 내비게이션">
<a href="./index.html" class="active" style="color: var(--accent); font-weight: 600;">6단계 프로토콜</a>
<a href="../index.html#services">케어 솔루션</a>
<a href="../index.html#calculator">정찰제 견적</a>
<a href="../index.html#equipment">전문 장비</a>
</nav>
<div class="head-actions">
<a href="../index.html#calculator" class="btn">간편 견적</a>
</div>
</div>
</header>
<main id="main-content">
<!-- Hero Header -->
<section class="section protocol-hero" style="border-top: none; padding-top: var(--space-5);">
<div class="wrap">
<div class="breadcrumb-bar" style="margin-bottom: var(--space-4); display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); letter-spacing: 0.04em;">
<a href="../index.html">CHEONG-ON CARE</a>
<span class="sep">/</span>
<span class="current" style="color: var(--ink); font-weight: 600;">CLINICAL HYGIENE PROTOCOL &amp; CASE STUDY</span>
</div>
<div class="protocol-hero-head">
<p class="hero-label">HOSPITAL-GRADE SANITATION STANDARDS</p>
<h1 class="hero-title" style="margin-bottom: var(--space-2);">단순한 청소를 넘어<br /><span style="color: var(--accent);">병원급 6단계 임상 위생 프로토콜</span></h1>
<p class="hero-lead" style="font-size: var(--step-0); color: var(--ink-muted); max-width: var(--measure); margin-bottom: var(--space-5);">
독일 카처 산업용 스팀과 H14 헤파 필터 흡진, ATP 생체 오염도 계측을 통해 인체에 무해한 안심 공간을 과학적으로 보증합니다.
</p>
<dl class="protocol-specs" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6);">
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Steam Temp</dt>
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">160℃ High-Temp</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Filtration</dt>
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">0.3㎛ HEPA H14 (99.995%)</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">Detergent</dt>
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">ECOCERT 100% 식물성</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">QC Points</dt>
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">32개 정밀 항목</dd>
</div>
</dl>
</div>
<figure class="hero-photo" style="border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-card);">
<img
src="../assets/clean-hero.webp"
alt="완벽하게 정돈되고 살균된 프리미엄 주거 인테리어 공간"
width="1600"
height="900"
style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
/>
<figcaption style="padding: 10px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); border-top: 1px solid var(--line); background: var(--surface-raised);">
나인원한남 86평형 · 6단계 정밀 프로토콜 시공 완료
</figcaption>
</figure>
</div>
</section>
<!-- ATP Measurement Case Report -->
<section class="wrap" style="padding-bottom: var(--space-8);">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
<div class="frame">
<img
src="../assets/atp-tester.webp"
alt="ATP 생체 오염도 측정기 계측 화면 (12 RLU)"
style="width: 100%; display: block;"
/>
</div>
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
<div class="eyebrow">SCIENTIFIC DATA VERIFICATION</div>
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
나인원한남 86평형<br />
<span style="color: var(--accent);">ATP 생체 오염도 99.1% 저감 리포트</span>
</h2>
<p style="font-size: var(--step--1); color: var(--ink-muted); line-height: 1.7;">
실제 한남동 나인원한남 입주 청소 현장에서 주방 싱크대 상판 및 욕실 수전 부위의 미생물 오염도(ATP RLU)를 측정한 결과입니다.
160℃ 고온 스팀과 에코서트 효소 분해 세정 후 병원 무균실 기준(30 RLU 이하)을 상회하는 12 RLU 수준으로 완벽하게 저감되었습니다.
</p>
<div class="atp-compare-box">
<div>
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">시공 전 (BEFORE)</div>
<div class="atp-val before">1,420 <span style="font-size: 14px;">RLU</span></div>
<div style="font-size: 12px; color: #ef4444; margin-top: 4px;">심각한 미세 세균 번식</div>
</div>
<div style="border-left: 1px solid var(--line);">
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">시공 후 (AFTER)</div>
<div class="atp-val after">12 <span style="font-size: 14px;">RLU</span></div>
<div style="font-size: 12px; color: var(--accent); margin-top: 4px;">병원 수술실 기준 패스 (99.1% 저감)</div>
</div>
</div>
</div>
</div>
</section>
<!-- 6-Stage Deep Steps -->
<section class="wrap" style="padding-bottom: var(--space-8);">
<div class="eyebrow" style="text-align: center;">STEP-BY-STEP ENGINEERING</div>
<h2 style="font-size: var(--step-3); font-weight: 800; text-align: center; margin-top: 6px;">
청온 케어 임상 6단계 시공 체계
</h2>
<div class="step-deep-list">
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 01</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">H14 헤파 필터 건식 초미세 흡진</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
벽면 실크벽지 미세 분진, 천장 우물 몰딩, 조명 레일 내부의 0.3㎛ 부유 입자를 99.995% 집진하여 공기 중 재비산을 완벽 차단합니다.
</p>
</div>
</div>
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 02</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">160℃ 4.0bar 고온 스팀 살균</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
배수구 유가, 변기 틈새, 주방 후드 필터에 화학 세제 없이 4.0bar 고압 증기를 분사하여 기름때를 녹이고 99.9% 멸균합니다.
</p>
</div>
</div>
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 03</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">약알칼리 에코서트 친환경 효소 분해</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
원목 마루 및 천연 대리석 자재에 손상을 주지 않는 100% 생분해성 식물 세정제를 도포하여 찌든 산화 피막을 안전하게 중화합니다.
</p>
</div>
</div>
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 04</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">서랍장·걸레받이 전면 탈거 정밀 세척</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
싱크대 하부 걸레받이와 수납장 레일을 전량 분리 탈거하여, 보이지 않는 시공 톱밥 및 유해 분진을 내측까지 완전 제거합니다.
</p>
</div>
</div>
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 05</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">제주 편백 피톤치드 연무 훈증</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
새집증후군 유발 물질인 포름알데히드 및 휘발성유기화합물(VOCs)을 중화하고 실내 숲속 공기질을 복원합니다.
</p>
</div>
</div>
<div class="step-deep-item">
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 06</div>
<div>
<h3 style="font-size: var(--step-1); font-weight: 700;">32개 항목 마스터 닥터 1:1 검수 및 디지털 인증</h3>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
배차 현장 팀장과 고객이 함께 32개 정밀 체크리스트를 1:1 대조하고, 모바일 리포트를 발행하여 최종 승인합니다.
</p>
</div>
</div>
</div>
</section>
<!-- Steam QC Equipment Spec Strip -->
<div class="wrap" style="margin-bottom: var(--space-8);">
<div class="frame">
<img
src="../assets/steam-qc.webp"
alt="독일 카처 4.0bar 정밀 스팀 위생 장비와 멸균 키트"
style="width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover;"
/>
</div>
</div>
<!-- Quick CTA -->
<section class="wrap" style="padding-bottom: var(--space-8); text-align: center;">
<div class="card" style="max-width: 640px; margin: 0 auto; background: var(--surface-card);">
<div class="eyebrow" style="color: var(--accent);">TRANSPARENT PRICING</div>
<h2 style="font-size: var(--step-2); font-weight: 800; margin-top: 4px;">
우리 집 공간의 정찰제 견적을 확인하세요
</h2>
<p style="font-size: var(--step--1); color: var(--ink-muted); margin: 12px 0 24px;">
추가금 없는 평당 11,000원 명확한 기준. 1분 만에 실시간 견적 산출이 가능합니다.
</p>
<a href="../index.html#quote" class="btn-primary" style="display: inline-block; padding: 14px 28px; text-decoration: none; font-size: var(--step-0);">
실시간 정찰제 견적 계산하기 →
</a>
</div>
</section>
</main>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>청온 케어 · 서울 마포구 마포대로 109 청온랩 4층</p>
<p class="muted">연중무휴 08:00 – 20:00 (사전 예약제)</p>
<p class="muted">care@cheongon.kr</p>
</div>
</footer>
</body>
</html>

View file

@ -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;
}
}

View file

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta http-equiv="refresh" content="0; url=./projects/hannam-duplex/index.html" />
<title>한남 듀플렉스 펜트하우스 이동 중...</title>
<script>
window.location.replace("./projects/hannam-duplex/index.html");
</script>
</head>
<body style="background: #0d0d0f; color: #f4f4f0; font-family: sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0;">
<p>한남 듀플렉스 펜트하우스 갤러리로 이동 중입니다... <a href="./projects/hannam-duplex/index.html" style="color: #c49d68;">직접 이동하기</a></p>
</body>
</html>

View file

@ -0,0 +1,430 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>마테리아 스튜디오 (MATERIA) · 하이엔드 주거 & 상업 공간 인테리어 건축</title>
<meta
name="description"
content="트래버틴 대리석, 훈증 오크, 벨기에 리넨의 촉각적 물성과 자연광의 궤적을 설계하는 하이엔드 인테리어 건축 아틀리에. 한남·청담 펜트하우스 및 프라이빗 갤러리 비스포크 설계."
/>
<meta property="og:title" content="마테리아 스튜디오 (MATERIA) · 하이엔드 인테리어 건축" />
<meta property="og:description" content="빛과 트래버틴이 빚어내는 침묵의 공간 건축. 1:1 비스포크 공간 컨설팅." />
<meta property="og:type" content="website" />
<meta name="theme-color" content="#f5f3ef" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="./styles/main.css?v=1" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23F5F3EF'/%3E%3Crect x='6' y='6' width='20' height='20' stroke='%23161514' stroke-width='2' fill='none'/%3E%3Crect x='12' y='12' width='8' height='8' fill='%239E8062'/%3E%3C/svg%3E"
/>
</head>
<body data-ambiance="day">
<a class="skip-link" href="#mainContent">본문으로 건너뛰기</a>
<!-- 상단 헤더 & 조도 스위처 -->
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="./index.html">
MATERIA <span class="wordmark-han">空間素材</span>
</a>
<nav class="nav" aria-label="메인 메뉴">
<a href="./projects/hannam-duplex/index.html" style="color: var(--accent); font-weight: 600;">24개 공간 갤러리</a>
<a href="#works">엄선 프로젝트</a>
<a href="#materials">물성 아카이브</a>
<a href="#process">설계 프로세스</a>
<a href="#contact">프라이빗 상담</a>
</nav>
<div class="head-actions">
<div class="ambiance-switcher" role="group" aria-label="조도 모드 전환">
<button type="button" class="ambiance-btn is-active" id="btnAmbianceDay">
Day
</button>
<button type="button" class="ambiance-btn" id="btnAmbianceNight">
Night
</button>
</div>
<a href="#contact" class="btn">
상담 신청
</a>
</div>
</div>
</header>
<main id="mainContent">
<!-- 히어로 섹션 -->
<section class="hero-section" aria-labelledby="heroHeading">
<div class="wrap hero-grid">
<div class="hero-content">
<div class="eyebrow">
<span>HIGH-END ARCHITECTURAL INTERIOR</span>
</div>
<h1 id="heroHeading" class="hero-title">
빛과 트래버틴이 빚어내는<br />
침묵의 <span class="italic">공간 건축</span>
</h1>
<p class="hero-desc">
우리는 장식을 덧대지 않습니다. 돌의 거친 결, 훈증 오크의 온기, 리넨을 투과하는 자연광의 궤적을 치밀하게 계산하여 시간이 흘러도 변치 않는 하이엔드 주거와 갤러리 공간을 완성합니다.
</p>
<div class="hero-ctas">
<a href="./projects/hannam-duplex/index.html" class="btn-materia">
한남 펜트하우스 24개 공간 감상
<span aria-hidden="true">↗</span>
</a>
<a href="#materials" class="btn-materia" style="background: var(--surface-card); color: var(--ink); border: 1px solid var(--line);">
소재와 물성 탐색
</a>
</div>
</div>
<div class="hero-visual" style="position: relative;">
<div class="bezel-outer">
<div class="hero-frame">
<img
src="./assets/penthouse-hero.webp"
alt="트래버틴 벽체와 훈증 오크 수납장이 조화된 하이엔드 펜트하우스 거실 전경"
width="1200"
height="1600"
loading="eager"
/>
</div>
</div>
<div class="hero-caption-pill">
<div class="hero-caption-name">한남 펜트하우스 레지던스</div>
<div class="hero-caption-meta">Hannam · 284㎡ (86평) · 2026.04 준공</div>
</div>
</div>
</div>
</section>
<!-- 엄선 프로젝트 갤러리 (Selected Works) -->
<section class="works-section" id="works" aria-labelledby="worksHeading">
<div class="wrap">
<div class="section-head">
<h2 id="worksHeading" class="section-title">엄선된 하이엔드 공간 아카이브</h2>
<p class="section-sub">주거 펜트하우스부터 프라이빗 갤러리까지, 공간의 고유한 결을 살린 마테리아의 대표 프로젝트입니다.</p>
</div>
<div class="works-grid">
<!-- 프로젝트 1 -->
<article class="work-card">
<div class="work-card-media">
<img
src="./assets/penthouse-hero.webp"
alt="한남 듀플렉스 펜트하우스 거실 전경"
width="600"
height="400"
loading="lazy"
/>
</div>
<div class="work-card-body">
<h3 class="work-card-title">한남 듀플렉스 펜트하우스</h3>
<div class="work-card-type">Residential · 284㎡ · 로만 트래버틴 & 훈증 오크</div>
<p class="work-card-desc">
층고 5.8m의 복층 구조에 거친 트래버틴 석재 벽체와 다크 훈증 오크를 대비시켜 웅장하면서도 고요한 빛의 음영을 연출했습니다.
</p>
<div class="work-card-actions">
<a
href="./projects/hannam-duplex/index.html"
class="btn-materia"
>
24개 공간 갤러리 감상 ↗
</a>
</div>
</div>
</article>
<!-- 프로젝트 2 -->
<article class="work-card">
<div class="work-card-media">
<img
src="./assets/materials-macro.webp"
alt="청담 프라이빗 갤러리 마감재 매크로"
width="600"
height="400"
loading="lazy"
/>
</div>
<div class="work-card-body">
<h3 class="work-card-title">청담 프라이빗 갤러리</h3>
<div class="work-card-type">Commercial / Gallery · 195㎡ · 마이크로토핑 & 브론즈</div>
<p class="work-card-desc">
작품이 온전히 숨 쉴 수 있도록 모든 몰딩을 숨긴 무문선·무걸레받이 디테일과 브러시드 브론즈 프레임으로 정갈함을 극대화했습니다.
</p>
<div class="work-card-actions">
<button
type="button"
class="btn-materia"
onclick="openProjectModal('청담 프라이빗 갤러리', '195㎡ (59평)', '이탈리아 마이크로토핑 바닥재, 브러시드 파티나 브론즈, 무반사 저철분 유리', '색채를 배제한 무채색 베이스 위에 섬세한 텍스처를 레이어드하여 미술품의 몰입도를 극대화한 공간입니다.');"
>
설계 명세 보기 →
</button>
</div>
</div>
</article>
<!-- 프로젝트 3 -->
<article class="work-card">
<div class="work-card-media">
<img
src="./assets/hannam-08-terrace.webp"
alt="성북동 포레스트 레지던스 자연석 테라스와 수림 차경"
width="600"
height="400"
loading="lazy"
/>
</div>
<div class="work-card-body">
<h3 class="work-card-title">성북동 포레스트 레지던스</h3>
<div class="work-card-type">Private Residence · 340㎡ · 자연석 화강석 & 벨기에 리넨</div>
<p class="work-card-desc">
북악산 사계절의 숲 풍경을 차경(借景)으로 끌어들이고, 실내에는 자연석 아일랜드와 수공예 패브릭을 배치한 친환경 럭셔리 주거.
</p>
<div class="work-card-actions">
<button
type="button"
class="btn-materia"
onclick="openProjectModal('성북동 포레스트 레지던스', '340㎡ (103평)', '가평 통화강석 아일랜드, 스위스 비트라 가구 빌트인, 천연 회벽 마감', '산자락의 자연광과 계절의 변화를 고스란히 담아내는 차경 설계와 천연 자재의 숨결을 결합했습니다.');"
>
설계 명세 보기 →
</button>
</div>
</div>
</article>
</div>
</div>
</section>
<!-- 머티리얼 보드 & 촉각성 (Tactile Materiality) -->
<section class="material-section" id="materials" aria-labelledby="matHeading">
<div class="wrap">
<div class="section-head">
<h2 id="matHeading" class="section-title">자연이 빚은 물성과 촉각의 조화</h2>
<p class="section-sub">마테리아가 선별한 최상급 건축 마감재는 인위적인 가공을 최소화하여 시간의 깊이를 머금습니다.</p>
</div>
<div class="material-grid">
<div class="material-frame">
<img
src="./assets/materials-macro.webp"
alt="트래버틴 대리석, 훈증 오크, 벨기에 리넨, 브론즈 머티리얼 플랫레이"
width="800"
height="600"
loading="lazy"
/>
</div>
<div class="material-specs">
<div class="mat-item">
<span class="mat-name">로만 트래버틴</span>
<span class="mat-desc">이탈리아 티볼리 채석장의 천연 석회암. 무수한 다공성 질감이 빛을 부드럽게 난반사합니다.</span>
</div>
<div class="mat-item">
<span class="mat-name">훈증 오크 (Bog Oak)</span>
<span class="mat-desc">수백 년간 수중 숙성된 천연 고재의 깊은 탄화 흑색과 치밀한 나이테 결을 그대로 보존합니다.</span>
</div>
<div class="mat-item">
<span class="mat-name">벨기에 천연 리넨</span>
<span class="mat-desc">플랑드르 지방의 무표백 아마 섬유로 짠 고밀도 원단. 부드럽게 빛을 거르고 통기성을 제공합니다.</span>
</div>
<div class="mat-item">
<span class="mat-name">파티나 브론즈</span>
<span class="mat-desc">손길이 닿을수록 자연스러운 에이징 산화 피막이 형성되는 수공예 황동 하드웨어입니다.</span>
</div>
</div>
</div>
</div>
</section>
<!-- 4단계 마스터 프로세스 -->
<section class="process-section" id="process" aria-labelledby="procHeading">
<div class="wrap">
<div class="section-head">
<h2 id="procHeading" class="section-title">비스포크 공간 건축 4단계 프로세스</h2>
<p class="section-sub">기획부터 완공 인도까지, 총괄 디렉터 1인이 모든 자재와 조도를 책임지고 총괄합니다.</p>
</div>
<div class="process-grid">
<div class="process-card">
<div class="process-step">PHASE 01</div>
<h3>일조 및 공간 진단</h3>
<p>현장의 24시간 태양 궤적, 조망 축, 구조 하중을 분석하여 채광의 최적 배치를 기획합니다.</p>
</div>
<div class="process-card">
<div class="process-step">PHASE 02</div>
<h3>촉각적 머티리얼 페어링</h3>
<p>실제 1:1 자재 샘플 보드를 제작하여 거주자의 라이프스타일과 촉감 선호도를 일치시킵니다.</p>
</div>
<div class="process-card">
<div class="process-step">PHASE 03</div>
<h3>3D 광학 시뮬레이션</h3>
<p>주광(Daylight)과 2700K 인공 조명의 조도 분포를 가상 렌더링으로 정밀 검증합니다.</p>
</div>
<div class="process-card">
<div class="process-step">PHASE 04</div>
<h3>장인 직영 디테일 시공</h3>
<p>30년 경력의 석재·목공 마스터 장인팀이 밀리미터 단위의 오차 없는 시공을 완성합니다.</p>
</div>
</div>
</div>
</section>
<!-- 프라이빗 공간 컨설팅 신청 -->
<section class="contact-section" id="contact" aria-labelledby="contactHeading">
<div class="wrap">
<div class="contact-card">
<div style="text-align: center; margin-bottom: var(--space-6);">
<div class="eyebrow">BESPOKE CONSULTATION</div>
<h2 id="contactHeading" style="font-family: var(--font-serif); font-size: var(--step-2); margin-top: 6px;">
프라이빗 공간 컨설팅 신청
</h2>
<p style="font-size: var(--step-0); color: var(--ink-muted); margin-top: 6px;">
월 최대 3건의 프로젝트만을 엄선하여 타협 없는 완성도를 보증합니다.
</p>
</div>
<form id="consultForm" onsubmit="return false;">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
<div class="form-group">
<label class="form-label" for="spaceType">공간 유형</label>
<select id="spaceType" class="form-select">
<option>주거 펜트하우스 / 타운하우스</option>
<option>단독주택 신축 / 대수선</option>
<option>프라이빗 갤러리 / 플래그십</option>
<option>하이엔드 오피스 라운지</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="spaceArea">예상 전용면적 (평형)</label>
<input type="text" id="spaceArea" class="form-input" placeholder="예: 65평 (214㎡)" value="82평 (271㎡)" />
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
<div class="form-group">
<label class="form-label" for="clientName">성함</label>
<input type="text" id="clientName" class="form-input" placeholder="성함" value="김민준" required />
</div>
<div class="form-group">
<label class="form-label" for="clientPhone">연락처</label>
<input type="tel" id="clientPhone" class="form-input" placeholder="010-0000-0000" value="010-5920-8812" required />
</div>
</div>
<div class="form-group">
<label class="form-label" for="clientBudget">예상 공사 예산 범위</label>
<select id="clientBudget" class="form-select">
<option>1억 5천만 원 ~ 3억 원</option>
<option selected>3억 원 ~ 5억 원</option>
<option>5억 원 이상 (풀 하이엔드 비스포크)</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="clientNote">공간에 바라는 철학 및 특별 요청사항</label>
<textarea id="clientNote" class="form-textarea" rows="3" placeholder="추구하시는 분위기, 선호 자재, 착공 희망 시기 등을 자유롭게 적어주세요.">거실에 웅장한 트래버틴 벽체와 자연스러운 간접 조명을 조성하고 싶습니다. 2026년 10월 착공 희망합니다.</textarea>
</div>
<button type="button" class="btn-materia" id="btnSubmitConsult" style="width: 100%; padding: 14px; margin-top: 10px;">
프라이빗 디자인 상담 신청 완료
</button>
</form>
</div>
</div>
</section>
</main>
<!-- 프로젝트 상세 모달 다이얼로그 -->
<dialog id="projectModal" class="project-dialog" aria-labelledby="pModalTitle">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.25rem; border-bottom: 1px solid var(--line); padding-bottom: 0.75rem;">
<h3 id="pModalTitle" style="font-family: var(--font-serif); font-size: var(--step-1); color: var(--ink);">
프로젝트 설계 아카이브
</h3>
<button type="button" onclick="document.getElementById('projectModal').close();" aria-label="닫기" style="font-size: 18px; font-weight: 700; color: var(--ink-muted);">✕</button>
</div>
<div style="display: grid; gap: 0.85rem; font-size: var(--step--1); line-height: 1.6;">
<div><strong>프로젝트명:</strong> <span id="mProjName" style="color:var(--accent); font-weight:700;"></span></div>
<div><strong>공간 면적:</strong> <span id="mProjArea" class="mono"></span></div>
<div><strong>주요 마감재:</strong> <span id="mProjMaterials"></span></div>
<div style="margin-top: 4px; padding: 12px; background: var(--surface-card); border-radius: var(--radius-sm); border: 1px solid var(--line);">
<strong>설계 의도:</strong><br />
<span id="mProjNotes" style="color:var(--ink-muted);"></span>
</div>
</div>
<div style="margin-top: 1.5rem; text-align: right;">
<button type="button" class="btn-materia" style="padding: 6px 20px; font-size: 13px;" onclick="document.getElementById('projectModal').close();">닫기</button>
</div>
</dialog>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>마테리아 스튜디오 · 서울 강남구 압구정로 72길 14</p>
<p class="muted">월요일부터 금요일까지 (사전 예약제)</p>
<p class="muted">studio@materia.kr</p>
</div>
</footer>
<!-- 인터랙티브 스크립트 -->
<script>
(function () {
// 1. 조도 앰비언스 모드 스위처 (자연광 Day vs 야간 Night)
const btnDay = document.getElementById("btnAmbianceDay");
const btnNight = document.getElementById("btnAmbianceNight");
function setAmbiance(mode) {
document.body.setAttribute("data-ambiance", mode);
if (mode === "night") {
btnNight.classList.add("is-active");
btnDay.classList.remove("is-active");
} else {
btnDay.classList.add("is-active");
btnNight.classList.remove("is-active");
}
}
btnDay.addEventListener("click", () => setAmbiance("day"));
btnNight.addEventListener("click", () => setAmbiance("night"));
// 2. 상담 신청 폼
const btnConsult = document.getElementById("btnSubmitConsult");
if (btnConsult) {
btnConsult.addEventListener("click", () => {
const name = document.getElementById("clientName").value || "고객님";
const spaceType = document.getElementById("spaceType").value;
const area = document.getElementById("spaceArea").value;
alert(`[마테리아 스튜디오] ${name}님의 프라이빗 공간 컨설팅 신청(${spaceType} / ${area})이 접수되었습니다.\n담당 수석 디렉터가 24시간 이내에 포트폴리오 자료와 함께 직접 연락드리겠습니다.`);
});
}
})();
// 프로젝트 모달 팝업 함수
function openProjectModal(name, area, materials, notes) {
document.getElementById("mProjName").textContent = name;
document.getElementById("mProjArea").textContent = area;
document.getElementById("mProjMaterials").textContent = materials;
document.getElementById("mProjNotes").textContent = notes;
document.getElementById("projectModal").showModal();
}
</script>
</body>
</html>

View file

@ -0,0 +1,952 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<title>한남 듀플렉스 펜트하우스 (86평) — MATERIA Project Archive</title>
<meta name="description" content="마테리아 스튜디오의 한남 듀플렉스 펜트하우스 86평 프로젝트 아카이브. 5.8m 보이드 천장고, 로만 트래버틴과 3,000년 훈증 오크, 자연광 조도 시뮬레이션 및 24개 공간 갤러리." />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
/>
<link rel="stylesheet" href="../../styles/tokens.css" />
<link rel="stylesheet" href="../../styles/main.css" />
<style>
/* Detail Page Specific Styles */
.breadcrumb-bar {
padding-top: var(--space-4);
padding-bottom: var(--space-3);
display: flex;
align-items: center;
gap: var(--space-2);
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
border-bottom: 1px solid var(--line);
}
.breadcrumb-bar a {
color: var(--ink-muted);
text-decoration: none;
transition: color 0.15s ease;
}
.breadcrumb-bar a:hover {
color: var(--accent);
}
.breadcrumb-bar .sep {
opacity: 0.4;
}
.breadcrumb-bar .current {
color: var(--ink);
font-weight: 600;
}
.project-hero-section {
padding-block: var(--space-4) var(--space-7);
border-bottom: 1px solid var(--line);
}
.project-hero-head {
padding-top: var(--space-5);
margin-bottom: var(--space-6);
}
.project-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-4);
word-break: keep-all;
}
.project-hero-title .hero-sub {
display: block;
font-family: var(--font-serif);
font-size: var(--step-1);
font-weight: 400;
color: var(--ink-muted);
margin-top: var(--space-2);
line-height: 1.5;
}
.project-meta-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
gap: var(--space-3);
margin-top: var(--space-5);
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: var(--space-4) var(--space-5);
}
.meta-item {
display: flex;
flex-direction: column;
gap: 3px;
}
.meta-item .label {
font-family: var(--font-mono);
font-size: var(--step--1);
color: var(--ink-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.meta-item .value {
font-size: var(--step-0);
font-weight: 700;
color: var(--ink);
}
.project-hero-media {
position: relative;
margin-top: var(--space-6);
}
.hero-frame {
border-radius: calc(var(--radius-md) - 6px);
overflow: hidden;
background: var(--surface-subtle);
}
.hero-frame img {
width: 100%;
height: auto;
display: block;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.hero-caption-pill {
display: inline-flex;
align-items: center;
gap: var(--space-3);
margin-top: var(--space-3);
padding: 6px 14px;
background: var(--surface-card);
border: 1px solid var(--line);
border-radius: var(--radius-pill);
font-size: var(--step--1);
}
.hero-caption-name {
font-weight: 600;
color: var(--ink);
}
.hero-caption-meta {
color: var(--accent);
}
.narrative-section {
padding-block: var(--space-7);
border-bottom: 1px solid var(--line);
}
.narrative-grid {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
gap: var(--space-7);
align-items: start;
}
@media (max-width: 900px) {
.narrative-grid {
grid-template-columns: 1fr;
gap: var(--space-4);
}
}
.narrative-text {
display: flex;
flex-direction: column;
gap: var(--space-3);
color: var(--ink-muted);
line-height: var(--leading-normal);
font-size: var(--step-0);
}
/* Gallery Architecture */
.gallery-section {
padding-block: var(--space-7) var(--space-8);
}
.gallery-head {
margin-bottom: var(--space-6);
}
.gallery-filter-chips {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
margin-bottom: var(--space-5);
}
.gallery-chip {
font-family: var(--font-mono);
font-size: var(--step--1);
padding: 8px 16px;
border-radius: var(--radius-xs);
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink-muted);
cursor: pointer;
transition: all 0.2s ease;
}
.gallery-chip:hover {
border-color: var(--accent);
color: var(--ink);
}
.gallery-chip.active {
background: var(--accent);
border-color: var(--accent);
color: #ffffff;
font-weight: 600;
}
.spatial-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: var(--space-4);
}
@media (min-width: 1200px) {
.spatial-grid {
grid-template-columns: repeat(3, 1fr);
}
}
.gallery-card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
overflow: hidden;
display: flex;
flex-direction: column;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
cursor: pointer;
position: relative;
}
.gallery-card:hover {
transform: translateY(-3px);
border-color: var(--accent);
box-shadow: 0 12px 28px rgba(0,0,0,0.06);
}
.gallery-card-img-wrap {
aspect-ratio: 16 / 10;
position: relative;
overflow: hidden;
background: var(--surface-sunken);
}
.gallery-card img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.gallery-card:hover img {
transform: scale(1.03);
}
.gallery-card-body {
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: 6px;
flex-grow: 1;
}
.gallery-card-zone {
font-family: var(--font-mono);
font-size: 11px;
color: var(--accent);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.gallery-card-title {
font-family: var(--font-serif);
font-size: var(--step-0);
font-weight: 700;
color: var(--ink);
}
.gallery-card-desc {
font-size: var(--step--1);
color: var(--ink-muted);
line-height: 1.5;
}
/* Lightbox Dialog */
.lightbox-dialog {
background: rgba(14, 14, 16, 0.96);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: var(--radius-sm);
max-width: 1200px;
width: 95vw;
max-height: 92dvh;
padding: 0;
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.8);
}
.lightbox-dialog::backdrop {
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(8px);
}
.lightbox-layout {
display: grid;
grid-template-columns: 1fr;
}
@media (min-width: 900px) {
.lightbox-layout {
grid-template-columns: 1.8fr 1fr;
}
}
.lightbox-media {
background: #000000;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-4);
max-height: 80vh;
}
.lightbox-media img {
max-width: 100%;
max-height: 75vh;
object-fit: contain;
border-radius: var(--radius-xs);
}
.lightbox-info {
padding: var(--space-6);
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--space-4);
overflow-y: auto;
}
/* Blueprint interactive section */
.blueprint-section {
background: var(--surface-sunken);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
padding: var(--space-7) 0;
}
.blueprint-viewer {
display: grid;
grid-template-columns: 1fr;
gap: var(--space-6);
align-items: center;
}
@media (min-width: 960px) {
.blueprint-viewer {
grid-template-columns: 1.4fr 1fr;
}
}
.blueprint-frame {
border: 1px solid var(--line);
border-radius: var(--radius-sm);
overflow: hidden;
background: #ECE7DE;
box-shadow: 0 16px 36px rgba(0,0,0,0.06);
}
.blueprint-frame img {
width: 100%;
display: block;
}
</style>
</head>
<body data-ambiance="day">
<!-- 상단 헤더 (.site-head SSOT) -->
<header class="site-head">
<div class="wrap head-inner">
<a href="../../index.html" class="wordmark" aria-label="MATERIA 홈">
MATERIA <span class="wordmark-han">空間素材</span>
</a>
<nav class="nav" aria-label="주요 메뉴">
<a href="./index.html" class="active" style="color: var(--accent); font-weight: 600;">24개 공간 갤러리</a>
<a href="../../index.html#works">엄선 프로젝트</a>
<a href="../../index.html#materials">물성 아카이브</a>
<a href="../../index.html#process">설계 프로세스</a>
<a href="../../index.html#contact">프라이빗 상담</a>
</nav>
<div class="head-actions">
<div class="ambiance-switcher" role="group" aria-label="공간 조도 앰비언스 모드">
<button type="button" class="ambiance-btn is-active" id="btnAmbianceDay">Day</button>
<button type="button" class="ambiance-btn" id="btnAmbianceNight">Night</button>
</div>
<a href="../../index.html#contact" class="btn">
상담 신청
</a>
</div>
</div>
</header>
<main id="main-content">
<!-- Project Hero Section -->
<section class="section project-hero" style="border-top: none; padding-top: var(--space-5);">
<div class="wrap">
<div class="breadcrumb-bar" style="margin-bottom: var(--space-4); display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); letter-spacing: 0.04em;">
<a href="../../index.html">MATERIA</a>
<span class="sep">/</span>
<a href="../../index.html#works">PROJECT ARCHIVE</a>
<span class="sep">/</span>
<span class="current" style="color: var(--ink); font-weight: 600;">HANNAM DUPLEX PENTHOUSE (86PY)</span>
</div>
<div class="project-hero-head">
<p class="hero-label">CASE STUDY · RESIDENTIAL ARCHITECTURE · 86PY</p>
<h1 class="hero-title" style="margin-bottom: var(--space-2);">한남 듀플렉스 펜트하우스</h1>
<p class="hero-lead" style="font-size: var(--step-0); color: var(--ink-muted); max-width: var(--measure); margin-bottom: var(--space-5);">
5.8m 보이드 천장고와 로만 트래버틴, 3,000년 훈증 오크가 빚어낸 침묵의 공간.
</p>
<dl class="project-specs" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6);">
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">위치</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">서울시 용산구 한남동</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">면적</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">284㎡ (86평형 복층)</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">천장고</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">5.8m Void Ceiling</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">공사기간</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">14주 (2026.04 준공)</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">핵심 소재</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">Roman Navona Travertine</dd>
</div>
<div>
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 2px;">목공 마감</dt>
<dd class="spec-val" style="font-family: var(--font-serif); font-size: var(--step-0); font-weight: 700; color: var(--ink);">3,000Y Smoked Bog Oak</dd>
</div>
</dl>
</div>
<!-- Hero Image in Frame -->
<figure class="hero-photo" style="border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-card);">
<img
src="../../assets/hannam-01-living-void.webp"
alt="한남 듀플렉스 펜트하우스 5.8m 보이드 거실 전경과 트래버틴 벽체"
width="1600"
height="900"
style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
/>
<figcaption style="padding: 10px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); border-top: 1px solid var(--line); background: var(--surface-raised);">
Zone A · 5.8m 보이드 거실 &amp; 20mm 로만 나보나 트래버틴 아트월
</figcaption>
</figure>
</div>
</section>
<!-- Architectural Narrative Section -->
<section class="narrative-section">
<div class="wrap">
<div class="narrative-grid">
<div>
<div class="eyebrow">DESIGN PHILOSOPHY</div>
<h2 class="section-title" style="font-family: var(--font-serif); font-size: var(--step-2); font-weight: 700; line-height: 1.25;">
비움으로 완성한<br />빛과 물성의 교감
</h2>
</div>
<div class="narrative-text">
<p>
한남 듀플렉스 펜트하우스는 장식적 기교를 배제하고, 자연광의 궤적과 거대한 석재의 물성이 공간의 주인이 되도록 설계된 프로젝트입니다.
남향으로 열린 한강 조망과 5.8미터 복층 보이드 구조를 관통하는 자연광을 수학적으로 계산하여, 시간에 따라 석재 표면의 음영이 시시각각 변화하도록 유도했습니다.
</p>
<p>
벽체를 감싸는 20mm 로만 나보나 트래버틴은 이탈리아 티볼리 채석장에서 원판을 엄선하여 이음매의 결을 무지향성으로 연결했으며,
3,000년 세월 동안 탄화된 훈증 오크(Bog Oak)와 수공예 파티나 브론즈를 접합하여 시각적 깊이와 촉각적 정숙함을 부여했습니다.
</p>
</div>
</div>
</div>
</section>
<!-- Interactive 24-Piece Spatial Gallery Architecture -->
<section class="gallery-section" id="spatial-gallery">
<div class="wrap">
<div class="gallery-head">
<div class="eyebrow">SPATIAL EXPLORATION · 24 SHOTS</div>
<h2 class="section-title" style="font-family: var(--font-serif); font-size: var(--step-2); font-weight: 700;">공간별 심층 아카이브 갤러리</h2>
<p class="section-sub" style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 4px;">
각 카드를 클릭하시면 고해상도 확대 뷰어와 설계 물성 상세 명세를 확인하실 수 있습니다.
</p>
</div>
<!-- Zone Filters -->
<div class="gallery-filter-chips" role="tablist" aria-label="공간 존별 필터">
<button type="button" class="gallery-chip active" data-zone="all">전체 공간 (24)</button>
<button type="button" class="gallery-chip" data-zone="living">Zone A: 거실 & 다이닝 (4)</button>
<button type="button" class="gallery-chip" data-zone="master">Zone B: 마스터 스위트 & 스파 (4)</button>
<button type="button" class="gallery-chip" data-zone="study">Zone C: 서재 & 와인 & 드레스룸 (4)</button>
<button type="button" class="gallery-chip" data-zone="garden">Zone D: 테라스 & 중정 & 현관 (4)</button>
<button type="button" class="gallery-chip" data-zone="materials">Zone E: 물성 매크로 접사 (4)</button>
<button type="button" class="gallery-chip" data-zone="analysis">Zone F: 도면 & 광학 분석 & 디테일 (4)</button>
</div>
<!-- 24 Gallery Cards Grid -->
<div class="spatial-grid" id="spatialGrid">
<!-- Card 1 -->
<div class="gallery-card" data-zone="living" onclick="openLightbox(0)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-01-living-void.webp" alt="5.8m 보이드 거실" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone A · Living</span>
<h3 class="gallery-card-title">5.8m 보이드 그레이트 룸</h3>
<p class="gallery-card-desc">로만 트래버틴 일체형 벽난로와 2개 층을 관통하는 통창 채광 설계.</p>
</div>
</div>
<!-- Card 2 -->
<div class="gallery-card" data-zone="living" onclick="openLightbox(1)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-03-dining.webp" alt="다이닝 룸" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone A · Dining</span>
<h3 class="gallery-card-title">월넛 원목 슬랩 다이닝</h3>
<p class="gallery-card-desc">수제 브러시드 브론즈 펜던트 조명과 한강 파노라마 야경 조망.</p>
</div>
</div>
<!-- Card 3 -->
<div class="gallery-card" data-zone="living" onclick="openLightbox(2)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-04-kitchen.webp" alt="셰프 키친 아일랜드" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone A · Kitchen</span>
<h3 class="gallery-card-title">석재 통판 셰프 키친</h3>
<p class="gallery-card-desc">그레이 쿼츠자이트 아일랜드와 히든 플러시 훈증 오크 수납 시스템.</p>
</div>
</div>
<!-- Card 4 -->
<div class="gallery-card" data-zone="living" onclick="openLightbox(3)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-10-staircase.webp" alt="부유형 캔틸레버 계단" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone A · Staircase</span>
<h3 class="gallery-card-title">캔틸레버 부유식 오크 계단</h3>
<p class="gallery-card-desc">트래버틴 벽체에서 돌출된 무지주 솔리드 오크 디딤판과 매립 핸드레일.</p>
</div>
</div>
<!-- Card 5 -->
<div class="gallery-card" data-zone="master" onclick="openLightbox(4)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-05-master-bedroom.webp" alt="마스터 침실" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone B · Master Suite</span>
<h3 class="gallery-card-title">마스터 베드룸 스위트</h3>
<p class="gallery-card-desc">천연 리넨 헤드보드 월과 간접 라인 조명으로 완성한 정숙한 침실.</p>
</div>
</div>
<!-- Card 6 -->
<div class="gallery-card" data-zone="master" onclick="openLightbox(5)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-06-spa-bathroom.webp" alt="스파 욕실" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone B · Spa Bath</span>
<h3 class="gallery-card-title">화산석 통원석 스파 배스</h3>
<p class="gallery-card-desc">자연석을 통째로 깎아낸 원석 욕조와 독립형 파티나 브론즈 수전.</p>
</div>
</div>
<!-- Card 7 -->
<div class="gallery-card" data-zone="master" onclick="openLightbox(6)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-13-powder-room.webp" alt="파우더룸" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone B · Powder Room</span>
<h3 class="gallery-card-title">석재 세면대 파우더룸</h3>
<p class="gallery-card-desc">조각적인 통라임스톤 세면대와 수직 슬릿 조명이 빚은 명암.</p>
</div>
</div>
<!-- Card 8 -->
<div class="gallery-card" data-zone="master" onclick="openLightbox(7)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-14-guest-suite.webp" alt="게스트 침실" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone B · Guest Suite</span>
<h3 class="gallery-card-title">게스트 침실 스위트</h3>
<p class="gallery-card-desc">따뜻한 라임워시 도장 벽체와 천연 원목 가구의 안락한 조화.</p>
</div>
</div>
<!-- Card 9 -->
<div class="gallery-card" data-zone="study" onclick="openLightbox(8)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-07-library-audio.webp" alt="서재 및 오디오 룸" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone C · Library</span>
<h3 class="gallery-card-title">서재 & 하이파이 오디오 룸</h3>
<p class="gallery-card-desc">음향 반사를 고려한 훈증 오크 책장과 아날로그 턴테이블 석재 콘솔.</p>
</div>
</div>
<!-- Card 10 -->
<div class="gallery-card" data-zone="study" onclick="openLightbox(9)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-11-dressing-room.webp" alt="드레스룸" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone C · Dressing Room</span>
<h3 class="gallery-card-title">비스포크 워크인 드레스룸</h3>
<p class="gallery-card-desc">플루티드 브론즈 유리장과 시계/주얼리 수납 아일랜드 쇼케이스.</p>
</div>
</div>
<!-- Card 11 -->
<div class="gallery-card" data-zone="study" onclick="openLightbox(10)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-12-wine-cellar.webp" alt="와인 셀러 룸" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone C · Wine Cellar</span>
<h3 class="gallery-card-title">항온항습 글래스 와인 셀러</h3>
<p class="gallery-card-desc">백라이트 트래버틴 벽체와 브론즈 프레임 유리 파티션 셀러.</p>
</div>
</div>
<!-- Card 12 -->
<div class="gallery-card" data-zone="study" onclick="openLightbox(11)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-20-tea-room.webp" alt="티 룸 알코브" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone C · Tea Alcove</span>
<h3 class="gallery-card-title">명상 다도실 (Tea Room)</h3>
<p class="gallery-card-desc">천연 한지 스크린을 투과하는 부드러운 격자 채광과 원목 다도상.</p>
</div>
</div>
<!-- Card 13 -->
<div class="gallery-card" data-zone="garden" onclick="openLightbox(12)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-08-terrace.webp" alt="프라이빗 테라스" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone D · Terrace</span>
<h3 class="gallery-card-title">루프탑 프라이빗 테라스</h3>
<p class="gallery-card-desc">대형 트래버틴 페이버와 조형 소나무 분재, 한강 석양 조망.</p>
</div>
</div>
<!-- Card 14 -->
<div class="gallery-card" data-zone="garden" onclick="openLightbox(13)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-09-foyer.webp" alt="입구 현관 전경" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone D · Entrance</span>
<h3 class="gallery-card-title">미니멀 입구 현관 (Foyer)</h3>
<p class="gallery-card-desc">거대한 화강석 벤치 원석과 플러시 히든 피벗 도어 시스템.</p>
</div>
</div>
<!-- Card 15 -->
<div class="gallery-card" data-zone="garden" onclick="openLightbox(14)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-22-lightwell.webp" alt="실내 석경 중정" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone D · Lightwell</span>
<h3 class="gallery-card-title">실내 석경 중정 (Zen Lightwell)</h3>
<p class="gallery-card-desc">천장 보이드에서 쏟아지는 수직 채광과 백자갈 디딤돌 정원.</p>
</div>
</div>
<!-- Card 16 -->
<div class="gallery-card" data-zone="garden" onclick="openLightbox(15)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-21-art-niche.webp" alt="갤러리 아트 니치" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone D · Art Niche</span>
<h3 class="gallery-card-title">뮤지엄 갤러리 아트 니치</h3>
<p class="gallery-card-desc">트래버틴 조각 좌대와 정밀 프레이밍 조명으로 연출된 조각품.</p>
</div>
</div>
<!-- Card 17 -->
<div class="gallery-card" data-zone="materials" onclick="openLightbox(16)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-15-mat-travertine.webp" alt="로만 트래버틴 매크로" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone E · Material</span>
<h3 class="gallery-card-title">로만 나보나 트래버틴 접사</h3>
<p class="gallery-card-desc">자연이 형성한 미세한 다공성 기공과 따뜻한 꿀빛 석재 층위.</p>
</div>
</div>
<!-- Card 18 -->
<div class="gallery-card" data-zone="materials" onclick="openLightbox(17)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-16-mat-bogoak.webp" alt="3000년 훈증 오크 매크로" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone E · Material</span>
<h3 class="gallery-card-title">3,000년 탄화 훈증 오크</h3>
<p class="gallery-card-desc">시간이 빚어낸 깊은 차콜 에스프레소 나뭇결과 실키 매트 피니시.</p>
</div>
</div>
<!-- Card 19 -->
<div class="gallery-card" data-zone="materials" onclick="openLightbox(18)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-17-mat-linen.webp" alt="벨기에 리넨 매크로" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone E · Material</span>
<h3 class="gallery-card-title">벨기에 무표백 천연 리넨</h3>
<p class="gallery-card-desc">불규칙한 천연 실타래 슬럽 조직이 주는 촉각적 안도감과 빛 흡수력.</p>
</div>
</div>
<!-- Card 20 -->
<div class="gallery-card" data-zone="materials" onclick="openLightbox(19)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-18-mat-bronze.webp" alt="파티나 브론즈 매크로" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone E · Material</span>
<h3 class="gallery-card-title">수공예 파티나 브러시드 브론즈</h3>
<p class="gallery-card-desc">손길이 닿을수록 깊어지는 유기적 산화 피막의 은은한 광채.</p>
</div>
</div>
<!-- Card 21 -->
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(20)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-02-blueprint.webp" alt="건축 평면 설계도면" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone F · Blueprint</span>
<h3 class="gallery-card-title">86평 복층 건축 평면 설계도</h3>
<p class="gallery-card-desc">동선 효율과 채광축을 정밀하게 구획한 마스터 아키텍처 드래프트.</p>
</div>
</div>
<!-- Card 22 -->
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(21)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-23-light-simulation.webp" alt="3D 일조 광학 시뮬레이션" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone F · Analysis</span>
<h3 class="gallery-card-title">3D 태양광 입사 조도 분석</h3>
<p class="gallery-card-desc">하지 및 춘분 정오 55° 고도각에 따른 Lux 분포 및 음영 계산도.</p>
</div>
</div>
<!-- Card 23 -->
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(22)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-19-joinery.webp" alt="5mm 섀도우 갭 조인트" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone F · Detail</span>
<h3 class="gallery-card-title">5mm 음각 섀도우 갭 조인트</h3>
<p class="gallery-card-desc">석재와 원목 패널 사이의 0.5mm 오차 없는 스위스 규격 결합부.</p>
</div>
</div>
<!-- Card 24 -->
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(23)">
<div class="gallery-card-img-wrap">
<img src="../../assets/hannam-24-timber-joinery.webp" alt="장인 장부 맞춤 목공" loading="lazy" />
</div>
<div class="gallery-card-body">
<span class="gallery-card-zone">Zone F · Detail</span>
<h3 class="gallery-card-title">전통 장부 맞춤 마스터 목공</h3>
<p class="gallery-card-desc">못 없이 체결되는 장인 직영 정밀 결구 구조의 하드웨어.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Interactive Blueprint & Spec Analysis Viewer -->
<section class="blueprint-section">
<div class="wrap">
<div class="blueprint-viewer">
<div class="blueprint-frame">
<img src="../../assets/hannam-02-blueprint.webp" alt="한남 듀플렉스 펜트하우스 평면도" />
</div>
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
<div class="eyebrow" style="color: var(--accent);">TECHNICAL SPECIFICATION</div>
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
수치로 입증하는<br />하이엔드 시공 규격
</h2>
<div style="display: flex; flex-direction: column; gap: var(--space-3); font-size: var(--step--1);">
<div style="padding: var(--space-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);">
<strong style="color: var(--ink);">단차 허용 공차</strong>: 0.3mm 이내 (레이저 레벨링 및 통석재 현장 가공)
</div>
<div style="padding: var(--space-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);">
<strong style="color: var(--ink);">조명 색온도 제어</strong>: 2200K (석양 캔들) ~ 5500K (자연 주광) DALI 스마트 디밍
</div>
<div style="padding: var(--space-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);">
<strong style="color: var(--ink);">공조 매립 규격</strong>: 무몰딩 트림리스 선형 디퓨저 & 히든 점검구 100% 설계
</div>
</div>
<a href="../../index.html#consultation" class="btn-primary" style="display: inline-block; text-align: center; text-decoration: none; margin-top: var(--space-2);">
이 프로젝트 스타일로 상담 신청하기 →
</a>
</div>
</div>
</div>
</section>
</main>
<!-- Fullscreen Lightbox Modal Dialog -->
<dialog id="lightboxDialog" class="lightbox-dialog">
<div class="lightbox-layout">
<div class="lightbox-media">
<img id="lightboxImg" src="../../assets/hannam-01-living-void.webp" alt="확대 이미지" />
</div>
<div class="lightbox-info">
<div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-3);">
<span id="lightboxZone" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); font-weight: 700; text-transform: uppercase;"></span>
<button type="button" onclick="document.getElementById('lightboxDialog').close();" aria-label="닫기" style="background: none; border: none; color: #fff; font-size: 24px; cursor: pointer;">✕</button>
</div>
<h3 id="lightboxTitle" style="font-size: var(--step-1); font-weight: 700; margin-bottom: var(--space-2); color: #fff;"></h3>
<p id="lightboxDesc" style="font-size: var(--step--1); color: #a1a1aa; line-height: 1.6;"></p>
</div>
<div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: var(--space-4); display: flex; justify-content: space-between; align-items: center;">
<span id="lightboxIndex" style="font-family: var(--font-mono); font-size: 12px; color: #71717a;"></span>
<div style="display: flex; gap: 8px;">
<button type="button" onclick="prevLightbox()" style="padding: 6px 14px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 4px; cursor: pointer;">← 이전</button>
<button type="button" onclick="nextLightbox()" style="padding: 6px 14px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 4px; cursor: pointer;">다음 →</button>
</div>
</div>
</div>
</div>
</dialog>
<!-- 푸터 (.site-foot SSOT) -->
<footer class="site-foot">
<div class="wrap foot-inner">
<p>마테리아 스튜디오 · 서울 강남구 압구정로 72길 14</p>
<p class="muted">월요일부터 금요일까지 (사전 예약제)</p>
<p class="muted">studio@materia.kr</p>
</div>
</footer>
<!-- Interactive Scripts -->
<script>
// 1. Ambiance Day/Night Switcher
const btnDay = document.getElementById('btnAmbianceDay');
const btnNight = document.getElementById('btnAmbianceNight');
btnDay?.addEventListener('click', () => {
document.documentElement.removeAttribute('data-theme');
btnDay.classList.add('active');
btnNight.classList.remove('active');
});
btnNight?.addEventListener('click', () => {
document.documentElement.setAttribute('data-theme', 'night');
btnNight.classList.add('active');
btnDay.classList.remove('active');
});
// 2. 24 Gallery Items Data Store
const galleryData = [
{ img: '../../assets/hannam-01-living-void.webp', zone: 'Zone A · Living', title: '5.8m 보이드 그레이트 룸', desc: '로만 나보나 트래버틴 원판 벽체와 2개 층을 관통하는 통창 채광 설계.' },
{ img: '../../assets/hannam-03-dining.webp', zone: 'Zone A · Dining', title: '월넛 원목 슬랩 다이닝', desc: '수제 브러시드 브론즈 펜던트 조명과 한강 파노라마 야경 조망.' },
{ img: '../../assets/hannam-04-kitchen.webp', zone: 'Zone A · Kitchen', title: '석재 통판 셰프 키친', desc: '그레이 쿼츠자이트 아일랜드와 히든 플러시 훈증 오크 수납 시스템.' },
{ img: '../../assets/hannam-10-staircase.webp', zone: 'Zone A · Staircase', title: '캔틸레버 부유식 오크 계단', desc: '트래버틴 벽체에서 돌출된 무지주 솔리드 오크 디딤판과 매립 핸드레일.' },
{ img: '../../assets/hannam-05-master-bedroom.webp', zone: 'Zone B · Master Suite', title: '마스터 베드룸 스위트', desc: '천연 리넨 헤드보드 월과 간접 라인 조명으로 완성한 정숙한 침실.' },
{ img: '../../assets/hannam-06-spa-bathroom.webp', zone: 'Zone B · Spa Bath', title: '화산석 통원석 스파 배스', desc: '자연석을 통째로 깎아낸 원석 욕조와 독립형 파티나 브론즈 수전.' },
{ img: '../../assets/hannam-13-powder-room.webp', zone: 'Zone B · Powder Room', title: '석재 세면대 파우더룸', desc: '조각적인 통라임스톤 세면대와 수직 슬릿 조명이 빚은 명암.' },
{ img: '../../assets/hannam-14-guest-suite.webp', zone: 'Zone B · Guest Suite', title: '게스트 침실 스위트', desc: '따뜻한 라임워시 도장 벽체와 천연 원목 가구의 안락한 조화.' },
{ img: '../../assets/hannam-07-library-audio.webp', zone: 'Zone C · Library', title: '서재 & 하이파이 오디오 룸', desc: '음향 반사를 고려한 훈증 오크 책장과 아날로그 턴테이블 석재 콘솔.' },
{ img: '../../assets/hannam-11-dressing-room.webp', zone: 'Zone C · Dressing Room', title: '비스포크 워크인 드레스룸', desc: '플루티드 브론즈 유리장과 시계/주얼리 수납 아일랜드 쇼케이스.' },
{ img: '../../assets/hannam-12-wine-cellar.webp', zone: 'Zone C · Wine Cellar', title: '항온항습 글래스 와인 셀러', desc: '백라이트 트래버틴 벽체와 브론즈 프레임 유리 파티션 셀러.' },
{ img: '../../assets/hannam-20-tea-room.webp', zone: 'Zone C · Tea Alcove', title: '명상 다도실 (Tea Room)', desc: '천연 한지 스크린을 투과하는 부드러운 격자 채광과 원목 다도상.' },
{ img: '../../assets/hannam-08-terrace.webp', zone: 'Zone D · Terrace', title: '루프탑 프라이빗 테라스', desc: '대형 트래버틴 페이버와 조형 소나무 분재, 한강 석양 조망.' },
{ img: '../../assets/hannam-09-foyer.webp', zone: 'Zone D · Entrance', title: '미니멀 입구 현관 (Foyer)', desc: '거대한 화강석 벤치 원석과 플러시 히든 피벗 도어 시스템.' },
{ img: '../../assets/hannam-22-lightwell.webp', zone: 'Zone D · Lightwell', title: '실내 석경 중정 (Zen Lightwell)', desc: '천장 보이드에서 쏟아지는 수직 채광과 백자갈 디딤돌 정원.' },
{ img: '../../assets/hannam-21-art-niche.webp', zone: 'Zone D · Art Niche', title: '뮤지엄 갤러리 아트 니치', desc: '트래버틴 조각 좌대와 정밀 프레이밍 조명으로 연출된 조각품.' },
{ img: '../../assets/hannam-15-mat-travertine.webp', zone: 'Zone E · Material', title: '로만 나보나 트래버틴 접사', desc: '자연이 형성한 미세한 다공성 기공과 따뜻한 꿀빛 석재 층위.' },
{ img: '../../assets/hannam-16-mat-bogoak.webp', zone: 'Zone E · Material', title: '3,000년 탄화 훈증 오크', desc: '시간이 빚어낸 깊은 차콜 에스프레소 나뭇결과 실키 매트 피니시.' },
{ img: '../../assets/hannam-17-mat-linen.webp', zone: 'Zone E · Material', title: '벨기에 무표백 천연 리넨', desc: '불규칙한 천연 실타래 슬럽 조직이 주는 촉각적 안도감과 빛 흡수력.' },
{ img: '../../assets/hannam-18-mat-bronze.webp', zone: 'Zone E · Material', title: '수공예 파티나 브러시드 브론즈', desc: '손길이 닿을수록 깊어지는 유기적 산화 피막의 은은한 광채.' },
{ img: '../../assets/hannam-02-blueprint.webp', zone: 'Zone F · Blueprint', title: '86평 복층 건축 평면 설계도', desc: '동선 효율과 채광축을 정밀하게 구획한 마스터 아키텍처 드래프트.' },
{ img: '../../assets/hannam-23-light-simulation.webp', zone: 'Zone F · Analysis', title: '3D 태양광 입사 조도 분석', desc: '하지 및 춘분 정오 55° 고도각에 따른 Lux 분포 및 음영 계산도.' },
{ img: '../../assets/hannam-19-joinery.webp', zone: 'Zone F · Detail', title: '5mm 음각 섀도우 갭 조인트', desc: '석재와 원목 패널 사이의 0.5mm 오차 없는 스위스 규격 결합부.' },
{ img: '../../assets/hannam-24-timber-joinery.webp', zone: 'Zone F · Detail', title: '전통 장부 맞춤 마스터 목공', desc: '못 없이 체결되는 장인 직영 정밀 결구 구조의 하드웨어.' }
];
// 3. Filter Chips Logic
const filterChips = document.querySelectorAll('.gallery-chip');
const galleryCards = document.querySelectorAll('.gallery-card');
filterChips.forEach(chip => {
chip.addEventListener('click', () => {
filterChips.forEach(c => c.classList.remove('active'));
chip.classList.add('active');
const zone = chip.getAttribute('data-zone');
galleryCards.forEach(card => {
if (zone === 'all' || card.getAttribute('data-zone') === zone) {
card.style.display = 'flex';
} else {
card.style.display = 'none';
}
});
});
});
// 4. Lightbox Modal Logic
let currentLightboxIdx = 0;
const lightboxModal = document.getElementById('lightboxDialog');
const lightboxImg = document.getElementById('lightboxImg');
const lightboxZone = document.getElementById('lightboxZone');
const lightboxTitle = document.getElementById('lightboxTitle');
const lightboxDesc = document.getElementById('lightboxDesc');
const lightboxIndex = document.getElementById('lightboxIndex');
function openLightbox(idx) {
currentLightboxIdx = idx;
renderLightbox();
lightboxModal.showModal();
}
function renderLightbox() {
const item = galleryData[currentLightboxIdx];
if (!item) return;
lightboxImg.src = item.img;
lightboxImg.alt = item.title;
lightboxZone.textContent = item.zone;
lightboxTitle.textContent = item.title;
lightboxDesc.textContent = item.desc;
lightboxIndex.textContent = `${currentLightboxIdx + 1} / ${galleryData.length}`;
}
function prevLightbox() {
currentLightboxIdx = (currentLightboxIdx - 1 + galleryData.length) % galleryData.length;
renderLightbox();
}
function nextLightbox() {
currentLightboxIdx = (currentLightboxIdx + 1) % galleryData.length;
renderLightbox();
}
lightboxModal.addEventListener('click', (e) => {
if (e.target === lightboxModal) lightboxModal.close();
});
</script>
</body>
</html>

View file

@ -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); }

View file

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

View file

@ -0,0 +1,64 @@
---
interface Props {
class?: string;
}
const { class: className } = Astro.props;
---
<div class:list={["hero-gl-wrap", className]} aria-hidden="true">
<div class="hero-gl-fallback"></div>
<canvas id="showcase-gl" class="hero-gl-canvas" style="opacity: 0;"></canvas>
<div class="hero-gl-scrim"></div>
</div>
<script>
import { boot } from "../gl/boot.ts";
const canvas = document.getElementById("showcase-gl") as HTMLCanvasElement | null;
if (canvas) {
boot(canvas, () => import("../gl/scenes/showcase-hero.ts"));
}
</script>
<style>
.hero-gl-wrap {
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
overflow: hidden;
mask-image: linear-gradient(to bottom, black 70%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, black 70%, transparent 100%);
}
.hero-gl-fallback {
position: absolute;
inset: 0;
background:
radial-gradient(85% 60% at 50% -10%, color-mix(in oklab, var(--accent) 18%, transparent) 0%, transparent 70%),
radial-gradient(60% 50% at 85% 20%, rgba(217, 155, 56, 0.12) 0%, transparent 60%),
linear-gradient(180deg, var(--surface-sunken) 0%, var(--surface) 100%);
}
.hero-gl-canvas {
position: absolute;
inset: 0;
width: 100% !important;
height: 100% !important;
max-width: none !important;
display: block;
}
.hero-gl-scrim {
position: absolute;
inset: 0;
background: radial-gradient(circle at 50% 30%, rgba(12, 16, 23, 0.35) 0%, rgba(12, 16, 23, 0.8) 100%);
pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
.hero-gl-canvas {
display: none !important;
}
}
</style>

View file

@ -34,6 +34,12 @@ import gaonLms from "../assets/shots/work-gaon-lms.png";
import gaonLms2 from "../assets/shots/work-gaon-lms-2.png"; import gaonLms2 from "../assets/shots/work-gaon-lms-2.png";
import dureEnrollment from "../assets/shots/work-dure-enrollment.png"; import dureEnrollment from "../assets/shots/work-dure-enrollment.png";
import dureEnrollment2 from "../assets/shots/work-dure-enrollment-2.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 { export interface WorkItem {
slug: string; slug: string;
@ -45,6 +51,7 @@ export interface WorkItem {
date: string; date: string;
metrics: string[]; metrics: string[];
href: string; href: string;
subLinks?: { label: string; href: string }[];
shot: ImageMetadata; shot: ImageMetadata;
shotAlt?: ImageMetadata; shotAlt?: ImageMetadata;
alt: string; alt: string;
@ -258,4 +265,60 @@ export const WORK: WorkItem[] = [
alt: "두레대학 수강신청 — 과목 원장 목록과 주홍 도장 블록이 놓이는 주간 시간표 격자", alt: "두레대학 수강신청 — 과목 원장 목록과 주홍 도장 블록이 놓이는 주간 시간표 격자",
size: "md", 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",
},
]; ];

71
apps/site/src/gl/boot.ts Normal file
View file

@ -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<void> {
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);
});
}

View file

@ -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();
};
}

133
apps/site/src/gl/stage.ts Normal file
View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -7,10 +7,21 @@
*::before, *::before,
*::after { box-sizing: border-box; } *::after { box-sizing: border-box; }
img,
picture,
video,
canvas,
svg {
max-width: 100%;
height: auto;
display: block;
}
html { html {
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
scroll-behavior: smooth; scroll-behavior: smooth;
background: var(--surface); background: var(--surface);
overflow-x: hidden;
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; } html { scroll-behavior: auto; }

View file

@ -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();

View file

@ -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(", ") || "없음");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

View file

@ -1,4 +1,4 @@
# E2E 시나리오 게이트 — 2026-08-23 06:30 # E2E 시나리오 게이트 — 2026-08-23 12:48
단언 1805 · 통과 1805 · 실패 0 — **GREEN — 전 단언 통과** 단언 1805 · 통과 1805 · 실패 0 — **GREEN — 전 단언 통과**

View file

@ -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 | | L0-정식/html-validate | PASS | PASS — | 1.3s |
| L1-단위/계산로직 | PASS | PASS 41 | 0.1s | | L1-단위/계산로직 | PASS | PASS 41 | 0.1s |
| L2-기능/감사 | PASS | PASS 72 | 9.7s | | L2-기능/감사 | PASS | PASS 72 | 10.2s |
| L2-불변식/렌더 | PASS | PASS 10 | 17.2s | | L2-불변식/렌더 | PASS | PASS 10 | 17.5s |
| L3-시각회귀/diff | PASS | PASS 30 | 11.7s | | L3-시각회귀/diff | PASS | PASS 30 | 12.2s |
| L4-WebKit | PASS | PASS 8 | 3.9s | | L4-WebKit | PASS | PASS 8 | 4.7s |
| L5-탐색/키보드 | PASS | PASS 14 | 6.3s | | L5-탐색/키보드 | PASS | PASS 14 | 6.4s |
| L6-시나리오/E2E | PASS | PASS — | 45.4s | | L6-시나리오/E2E | PASS | PASS — | 42.6s |

View file

@ -1,5 +1,14 @@
# designpaca # designpaca
## 0.9.1
### Patch Changes
- CSS 변수 및 토큰 안전 폴백 의무화 (`var(--step-4, clamp(...))`) — 토큰 누락 시 16px 축소 및 위계 역전 방지
- 서브페이지 및 에셋 경로 무결성 게이트 — 404 브로큰 링크 차단 및 안전 리다이렉트 동봉
- 다단 데이터 테이블 1120px 컨테이너 핏 및 테마 맞춤 앰버/다크 커스텀 스크롤바 표준화
- 완료 선언 전 헤드리스 렌더링 실측 게이트 (`getComputedStyle` 폰트/패딩/오버플로) 강화
## 0.9.0 ## 0.9.0
### Minor Changes ### Minor Changes

View file

@ -1,6 +1,6 @@
{ {
"name": "designpaca", "name": "designpaca",
"version": "0.9.0", "version": "0.9.1",
"description": "웹 디자인 파이프라인 스킬 — Claude Code · Codex · Cursor 에 한 줄로 설치한다", "description": "웹 디자인 파이프라인 스킬 — Claude Code · Codex · Cursor 에 한 줄로 설치한다",
"keywords": [ "keywords": [
"design", "design",

View file

@ -1,5 +1,11 @@
# @designpaca/core # @designpaca/core
## 0.9.1
### Patch Changes
- Internal core engine synchronization for 0.9.1 release.
## 0.9.0 ## 0.9.0
### Minor Changes ### Minor Changes

View file

@ -1,6 +1,6 @@
{ {
"name": "@designpaca/core", "name": "@designpaca/core",
"version": "0.9.0", "version": "0.9.1",
"private": true, "private": true,
"description": "designpaca 설치 엔진 — 타깃 어댑터, 매니페스트, 드리프트 감지", "description": "designpaca 설치 엔진 — 타깃 어댑터, 매니페스트, 드리프트 감지",
"type": "module", "type": "module",

View file

@ -1,5 +1,14 @@
# CHANGELOG # CHANGELOG
## 0.9.1
### Patch Changes
- CSS 변수 및 토큰 안전 폴백 의무화 (`var(--step-4, clamp(...))`) — 토큰 누락 시 16px 축소 및 위계 역전 방지
- 서브페이지 및 에셋 경로 무결성 게이트 — 404 브로큰 링크 차단 및 안전 리다이렉트 동봉
- 다단 데이터 테이블 1120px 컨테이너 핏 및 테마 맞춤 앰버/다크 커스텀 스크롤바 표준화
- 완료 선언 전 헤드리스 렌더링 실측 게이트 (`getComputedStyle` 폰트/패딩/오버플로) 강화
## 0.9.0 ## 0.9.0
### Minor Changes ### Minor Changes

View file

@ -1,6 +1,6 @@
{ {
"name": "@designpaca/skill", "name": "@designpaca/skill",
"version": "0.9.0", "version": "0.9.1",
"private": true, "private": true,
"description": "designpaca 스킬 원본 — SKILL.md 와 참조 문서", "description": "designpaca 스킬 원본 — SKILL.md 와 참조 문서",
"scripts": { "scripts": {