release: v0.11.0

This commit is contained in:
Yun Chan 2026-09-12 15:28:46 +09:00
parent 3c39a1731d
commit 45ba92ea82
45 changed files with 1459 additions and 1887 deletions

View file

@ -0,0 +1,47 @@
# design.md — 한성활자연구소
## 브리프
- **무엇을**: 가상 서체 기획을 정직하게 밝히면서, 실제 마루 부리와 Pretendard로 문장 조판의 차이를 즉시 체험하는 한국어 타입 스튜디오.
- **누구에게**: 활자를 고르거나 본문 조판 값을 결정해야 하는 편집자·디자이너·개발자.
- **제약**: ko-KR 한국어, 밝은 아이보리·검정 본문·버밀리언 정체성 유지. 가상 서체를 판매하거나 가짜 스펙을 제시하지 않으며, 본문은 최대 42rem. 320px부터 가로 넘침이 없어야 한다.
## 리디자인 감사와 레퍼런스
이전 판은 한 글자 표본과 실제 렌더 서체의 관계가 멀고, 11,172자·1000 UPM 같은 가상 수치가 체험보다 앞섰다. 실제 사용자가 판단할 수 있는 것은 문장 안의 폭·획·간격이므로 그 역할을 첫 화면으로 올렸다.
| 출처 ID · 슬롯 | 출처 | 관찰 | 채택한 원리와 검증 |
| --- | --- | --- | --- |
| `KLIM-FONTS` · R1 구조 | https://klim.co.nz/fonts/ | Display, Text, Narrow, Condensed처럼 같은 계열의 역할과 폭을 나란히 제시한다. | 글꼴을 사양 표가 아니라 읽는 역할로 비교한다. 같은 문장을 두 역할로 렌더해 비교한다. |
| `CHIPPERFIELD-PROJECTS` · R2 톤 | https://davidchipperfield.com/projects | 프로젝트명·연도·필터가 정렬선과 짧은 메타 안에서 읽힌다. | 큰 표본 아래에 기준선, 짧은 메타, 한 가지 행동을 정렬한다. 1440px 첫 화면에서 표본·상태·CTA의 순서를 확인한다. |
| `SANDOLL-TYPELAB` · R3 디테일 | https://typelab.sandoll.co.kr/experience | 권장규격과 맞춤규격, 한글 범위를 선택하며 표본을 실제 작업으로 연결한다. | 버튼과 조판 슬라이더가 즉시 보이는 결과를 바꾸게 한다. 키보드·range·리셋으로 값을 바꾸어 검증한다. |
| `WCAG-READ` · 접근성 | 200% 텍스트 확대와 320 CSS px reflow를 핵심 화면에서 별도로 시험한다. | 큰 활자 장면은 접근성 제목을 대신하지 않는다. | 320px 200% 텍스트에서 문서 폭 320px, h1 80px을 기록하고 장식 표본은 `aria-hidden`으로 둔다. |
## 방향
- **컨셉**: 문장이 보여 주는 활자의 차이.
- **프리셋**: `editorial` — 읽는 시간이 주제이므로 따뜻한 바탕, 제한된 색, 좁은 본문 폭, 세리프 표제를 쓴다.
- **감수한 리스크**: 이미지를 히어로에서 빼고 버밀리언 세 글자 표본 하나를 가장 큰 장면으로 사용한다. 제목과 설명은 별도 DOM으로 남겨 접근성 판단을 표본에 기대지 않는다.
## 토큰과 구현
- **폰트**: 마루 부리는 제목·조판 시험대·표본의 제목 상태, Pretendard는 본문·기본 표본 상태. 두 폰트 모두 CDN 웹폰트이며 `font-synthesis-weight: none`으로 합성 굵기를 막는다.
- **색**: `--surface #f5f1e8`, `--ink #26221d`, `--accent #a63d2f`. 버밀리언은 표본과 행동에만 쓴다.
- **크기**: h1 `clamp(40px, 1.3rem + 4.2vw, 80px)`, 표본 `clamp(96px, 17.5vw, 260px)`, 본문 폭 최대 `42rem`.
- **레이아웃**: 데스크톱 표본 레일 최대 1700px, 일반 콘텐츠 1216px. 모바일은 최소 20px 인셋을 지키고 표본은 한 줄에 남는다.
- **상호작용**: native 버튼 두 개의 `aria-pressed`가 표본 폰트와 상태 문구를 바꾼다. 기존의 크기·자간·행간 range와 리셋은 유지한다.
- **성능**: 새 JavaScript 라이브러리·이미지·애니메이션 없음. 초기 체험은 인라인 DOM 코드만 사용한다. 활자 케이스의 두 장면은 생성 이미지임을 캡션으로 밝힌다.
## 의도적으로 하지 않은 것
- 마루 부리·Pretendard를 가상의 한성 서체로 속여 판매하지 않는다. 가상 기획과 실제 구현 폰트를 표본 바로 위와 푸터에 밝힌다.
- 인장·붓글씨·세로쓰기·종이결처럼 한국 활자를 이국화하는 장식을 사용하지 않는다. 활자의 형태와 조판만으로 분위기를 만든다.
- 움직임으로 활자의 차이를 설명하지 않는다. reduced motion에서도 같은 표본과 모든 조작이 남는다.
## 검증 기록 · 2026-09-12
- `outputs/design-expertise-20260912/font/font-redesign-e2e.mjs`가 독립 정적 서버에서 320·390·768·1440·1920·2560px를 열었다. 각 폭의 문서 `scrollWidth`는 뷰포트 폭과 같고, 내비는 한 줄, 버튼은 최소 44px이었다.
- 320px에서 200% 텍스트 확대 후 문서 폭은 320px, 서술형 h1은 80px으로 실제 확대됐다. 장식 표본은 `aria-hidden="true"`로 남았다.
- `prefers-reduced-motion: reduce`에서 `scroll-behavior`는 `auto`였다. 키보드 Space로 제목 버튼을 선택하면 `aria-pressed`와 상태 문구가 마루 부리로 바뀌었다.
- 세 range는 24px·0.05em·2.2를 표본과 캡션에 적용했고, 리셋은 17px·0em·1.8을 둘 다 복원했다.
- 지연 이미지 두 장은 scrollIntoView 뒤 `decode()`를 통과했고, `metaltype.webp` 응답은 HTTP 200·naturalWidth 900이었다. `font-foundry-1440-first.png`, `font-foundry-390-first.png`, 전체 캡처와 JSON 보고서는 같은 outputs 폴더에 있다.

View file

@ -3,287 +3,165 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>한성활자연구소 · 본문용 한글 활자</title>
<meta
name="description"
content="획의 장력과 여백의 비례. 본문 조판을 위해 설계한 한글 활자 두 종과 조판 시험대. 1000 UPM, 측정 가능한 자간과 행간."
/>
<meta property="og:title" content="한성활자연구소 · 본문용 한글 활자" />
<meta
property="og:description"
content="본문 조판을 위해 설계한 한글 활자와 조판 시험대."
/>
<title>한성활자연구소 · 읽는 리듬을 만드는 활자</title>
<meta name="description" content="문장으로 확인하는 한글 조판. 마루 부리와 Pretendard의 역할을 비교하고 글자 크기·자간·행간을 직접 조절해 보세요." />
<meta property="og:title" content="한성활자연구소 · 읽는 리듬을 만드는 활자" />
<meta property="og:description" content="문장으로 확인하는 한글 조판과 즉시 체험 시험대." />
<meta property="og:type" content="website" />
<meta name="theme-color" content="#F5F1E8" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" 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="./styles/main.css?v=2" />
<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='%23F5F1E8'/%3E%3Crect x='8' y='8' width='16' height='16' fill='%23A63D2F'/%3E%3C/svg%3E"
/>
<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="./styles/main.css?v=3" />
<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='%23F5F1E8'/%3E%3Crect x='8' y='8' width='16' height='16' fill='%23A63D2F'/%3E%3C/svg%3E" />
</head>
<body>
<a class="skip" href="#main">본문으로 건너뛰기</a>
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="#main">한성활자연구소</a>
<nav class="nav" aria-label="섹션">
<a href="#typefaces">활자</a>
<a href="#bench">시험대</a>
<a href="#bench">조판해 보기</a>
<a href="#case">활자 케이스</a>
<a href="#order">구입</a>
<a href="#order">라이선스 안내</a>
</nav>
</div>
</header>
<main id="main">
<!--
스페시멘 히어로 — 파운드리의 첫 화면은 설명이 아니라 글자다.
'한' 한 글자를 페이지에서 가장 크게, 글자 자체의 비례로 보여준다.
-->
<section class="hero" aria-labelledby="hero-h">
<div class="wrap">
<p class="hero-kicker reveal">본문용 한글 활자 · 1000 UPM · 2018년 창립</p>
<h1 id="hero-h" class="hero-glyph reveal" style="--delay: 60ms" lang="ko" aria-label="한">한</h1>
<div class="hero-side">
<p class="hero-statement">
이 글자가 어디에 쓰이는지는 이 페이지 전체가 증명합니다. 우리는
제목용 활자를 만들지 않습니다 — 오래 읽어야 하는 본문의 활자만
만듭니다. 획의 장력, 자간의 균형, 행간의 숨을 그 단위(UPM)로
측정합니다.
</p>
<dl class="hero-facts reveal" style="--delay: 120ms">
<div>
<dt>서체</dt>
<dd>한성본문 · 한성제목</dd>
</div>
<div>
<dt>글자 수</dt>
<dd>11,172자 완성</dd>
</div>
<div>
<dt>단위</dt>
<dd>1000 UPM · 가변 웨이트 3종</dd>
</div>
</dl>
<div class="hero-rail">
<div class="hero-intro">
<div>
<p class="eyebrow">한글 타입 스튜디오</p>
<h1 id="hero-h">읽는 리듬을<br />만드는 활자.</h1>
</div>
<div class="hero-note">
<p>글자는 모양만으로 읽히지 않습니다. 문장 안에서 어떤 폭으로 서고, 얼마나 가까이 호흡하는지가 읽는 시간을 바꿉니다.</p>
<a class="text-link" href="#bench">조판해 보기 <span aria-hidden="true">↓</span></a>
</div>
</div>
</div>
</section>
<!--
활자 두 종 — 스페시멘은 실제 문장으로. Lorem ipsum 은 활자시험의 반대다.
-->
<section id="typefaces" class="section" aria-labelledby="typefaces-h">
<div class="wrap">
<h2 id="typefaces-h">활자 두 종</h2>
<article class="typeface reveal">
<header class="typeface-head">
<h3>한성본문</h3>
<p class="muted">본문용 · 웨이트 400 · 16px에서 행간 1.8</p>
</header>
<p class="specimen" style="font-family: 'MaruBuri', serif">
아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서
소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다. 획이 닿는
각도와 글자가 서로 미는 힘을 천 번쯤 고쳐 썼다. 그 결과 이 문장이
밤늦게도 읽히는 것이다.
</p>
</article>
<article class="typeface reveal" style="--delay: 80ms">
<header class="typeface-head">
<h3>한성제목</h3>
<p class="muted">제목·표제용 · 웨이트 600 · 큰 짝에서 벌어짐 0</p>
</header>
<p class="specimen specimen-display" style="font-family: 'MaruBuri', serif">
활자는 읽는 리듬을 설계한다
</p>
</article>
</div>
</section>
<!--
조판 시험대 — 이 페이지의 리스크이자 유일한 인터랙션.
슬라이더 세 개가 실제 본문에 즉시 반영된다. 라벨은 한국어, 값은 숫자로.
-->
<section id="bench" class="section band" aria-labelledby="bench-h">
<div class="wrap">
<h2 id="bench-h">조판 시험대</h2>
<p class="muted bench-lead">
자간과 행간, 글자 크기가 가독성을 어떻게 바꾸는지 직접 움직여 보세요.
아래 값은 권장값이 아니라 출발점입니다 — 용지 규격과 본문 길이에 따라
정답은 달라집니다.
</p>
<div class="bench">
<form class="bench-controls" aria-label="조판 설정">
<div class="control">
<div class="control-head"><label for="ctl-size">글자 크기</label><output id="out-size" for="ctl-size">17</output></div>
<input type="range" id="ctl-size" min="13" max="26" step="1" value="17" />
<div class="specimen-stage" data-kind="body">
<p class="stage-label">가상 서체 기획 · 화면은 마루 부리와 Pretendard로 구현</p>
<p class="hero-specimen" aria-hidden="true">글의 결</p>
<div class="baseline" aria-hidden="true"></div>
<div class="stage-footer">
<div class="type-switch" role="group" aria-label="표본 글꼴 선택">
<button type="button" class="type-choice" data-kind="body" aria-pressed="true">본문</button>
<button type="button" class="type-choice" data-kind="title" aria-pressed="false">제목</button>
</div>
<div class="control">
<div class="control-head"><label for="ctl-tracking">자간</label><output id="out-tracking" for="ctl-tracking">0</output></div>
<input type="range" id="ctl-tracking" min="-5" max="10" step="1" value="0" />
</div>
<div class="control">
<div class="control-head"><label for="ctl-leading">행간</label><output id="out-leading" for="ctl-leading">1.8</output></div>
<input type="range" id="ctl-leading" min="1.4" max="2.4" step="0.1" value="1.8" />
</div>
<button type="button" class="btn-reset" id="ctl-reset">권장값으로</button>
</form>
<div
class="bench-sample"
id="bench-sample"
style="font-family: 'MaruBuri', serif"
aria-live="polite"
>
활자의 역사는 글자를 찍는 기계의 역사가 아니라, 글자를 읽는 눈의
역사였다. 활자소의 식자공들은 하루에 만 글자를 짜면서 어느 글꼴이
눈을 덜 아프게 하는지 알고 있었다. 그 지식은 문서로 남지 않았고
손끝으로만 전해졌다. 이 시험대는 그 손끝의 판단을 누구나 해볼 수
있는 눈금으로 옮긴 것이다. 자간을 0에서 5로 옮겨 보라 — 문장이
늘어지는 것이 아니라 숨이 가빠지는 것을 느낄 것이다.
<p id="type-status" class="type-status" aria-live="polite">본문 표본 · Pretendard · 또렷한 문장 폭</p>
</div>
</div>
</div>
</section>
<section id="typefaces" class="section" aria-labelledby="typefaces-h">
<div class="wrap">
<div class="section-head">
<p class="eyebrow">두 가지 읽기 방식</p>
<h2 id="typefaces-h">같은 문장이<br />다른 속도로 읽힌다.</h2>
</div>
<div class="comparison">
<article class="typeface">
<header class="typeface-head">
<h3>본문</h3>
<p class="muted">Pretendard · 작고 긴 문장에 필요한 명료한 골격</p>
</header>
<p class="specimen specimen-body">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.</p>
</article>
<article class="typeface typeface-title">
<header class="typeface-head">
<h3>제목</h3>
<p class="muted">마루 부리 · 표제에서 멈춤과 여백을 만드는 결</p>
</header>
<p class="specimen specimen-display">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.</p>
</article>
</div>
</div>
</section>
<section id="bench" class="section band" aria-labelledby="bench-h">
<div class="wrap">
<div class="bench-heading">
<div>
<p class="eyebrow">조판 시험대</p>
<h2 id="bench-h">읽는 호흡을<br />직접 맞춰 보세요.</h2>
</div>
<p class="muted bench-lead">크기는 문장의 밀도를, 자간은 낱글자 사이의 공기를, 행간은 다음 줄로 넘어가는 속도를 바꿉니다. 하나씩 움직여 같은 문장이 어떻게 달라지는지 비교해 보세요.</p>
</div>
<div class="bench">
<form class="bench-controls" aria-label="조판 설정">
<div class="control"><div class="control-head"><label for="ctl-size">글자 크기</label><output id="out-size" for="ctl-size">17px</output></div><input type="range" id="ctl-size" name="size" min="13" max="26" step="1" value="17" /></div>
<div class="control"><div class="control-head"><label for="ctl-tracking">자간</label><output id="out-tracking" for="ctl-tracking">0em</output></div><input type="range" id="ctl-tracking" name="tracking" min="-5" max="10" step="1" value="0" /></div>
<div class="control"><div class="control-head"><label for="ctl-leading">행간</label><output id="out-leading" for="ctl-leading">1.8</output></div><input type="range" id="ctl-leading" name="leading" min="1.4" max="2.4" step="0.1" value="1.8" /></div>
<button type="button" class="btn-reset" id="ctl-reset">기본값으로</button>
</form>
<div class="bench-reading">
<p id="bench-caption" class="bench-caption">마루 부리 · 17px · 자간 0em · 행간 1.8</p>
<div class="bench-sample" id="bench-sample" aria-live="polite">활자의 역사는 글자를 찍는 기계의 역사가 아니라, 글자를 읽는 눈의 역사였다. 활자소의 식자공들은 하루에 만 글자를 짜면서 어느 글꼴이 눈을 덜 아프게 하는지 알고 있었다. 그 지식은 문서로 남지 않았고 손끝으로만 전해졌다. 이 미리보기는 그 판단을 눈으로 비교할 수 있게 옮긴 것입니다. 자간을 0에서 5로 바꾸면 문단의 호흡과 줄 길이가 어떻게 달라지는지 확인할 수 있습니다.</div>
</div>
</div>
</div>
</section>
<!--
활자 케이스 — 연구소의 재료. 금속 활자와 편집 도구.
-->
<section id="case" class="section" aria-labelledby="case-h">
<div class="wrap case-grid">
<figure class="case-photo reveal">
<img
src="./assets/metaltype.webp"
alt="나무 활자 케이스에 골라 담긴 금속 활자들 — 활자면이 위에서 빛을 받아 결이 보인다"
width="900"
height="1200"
loading="lazy"
/>
<figcaption>연구소 2층 · 금속 활자 케이스</figcaption>
<figure class="case-photo">
<img src="./assets/metaltype.webp" alt="나무 활자 케이스에 골라 담긴 금속 활자들 — 활자면이 위에서 빛을 받아 결이 보인다" width="900" height="1200" loading="lazy" />
<figcaption>생성 이미지 · 금속 활자 케이스의 관찰 장면</figcaption>
</figure>
<div class="case-text">
<h2 id="case-h">금속에서 시작해<br />화면으로 끝난다</h2>
<p>
한성본문의 자간 규칙은 금속 활자의 '옆면 여백'에서 왔습니다. 납활자는
글자 바로 옆에 살이 붙어 있어서, 찍히는 글자 사이는 언제나 일정했고
그 일정함이 읽는 리듬이었습니다. 화면용 폰트는 그 살을 지울 수
있지만, 우리는 지우지 않았습니다.
</p>
<figure class="case-photo-b reveal" style="--delay: 80ms">
<img
src="./assets/metaltype.webp"
alt="빛을 받은 금속 활자와 정렬된 활자 케이스"
width="1100"
height="825"
loading="lazy"
/>
<figcaption>활자 폭과 여백을 비교하는 편집 자리</figcaption>
<p class="eyebrow">활자 케이스</p>
<h2 id="case-h">금속에서 시작해<br />화면에서 읽힌다.</h2>
<p>이 가상 기획은 금속 활자의 옆면 여백을 관찰의 출발점으로 삼습니다. 납활자는 글자 바로 옆에 살이 붙어 있어, 찍히는 글자 사이가 일정했습니다. 이 화면에서는 그 일정함이 문장 안의 자간과 행간에 어떤 리듬을 만드는지 살펴봅니다.</p>
<figure class="case-photo-b">
<img src="./assets/metaltype.webp" alt="빛을 받은 금속 활자와 정렬된 활자 케이스" width="1100" height="825" loading="lazy" />
<figcaption>생성 이미지 · 활자 폭과 여백을 보는 편집 장면</figcaption>
</figure>
</div>
</div>
</section>
<!--
구입 — 라이선스는 조판 단위로. 조용한 두 줄.
-->
<section id="order" class="section" aria-labelledby="order-h">
<section id="order" class="section band" aria-labelledby="order-h">
<div class="wrap order-grid">
<div>
<h2 id="order-h">구입</h2>
<p class="muted">
웹·출판·임베드 세 가지 라이선스만 있습니다. 사용 수를 세지 않습니다 —
조판되는 단위(도서 권수, 사이트 도메인, 기기)로 셉니다.
</p>
<p class="eyebrow">가상 서체 기획</p>
<h2 id="order-h">라이선스 안내</h2>
<p class="muted">아래 라이선스 종류와 가격은 서체 판매 정보를 구성하는 방식을 보여 주는 예시입니다. 실제 주문·결제·문의로 이어지지 않습니다.</p>
</div>
<div class="order-rows">
<div class="order-row">
<div>
<h3>웹</h3>
<p>도메인당 · 웨이트 전종 · 자체 호스팅 WOFF2</p>
</div>
<p class="order-price">140,000원</p>
</div>
<div class="order-row">
<div>
<h3>출판</h3>
<p>도서 권수당 · 인쇄+전자 동시</p>
</div>
<p class="order-price">220,000원</p>
</div>
<div class="order-row"><div><h3>웹</h3><p>도메인당 · 웨이트 전종 · 자체 호스팅 WOFF2</p></div><p class="order-price">예시 140,000원</p></div>
<div class="order-row"><div><h3>출판</h3><p>도서 권수당 · 인쇄+전자 동시</p></div><p class="order-price">예시 220,000원</p></div>
</div>
<a class="btn" href="mailto:foundry@hanseong.kr">견적 문의</a>
<a class="btn" href="#bench">조판해 보기</a>
</div>
</section>
</main>
<footer class="site-foot">
<div class="wrap foot-inner">
<p>한성활자연구소 · 서울 마포</p>
<p class="muted">foundry@hanseong.kr</p>
</div>
</footer>
<footer class="site-foot"><div class="wrap foot-inner"><p>한성활자연구소 · 가상 서체 기획</p><p class="muted">화면의 글꼴은 마루 부리와 Pretendard로 구현했습니다.</p></div></footer>
<script>
/* 시험대 — 슬라이더 세 개가 본문 스타일을 직접 쓴다. 프레임당 레이아웃 없음. */
const specimenStage = document.querySelector(".specimen-stage");
const choices = document.querySelectorAll(".type-choice");
const typeStatus = document.getElementById("type-status");
const typeMessages = { body: "본문 표본 · Pretendard · 또렷한 문장 폭", title: "제목 표본 · 마루 부리 · 여백이 보이는 획의 대비" };
choices.forEach((choice) => choice.addEventListener("click", () => {
const kind = choice.dataset.kind;
specimenStage.dataset.kind = kind;
choices.forEach((button) => button.setAttribute("aria-pressed", String(button === choice)));
typeStatus.textContent = typeMessages[kind];
}));
const sample = document.getElementById("bench-sample");
const ctl = {
size: document.getElementById("ctl-size"),
tracking: document.getElementById("ctl-tracking"),
leading: document.getElementById("ctl-leading"),
};
const out = {
size: document.getElementById("out-size"),
tracking: document.getElementById("out-tracking"),
leading: document.getElementById("out-leading"),
};
const ctl = { size: document.getElementById("ctl-size"), tracking: document.getElementById("ctl-tracking"), leading: document.getElementById("ctl-leading") };
const out = { size: document.getElementById("out-size"), tracking: document.getElementById("out-tracking"), leading: document.getElementById("out-leading") };
const defaults = { size: 17, tracking: 0, leading: 1.8 };
function apply() {
const px = ctl.size.value + "px";
const em = ctl.tracking.value / 100 + "em";
const lh = ctl.leading.value;
sample.style.fontSize = px;
sample.style.letterSpacing = em;
sample.style.lineHeight = lh;
out.size.textContent = ctl.size.value;
out.tracking.textContent = ctl.tracking.value;
sample.style.fontSize = ctl.size.value + "px";
sample.style.letterSpacing = ctl.tracking.value / 100 + "em";
sample.style.lineHeight = ctl.leading.value;
out.size.textContent = ctl.size.value + "px";
out.tracking.textContent = ctl.tracking.value / 100 + "em";
out.leading.textContent = Number(ctl.leading.value).toFixed(1);
document.getElementById("bench-caption").textContent = `마루 부리 · ${ctl.size.value}px · 자간 ${ctl.tracking.value / 100}em · 행간 ${Number(ctl.leading.value).toFixed(1)}`;
}
for (const input of Object.values(ctl)) {
input.addEventListener("input", apply);
}
document.getElementById("ctl-reset").addEventListener("click", () => {
ctl.size.value = defaults.size;
ctl.tracking.value = defaults.tracking;
ctl.leading.value = defaults.leading;
apply();
});
Object.values(ctl).forEach((input) => input.addEventListener("input", apply));
document.getElementById("ctl-reset").addEventListener("click", () => { ctl.size.value = defaults.size; ctl.tracking.value = defaults.tracking; ctl.leading.value = defaults.leading; apply(); });
apply();
const io = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.classList.add("is-in");
io.unobserve(e.target);
}
},
{ rootMargin: "0px 0px -8% 0px", threshold: 0.05 },
);
for (const el of document.querySelectorAll(".reveal")) io.observe(el);
</script>
</body>
</html>

View file

@ -1,413 +1,109 @@
@import url("./tokens.css");
@import url("./tokens.css?v=3");
/* ==========================================================================
한성활자연구소 — editorial-archival. 글자가 상품.
========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body { background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); font-synthesis-weight: none; -webkit-font-smoothing: antialiased; }
:where(h1, h2, h3) { font-family: var(--font-display); font-weight: 600; line-height: var(--leading-tight); text-wrap: balance; word-break: keep-all; overflow-wrap: break-word; }
h2 { font-size: var(--step-3); }
h3 { font-family: var(--font-body); font-size: var(--step-1); font-weight: 600; }
:where(p, li, dd, dt, th, td, figcaption) { overflow-wrap: break-word; word-break: keep-all; text-wrap: pretty; }
a { color: inherit; }
.muted { color: var(--ink-muted); }
.eyebrow, .stage-label, .bench-caption { color: var(--ink-muted); font-size: var(--step--1); letter-spacing: 0.04em; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.skip { position: absolute; z-index: 100; top: 0; left: -9999px; padding: var(--space-3) var(--space-4); background: var(--ink); color: var(--surface); }
.skip:focus { left: 0; }
.wrap { width: min(100% - 2 * var(--pad-inline), var(--container)); margin-inline: auto; }
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
.site-head { position: sticky; z-index: 10; top: 0; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(6px); }
.head-inner { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); min-block-size: 4.25rem; }
.wordmark { font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; text-decoration: none; }
.nav { display: flex; gap: clamp(var(--space-3), 2vw, var(--space-5)); font-size: var(--step--1); }
.nav a { padding-block: var(--space-2); color: var(--ink-muted); text-decoration: none; }
.nav a:hover { color: var(--accent); }
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
background: var(--surface);
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
.hero { padding-block: clamp(var(--space-6), 7vw, var(--space-8)) var(--space-7); }
.hero-rail { width: min(100% - 2 * var(--pad-wide), var(--container-wide)); margin-inline: auto; }
.hero-intro { display: grid; grid-template-columns: minmax(0, 7fr) minmax(17rem, 3fr); gap: var(--space-6) clamp(var(--space-5), 7vw, var(--space-8)); align-items: end; }
.hero h1 { margin-top: var(--space-3); font-size: clamp(2.5rem, 1.3rem + 4.2vw, 5rem); max-inline-size: 10ch; }
.hero-note { max-inline-size: 27rem; padding-bottom: var(--space-2); }
.text-link { display: inline-flex; gap: var(--space-2); margin-top: var(--space-4); color: var(--accent); font-weight: 600; text-decoration-thickness: 1px; text-underline-offset: 0.25em; }
.specimen-stage { margin-top: clamp(var(--space-6), 8vw, var(--space-8)); color: var(--accent); }
.stage-label { color: var(--ink-muted); }
.hero-specimen { min-inline-size: 0; margin-top: var(--space-4); font-family: var(--font-body); font-size: clamp(6rem, 17.5vw, 16.25rem); font-weight: 650; letter-spacing: -0.09em; line-height: 0.82; text-wrap: nowrap; }
.specimen-stage[data-kind="title"] .hero-specimen { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.11em; }
.baseline { block-size: 1px; margin-top: var(--space-3); background: var(--accent); }
.stage-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding-top: var(--space-3); }
.type-switch { display: flex; gap: var(--space-2); }
.type-choice, .btn-reset { min-block-size: 2.75rem; border: 1px solid var(--line); border-radius: var(--radius); background: transparent; color: var(--ink); cursor: pointer; font: inherit; padding-inline: 0.9rem; }
.type-choice[aria-pressed="true"] { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.type-status { color: var(--ink-muted); font-size: var(--step--1); text-align: end; }
body {
background: var(--surface);
color: var(--ink);
font-family: var(--font-body);
font-size: var(--step-0);
line-height: var(--leading-normal);
-webkit-font-smoothing: antialiased;
}
.section { padding-block: var(--space-8); }
.band { border-block: 1px solid var(--line); background: var(--surface-raised); }
.section-head { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--space-5); align-items: baseline; }
.comparison { margin-top: var(--space-7); border-top: 1px solid var(--line); }
.typeface { display: grid; grid-template-columns: minmax(10rem, 3fr) minmax(0, 7fr); gap: var(--space-5) var(--space-7); padding-block: var(--space-6); border-bottom: 1px solid var(--line); }
.typeface-head .muted { margin-top: var(--space-2); font-size: var(--step--1); }
.specimen { max-inline-size: 42rem; }
.specimen-body { font-family: var(--font-body); font-size: 1.15rem; line-height: 1.9; }
.specimen-display { font-family: var(--font-display); font-size: clamp(1.7rem, 1.2rem + 2.3vw, 3.25rem); line-height: 1.48; }
:where(h1, h2, h3) {
font-family: var(--font-display);
font-weight: 600;
line-height: var(--leading-tight);
word-break: keep-all;
overflow-wrap: break-word;
text-wrap: balance;
}
h2 {
font-size: var(--step-2);
}
h3 {
font-family: var(--font-body);
font-weight: 600;
font-size: var(--step-1);
}
:where(p, li, dd, dt, th, td, figcaption) {
word-break: keep-all;
overflow-wrap: break-word;
line-height: var(--leading-normal);
text-wrap: pretty;
}
.bench-heading { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: var(--space-6); align-items: end; }
.bench-lead { max-inline-size: 28rem; }
.bench { display: grid; grid-template-columns: minmax(13rem, 3fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--line); }
.bench-controls { display: grid; align-content: start; gap: var(--space-4); }
.control { display: grid; gap: var(--space-2); }
.control-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.control label, .control output { color: var(--ink-muted); font-size: var(--step--1); }
.control output, .order-price { font-variant-numeric: tabular-nums; }
input[type="range"] { inline-size: 100%; accent-color: var(--accent); }
.btn-reset { justify-self: start; margin-top: var(--space-2); }
.btn-reset:hover, .type-choice:hover { border-color: var(--accent); }
.bench-caption { margin-bottom: var(--space-3); }
.bench-sample { max-inline-size: 42rem; border: 1px solid var(--line); background: var(--surface); padding: clamp(var(--space-4), 3vw, var(--space-6)); font-family: var(--font-display); font-size: 1.0625rem; line-height: 1.8; }
.muted {
color: var(--ink-muted);
}
a {
color: inherit;
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
.skip {
position: absolute;
left: -9999px;
top: 0;
background: var(--ink);
color: var(--surface);
padding: var(--space-3) var(--space-4);
z-index: 100;
}
.skip:focus {
left: 0;
}
.wrap {
width: min(100% - 2 * var(--pad-inline), var(--container));
margin-inline: auto;
}
/* ── 헤더 ─────────────────────────────────────────────────────────────── */
.site-head {
position: sticky;
top: 0;
z-index: 50;
background: color-mix(in srgb, var(--surface) 90%, transparent);
backdrop-filter: blur(6px);
border-bottom: 1px solid var(--line);
}
.head-inner {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
padding-block: var(--space-3);
}
.wordmark {
font-family: var(--font-display);
font-size: var(--step-1);
font-weight: 600;
text-decoration: none;
}
.nav {
display: flex;
gap: clamp(var(--space-3), 2vw, var(--space-5));
font-size: var(--step--1);
}
.nav a {
text-decoration: none;
color: var(--ink-muted);
padding-block: 2px;
border-bottom: 1px solid transparent;
transition: color var(--dur-quick) var(--ease-soft),
border-color var(--dur-quick) var(--ease-soft);
}
.nav a:hover {
color: var(--ink);
border-color: var(--accent);
}
/* ── 스페시멘 히어로 ──────────────────────────────────────────────────── */
.hero {
padding-block: var(--space-7) var(--space-8);
}
.hero-kicker {
font-size: var(--step--1);
color: var(--ink-muted);
letter-spacing: 0.06em;
}
.hero-glyph {
font-size: clamp(11rem, 24vw, 22rem);
line-height: 0.95;
margin-top: var(--space-4);
position: relative;
}
.hero-side {
margin-top: var(--space-5);
display: grid;
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
gap: var(--space-5) var(--space-7);
align-items: start;
}
.hero-statement {
max-width: 32em;
}
.hero-facts {
display: grid;
gap: var(--space-3);
border-left: 1px solid var(--line);
padding-left: var(--space-5);
}
.hero-facts dt {
font-size: var(--step--1);
color: var(--ink-faint);
}
.hero-facts dd {
margin-top: 2px;
font-weight: 500;
}
/* ── 섹션 공통 ────────────────────────────────────────────────────────── */
.section {
padding-block: var(--space-8);
}
.band {
background: var(--surface-raised);
border-block: 1px solid var(--line);
}
/* ── 활자 두 종 ───────────────────────────────────────────────────────── */
.typeface + .typeface {
margin-top: var(--space-6);
}
.typeface-head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--space-4);
flex-wrap: wrap;
border-top: 1px solid var(--line);
padding-top: var(--space-3);
}
.typeface-head .muted {
font-size: var(--step--1);
}
.specimen {
margin-top: var(--space-4);
font-size: 1.1875rem;
line-height: 1.9;
max-width: 46em;
}
.specimen-display {
font-size: clamp(1.75rem, 1.2rem + 3vw, 3.25rem);
line-height: 1.45;
max-width: 22em;
font-weight: 600;
}
/* ── 조판 시험대 ──────────────────────────────────────────────────────── */
.bench-lead {
max-width: 36em;
margin-top: var(--space-3);
}
.bench {
margin-top: var(--space-5);
display: grid;
grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
gap: var(--space-5) var(--space-7);
align-items: start;
}
.bench-controls {
display: grid;
gap: var(--space-4);
}
.control-head {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.control label {
font-size: var(--step--1);
color: var(--ink-muted);
}
.control output {
font-variant-numeric: tabular-nums;
color: var(--ink);
}
input[type="range"] {
width: 100%;
accent-color: var(--accent);
}
.btn-reset {
justify-self: start;
background: none;
border: 1px solid var(--line);
border-radius: var(--radius);
color: var(--ink-muted);
font-size: var(--step--1);
padding: 0.4rem 0.9rem;
cursor: pointer;
transition: border-color var(--dur-quick) var(--ease-soft),
color var(--dur-quick) var(--ease-soft);
}
.btn-reset:hover {
border-color: var(--accent);
color: var(--ink);
}
.bench-sample {
border: 1px solid var(--line);
background: var(--surface);
border-radius: var(--radius);
padding: var(--space-5);
max-width: 46em;
}
/* ── 활자 케이스 ──────────────────────────────────────────────────────── */
.case-grid {
display: grid;
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
gap: var(--space-6) var(--space-7);
align-items: start;
}
.case-photo img,
.case-photo-b img {
display: block;
width: 100%;
height: auto;
border-radius: var(--radius);
}
.case-photo figcaption,
.case-photo-b figcaption {
font-size: var(--step--1);
color: var(--ink-faint);
margin-top: var(--space-2);
}
.case-text h2 {
margin-bottom: var(--space-4);
}
.case-photo-b {
margin-top: var(--space-5);
max-width: 34rem;
}
/* ── 구입 ─────────────────────────────────────────────────────────────── */
.order-grid {
display: grid;
grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto;
gap: var(--space-5) var(--space-7);
align-items: start;
}
.order-grid .muted {
margin-top: var(--space-3);
max-width: 26em;
}
.order-rows {
border-top: 1px solid var(--line);
}
.order-row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--space-4);
padding-block: var(--space-3);
border-bottom: 1px solid var(--line);
}
.order-row p {
font-size: var(--step--1);
color: var(--ink-muted);
margin-top: 2px;
}
.order-price {
font-family: var(--font-display);
font-size: var(--step-1);
white-space: nowrap;
}
.btn {
display: inline-block;
background: var(--accent);
color: var(--accent-ink);
font-weight: 500;
text-decoration: none;
padding: 0.75rem 1.5rem;
border-radius: var(--radius);
transition: background var(--dur-quick) var(--ease-soft);
}
.btn:hover {
background: var(--accent-hover);
}
/* ── 푸터 ─────────────────────────────────────────────────────────────── */
.site-foot {
border-top: 1px solid var(--line);
padding-block: var(--space-5);
font-size: var(--step--1);
}
.foot-inner {
display: flex;
justify-content: space-between;
gap: var(--space-3) var(--space-5);
flex-wrap: wrap;
}
/* ── 등장 모션 ────────────────────────────────────────────────────────── */
.reveal {
opacity: 0;
translate: 0 12px;
transition: opacity var(--dur-normal) var(--ease-soft),
translate var(--dur-normal) var(--ease-soft);
transition-delay: var(--delay, 0ms);
}
.reveal.is-in {
opacity: 1;
translate: 0 0;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
translate: 0 0;
transition: none;
}
}
/* ── 반응형 ───────────────────────────────────────────────────────────── */
.case-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); align-items: start; }
.case-photo, .case-text { min-inline-size: 0; }
.case-photo img, .case-photo-b img { display: block; max-inline-size: 100%; inline-size: 100%; block-size: auto; border-radius: var(--radius); }
.case-photo figcaption, .case-photo-b figcaption { margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); }
.case-text { max-inline-size: 42rem; }
.case-text h2 { margin-top: var(--space-3); margin-bottom: var(--space-4); }
.case-photo-b { max-inline-size: 34rem; margin-top: var(--space-5); }
.order-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto; gap: var(--space-5) var(--space-7); align-items: center; }
.order-grid > * { min-inline-size: 0; }
.order-grid h2 { margin-top: var(--space-3); }
.order-grid .muted { max-inline-size: 28rem; margin-top: var(--space-3); }
.order-rows { border-top: 1px solid var(--line); }
.order-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); }
.order-row p { margin-top: var(--space-1); color: var(--ink-muted); font-size: var(--step--1); }
.order-price { flex: 0 0 auto; color: var(--ink) !important; font-family: var(--font-display); font-size: var(--step-1) !important; white-space: nowrap; }
.btn { display: inline-flex; align-items: center; justify-content: center; min-block-size: 2.75rem; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); font-weight: 600; padding-inline: 1.25rem; text-decoration: none; }
.btn:hover { background: var(--accent-hover); }
.site-foot { border-top: 1px solid var(--line); padding-block: var(--space-5); font-size: var(--step--1); }
.foot-inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3) var(--space-5); }
@media (max-width: 900px) {
.hero-side,
.bench,
.case-grid,
.order-grid {
grid-template-columns: 1fr;
gap: var(--space-5);
}
.case-photo {
max-width: 24rem;
}
.hero,
.section {
padding-block: var(--space-6);
}
.hero-intro, .section-head, .bench-heading, .bench, .case-grid, .order-grid { grid-template-columns: 1fr; }
.hero-intro, .bench-heading { gap: var(--space-4); }
.hero-note { max-inline-size: 34rem; }
.typeface { grid-template-columns: 1fr; gap: var(--space-3); }
.bench { gap: var(--space-5); }
.case-photo { max-inline-size: 30rem; }
}
@media (max-width: 560px) {
.head-inner {
flex-direction: column;
align-items: flex-start;
gap: var(--space-1);
}
.nav {
width: 100%;
justify-content: space-between;
}
.order-row {
flex-direction: column;
align-items: flex-start;
gap: var(--space-1);
}
.head-inner { display: grid; gap: 0; min-block-size: 0; padding-block: var(--space-3); }
.nav { justify-content: space-between; gap: var(--space-2); max-inline-size: 100%; margin-top: var(--space-1); overflow-x: auto; }
.nav a { flex: 0 0 auto; padding-block: var(--space-1); }
.hero { padding-block: var(--space-6); }
.hero h1 { font-size: clamp(2.5rem, 11vw, 4rem); overflow-wrap: anywhere; word-break: normal; }
.hero-specimen { font-size: min(30vw, 9.5rem); letter-spacing: -0.12em; }
.stage-footer, .order-row { align-items: flex-start; flex-direction: column; }
.type-status { text-align: start; }
.section { padding-block: var(--space-7); }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after { transition-duration: 0.01ms !important; }
}

View file

@ -1,38 +1,22 @@
/* ==========================================================================
한성활자연구소 — 디자인 토큰
프리셋: editorial-archival
컨셉 한 문장: 글자가 상품이다 — 첫 화면부터 스페시멘, 나머지는 물성과 눈금.
레퍼런스: dogfood-foundry(파운드리 계열 구조 관찰 — 별도 파일). 방향:
스페시멘은 Lorem 이 아니라 실제 문장, 테스터는 즉시 반영.
하지 않은 것: Lorem ipsum, 서체 카드 3열, '구독하면 웨이트 전부' 마케팅,
책등 세로 내비. 이유: 활자는 보여주는 것이지 포장하는 게 아니다.
강조 벽돌빨강 #A63D2F — CTA에만. 배경 채움은 버튼 하나 뿐.
========================================================================== */
:root {
/* 타입 — 스페시멘 글리프는 스케일 밖(20vw). 본문 17px. */
--step--1: 0.8125rem; /* 13px */
--step-0: 1.0625rem; /* 17px — 본문 (조판을 파는 페이지) */
--step-1: 1.25rem; /* 20px */
--step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.75rem); /* 28px */
--step-3: clamp(2rem, 1.4rem + 3vw, 3.25rem); /* 52px */
--leading-tight: 1.3;
--leading-normal: 1.85;
--font-display: "MaruBuri", serif;
--font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
--surface: #f5f1e8; /* 따뜻한 중성 바탕 */
--step--1: 0.8125rem;
--step-0: 1.0625rem;
--step-1: 1.25rem;
--step-2: clamp(1.75rem, 1.3rem + 1.5vw, 2.5rem);
--step-3: clamp(2.5rem, 1.6rem + 4vw, 5rem);
--leading-tight: 1.08;
--leading-normal: 1.8;
--font-display: "MaruBuri", Georgia, serif;
--font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif;
--surface: #f5f1e8;
--surface-raised: #ede7d9;
--ink: #26221d;
--ink-muted: #6a635a;
--ink-faint: #948b7c;
--ink-muted: #625b52;
--ink-faint: #756c60;
--line: #d9d1bf;
--accent: #a63d2f; /* 강조 */
--accent: #a63d2f;
--accent-ink: #f5f1e8;
--accent-hover: #8a3225; /* 강조 호버 */
--accent-hover: #8a3225;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
@ -40,14 +24,12 @@
--space-5: 2.5rem;
--space-6: 4rem;
--space-7: 6rem;
--space-8: clamp(4.5rem, 3rem + 6vw, 8.5rem);
--space-8: clamp(5rem, 3rem + 7vw, 10rem);
--radius: 2px;
--container: 1120px;
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
--container: 76rem;
--container-wide: 106.25rem;
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem);
--pad-wide: clamp(1.25rem, 1vw + 1rem, 4rem);
--dur-quick: 160ms;
--dur-normal: 420ms;
--ease-soft: cubic-bezier(0.25, 1, 0.35, 1);
}

View file

@ -0,0 +1,43 @@
# 스튜디오 폼 — 디자인 결정 기록
## 0. 브리프와 범위
- 무엇을: 빛·비례·구조를 사진과 도면으로 함께 읽는 가상 공간건축 포트폴리오 예시.
- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 인덱스로 이동하려는 방문자.
- 제약: 기존 흑백 사진·도면과 Pretendard를 보존한다. 실제 수행 이력·연락처로 오인될 수 없게 가상 사례와 생성 이미지라는 사실을 가까이에 표기한다. 기본 언어는 ko-KR 한국어이며, 영문은 고유 표기 FORM만 사용한다.
경로: **연장**. 정적 HTML의 토큰·콘텐츠 구조를 재구성했으며 이 문서가 이후 작업의 국소 기준이 된다.
## 1. 조사와 관찰
| 출처 | 관찰 | 적용 범위 |
| --- | --- | --- |
| [David Chipperfield Architects — Projects](https://davidchipperfield.com/projects) | 프로젝트 사진·모형과 그 아래 정렬된 메타데이터가 탐색의 위계를 소유하고, Grid/List/Filter는 보조 탐색으로 물러난다. | 사진을 첫 화면의 주 장면으로 두고, 인덱스는 번호·이름·위치·연도를 정렬한다. |
| [Klim Type Foundry](https://klim.co.nz/) | 글자와 작업물이 표면을 주도하고 인터페이스는 뒤로 물러난다. | 큰 헤드라인, 얇은 선, 흑백 사진의 우선순위. |
| [Carbon 2x Grid](https://carbondesignsystem.com/elements/2x-grid/usage/) | 넓은 표면과 읽기 표면은 분리된 레일을 가져야 한다. | 최대 1800px stage와 1280px 정보 레일, 720px 본문 measure. |
표현을 복제하지 않았다. 이 페이지의 고유한 결합은 `제목 → 실제로 보이는 사진 → 도면의 이유`라는 건축 기록 순서다.
## 2. 수정 전 문제와 결정
- 1440px 첫 화면에서 소개 문단이 약 80px 열에 갇혀 단어마다 줄바꿈됐고, 사진과 인덱스 행동이 첫 화면에 없었다.
- `설립 연도 예시`, `팀 구성 예시`처럼 작업 판독에 기여하지 않는 가상 수치를 제거했다.
- 기존 인덱스의 여섯 항목은 모두 같은 도면으로 연결됐지만 그 관계가 설명되지 않았다. 이제 목록 직전에 같은 가상 도면을 다른 관점에서 참조한다는 사실을 고지한다.
- 기존 생성 이미지는 유지하고 각 사진 캡션에 `생성 이미지 · 실제 프로젝트 사진 아님`을 남긴다.
## 3. 토큰과 레이아웃 계약
- 표면 `#fdfdfb`, 전경 `#171b19`, 중립 선 `#d8dbd7`, 보조 글자 `#5b615d`.
- 본문 16px 이상, 캡션 12px 이상, 본문 줄간 1.75, 읽기 폭 720px.
- `h1: clamp(2.5rem, 5.5vw, 5.5rem)`; Pretendard 단일 패밀리.
- 1440px 정보 레일 최대 1280px, stage 최대 1800px; 2560px에서는 사진만 stage에 머물고 산문은 720px 내에서 읽힌다.
- 모바일 gutter는 20px, 320px에서 16px. 이미지의 원본 비율을 유지하며 `object-fit: cover`는 슬롯에서 정한 비율 안의 편집에만 사용한다.
성능 예산: 첫 화면 JS 0KB / 첫 인터랙션은 정적 링크 / 폰트 패밀리 1개. 이미지 생성·추가는 하지 않는다.
## 4. 검증 추적
- 320, 390, 768, 1440, 1920, 2560px에서 문서 및 요소 가로 오버플로, h1/본문 가시성, 레일 상한, 이미지 로드, 탭 순서를 점검한다.
- `prefers-reduced-motion`과 효과 제거 상태에서도 콘텐츠가 같은 순서로 읽히는지 점검한다.
- 200% 텍스트 확대에서 본문·CTA가 사라지지 않는지 점검한다.
- 실행 결과와 화면 캡처는 `outputs/design-expertise-20260912/form/`에 남긴다.

View file

@ -4,300 +4,91 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>스튜디오 폼 · 공간건축연구소</title>
<meta
name="description"
content="형태는 침묵하고 치수가 태도를 증명한다. 비례와 구조의 명료성으로 설계하는 건축연구소의 프로젝트 인덱스와 도면."
/>
<meta name="description" content="빛, 비례, 구조를 도면과 사진으로 읽는 가상 공간건축 포트폴리오의 기록 형식." />
<meta property="og:title" content="스튜디오 폼 · 공간건축연구소" />
<meta
property="og:description"
content="비례와 구조의 명료성으로 설계하는 건축연구소."
/>
<meta property="og:description" content="빛과 비례를 읽는 설계 아카이브." />
<meta property="og:type" content="website" />
<meta name="theme-color" content="#FDFDFB" />
<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="./styles/main.css?v=2" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='6' y='6' width='20' height='20' fill='none' stroke='%23111' stroke-width='2'/%3E%3Crect x='11' y='11' width='10' height='10' fill='%23111'/%3E%3C/svg%3E"
/>
<meta name="theme-color" content="#fdfdfb" />
<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="./styles/main.css?v=3" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='6' y='6' width='20' height='20' fill='none' stroke='%23171b19' stroke-width='2'/%3E%3Crect x='11' y='11' width='10' height='10' fill='%23171b19'/%3E%3C/svg%3E" />
</head>
<body>
<a class="skip" href="#main">본문으로 건너뛰기</a>
<!-- 4모서리 등록 표시(인쇄 판맞춤) — 이 페이지의 리스크. 넓은 화면에서만. -->
<div class="corner corner-tl" aria-hidden="true"></div>
<div class="corner corner-tr" aria-hidden="true"></div>
<div class="corner corner-bl" aria-hidden="true"></div>
<div class="corner corner-br" aria-hidden="true"></div>
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="#main">스튜디오 폼 <span class="wordmark-en">FORM</span></a>
<div class="rail head-inner">
<a class="wordmark" href="#main">스튜디오 폼 <span>FORM</span></a>
<nav class="nav" aria-label="섹션">
<a href="#index">인덱스</a>
<a href="#drawing">도면</a>
<a href="#photo">사진</a>
<a href="#contact">연락</a>
<a href="#index">인덱스</a><a href="#drawing">도면</a><a href="#photo">사진</a><a href="#contact">구성 안내</a>
</nav>
</div>
</header>
<main id="main">
<!--
서문 — Pawson의 교훈: 크기가 아니라 여백과 무게로 위계를 만든다.
큰 글자는 이 페이지에 하나만 있다.
-->
<section class="hero" aria-labelledby="hero-h">
<div class="wrap">
<p class="hero-meta reveal">
공간건축연구소 · 서울 · 2014년 설립
</p>
<h1 id="hero-h" class="reveal" style="--delay: 60ms">
형태는 침묵하고<br />치수가 태도를 증명한다
</h1>
<p class="hero-statement reveal" style="--delay: 120ms">
폼은 공간을 꾸미지 않습니다. 벽이 서는 이유, 창이 열리는 방향, 보가
건너뛰는 길이를 설계합니다. 아름다움은 그 결정들이 모였을 때의
부산물입니다 — 그래서 우리의 홍보는 도면과 치수로 합니다.
</p>
<dl class="hero-facts reveal" style="--delay: 180ms">
<div>
<dt>설립</dt>
<dd>2014년</dd>
</div>
<div>
<dt>구성</dt>
<dd>건축사 4 · 연구원 3</dd>
</div>
<div>
<dt>영역</dt>
<dd>주거 · 미술관 · 재생</dd>
</div>
</dl>
<div class="stage hero-top">
<div class="hero-title">
<p class="eyebrow">가상 공간건축 포트폴리오 · 설계 기록 형식</p>
<h1 id="hero-h">공간을 짓는<br />명료한 질서.</h1>
</div>
<div class="hero-intro">
<p>빛이 머무는 방향과 구조가 건너는 거리를 함께 읽습니다. 사진은 결과를, 도면은 그 결과가 만들어진 이유를 남깁니다.</p>
<a class="index-link" href="#index">프로젝트 인덱스 보기 <span aria-hidden="true">↓</span></a>
</div>
</div>
<figure class="hero-image stage">
<img src="./assets/pavilion-bw.webp" alt="긴 수평창이 난 콘크리트 파빌리온에 벽 그림자가 지는 모습" width="1440" height="810" />
<figcaption><span>폼 하우스 · 가평 · 2025</span><span>생성 이미지 · 실제 프로젝트 사진 아님</span></figcaption>
</figure>
<div class="hero-caption rail"><p>이 페이지의 프로젝트명·연도·도면·이미지는 공간 설계 기록의 형식을 설명하기 위한 가상 사례입니다.</p></div>
</section>
<!--
인덱스 — 이 페이지의 주인공. 사진 없는 역연도 목록.
R1(johnpawson.com): 프로젝트는 제목/위치/연도의 세 줄 텍스트다.
-->
<section id="index" class="section" aria-labelledby="index-h">
<div class="wrap">
<h2 id="index-h" class="index-h reveal">프로젝트 인덱스</h2>
<section id="index" class="section index-section" aria-labelledby="index-h">
<div class="wide-rail">
<div class="section-head"><h2 id="index-h">프로젝트 인덱스</h2><p>여섯 항목은 한 장의 가상 설계 도면을 서로 다른 기록의 관점에서 참조합니다.</p></div>
<ol class="index">
<li class="index-row reveal">
<a href="#drawing">
<span class="idx-name">폼 하우스</span>
<span class="idx-loc">경기 가평</span>
<span class="idx-year">2025</span>
</a>
</li>
<li class="index-row reveal" style="--delay: 40ms">
<a href="#drawing">
<span class="idx-name">백령도 해녀기념관</span>
<span class="idx-loc">인천 백령도</span>
<span class="idx-year">2024</span>
</a>
</li>
<li class="index-row reveal" style="--delay: 80ms">
<a href="#drawing">
<span class="idx-name">은평 재생주거</span>
<span class="idx-loc">서울 은평</span>
<span class="idx-year">2024</span>
</a>
</li>
<li class="index-row reveal" style="--delay: 120ms">
<a href="#drawing">
<span class="idx-name">제주 돌담 스튜디오</span>
<span class="idx-loc">제주 애월</span>
<span class="idx-year">2023</span>
</a>
</li>
<li class="index-row reveal" style="--delay: 160ms">
<a href="#drawing">
<span class="idx-name">성수 인쇄공장 재생</span>
<span class="idx-loc">서울 성수</span>
<span class="idx-year">2022</span>
</a>
</li>
<li class="index-row reveal" style="--delay: 200ms">
<a href="#drawing">
<span class="idx-name">폼 오피스</span>
<span class="idx-loc">서울 종로</span>
<span class="idx-year">2019</span>
</a>
</li>
<li><a href="#drawing"><span class="idx-no">01</span><span class="idx-name">폼 하우스</span><span class="idx-loc">경기 가평</span><span class="idx-year">2025</span></a></li>
<li><a href="#drawing"><span class="idx-no">02</span><span class="idx-name">백령도 해녀기념관</span><span class="idx-loc">인천 백령도</span><span class="idx-year">2024</span></a></li>
<li><a href="#drawing"><span class="idx-no">03</span><span class="idx-name">은평 재생주거</span><span class="idx-loc">서울 은평</span><span class="idx-year">2024</span></a></li>
<li><a href="#drawing"><span class="idx-no">04</span><span class="idx-name">제주 돌담 스튜디오</span><span class="idx-loc">제주 애월</span><span class="idx-year">2023</span></a></li>
<li><a href="#drawing"><span class="idx-no">05</span><span class="idx-name">성수 인쇄공장 재생</span><span class="idx-loc">서울 성수</span><span class="idx-year">2022</span></a></li>
<li><a href="#drawing"><span class="idx-no">06</span><span class="idx-name">폼 오피스</span><span class="idx-loc">서울 종로</span><span class="idx-year">2019</span></a></li>
</ol>
</div>
</section>
<!--
도면 — 스티키 평면도가 섹션을 지나는 동안 그어진다.
R3: CSS scroll-driven + stroke-dashoffset. 폴백은 완성된 도면.
도면은 장식이 아니라 이 연구소의 언어 그 자체다.
-->
<section id="drawing" class="section drawing" aria-labelledby="drawing-h">
<div class="wrap drawing-grid">
<div class="drawing-stage reveal">
<svg
viewBox="0 0 520 640"
role="img"
aria-label="폼 하우스 1층 평면도. 남쪽으로 긴 거실과 북쪽 서비스 블록, 동쪽 마당으로 이루어져 있다."
>
<!-- 외곽 벽 (이중선) -->
<path
class="wall"
d="M 80 60 H 440 V 580 H 80 Z M 88 68 H 432 V 572 H 88 Z"
/>
<!-- 내부 벽 -->
<path
class="wall inner"
d="M 260 68 V 300 M 260 380 V 572 M 88 300 H 432 M 88 380 H 260"
/>
<!-- 문 개구부 표시 -->
<path class="door" d="M 260 340 h -36 M 260 340 a 36 36 0 0 1 36 -36" />
<!-- 기둥 -->
<path class="column" d="M 176 130 h 10 v 10 h -10 z M 356 130 h 10 v 10 h -10 z M 176 250 h 10 v 10 h -10 z M 356 250 h 10 v 10 h -10 z" />
<!-- 치수선 -->
<g class="dim">
<path d="M 80 610 H 440 M 80 602 v 16 M 440 602 v 16" />
<path d="M 50 60 V 580 M 42 60 h 16 M 42 580 h 16" />
<text x="260" y="632" text-anchor="middle">14,400</text>
<text x="30" y="325" text-anchor="middle" transform="rotate(-90 30 325)">9,600</text>
</g>
<!-- 방 이름 -->
<g class="room-label">
<text x="174" y="190">거실</text>
<text x="350" y="190" text-anchor="middle"></text>
<text x="174" y="460">주방·식당</text>
<text x="350" y="455">서비스</text>
<text x="350" y="490">현관</text>
</g>
<!-- 방위 -->
<g class="north">
<path d="M 470 90 v -40 M 470 50 l -8 14 M 470 50 l 8 14" />
<text x="470" y="110" text-anchor="middle">N</text>
</g>
</svg>
<p class="drawing-caption">폼 하우스 · 1층 평면도 · mm</p>
</div>
<div class="wide-rail drawing-grid">
<div class="drawing-notes">
<p class="eyebrow">도면으로 읽는 장면</p>
<h2 id="drawing-h">폼 하우스, 가평</h2>
<p>
대지 남쪽의 소나무군을 지우지 않기 위해 집은 길게 눕고, 마당은 집
안쪽으로 들어왔습니다. 구조는 9.6m 축 한 번으로 건너뛰어 거실에 기둥이
없습니다.
</p>
<p>
벽체는 단열 성능의 요구치에서 두께가 정해졌습니다 — 마감 재료를 먼저
고르고 두께를 맞추지 않았습니다. 창은 남향 긴면 하나, 동쪽 마당 면
하나. 그 두 개로 이 집의 하루가 결정됩니다.
</p>
<dl class="drawing-spec">
<div>
<dt>구조</dt>
<dd>RC 라멘 · 9.6m 스팬</dd>
</div>
<div>
<dt>면적</dt>
<dd>132.4 ㎡ (40.0평)</dd>
</div>
<div>
<dt>창개구</dt>
<dd>남측 14.4m · 동측 4.2m</dd>
</div>
<div>
<dt>줄공사</dt>
<dd>2025-04 – 2026-01</dd>
</div>
</dl>
<p>대지 남쪽의 소나무군을 지우지 않기 위해 집은 길게 눕고, 마당은 집 안쪽으로 들어왔습니다. 구조는 9.6m 축 한 번으로 건너뛰어 거실에 기둥이 없습니다.</p>
<p>벽체는 단열 성능의 요구치에서 두께가 정해졌습니다. 창은 남향 긴면 하나, 동쪽 마당 면 하나. 그 두 개로 이 집의 하루가 결정됩니다.</p>
<dl class="drawing-spec"><div><dt>구조</dt><dd>RC 라멘 · 9.6m 스팬</dd></div><div><dt>면적</dt><dd>132.4 ㎡ (40.0평)</dd></div><div><dt>창개구</dt><dd>남측 14.4m · 동측 4.2m</dd></div><div><dt>공사 기간</dt><dd>2025-04 – 2026-01</dd></div></dl>
</div>
<div class="drawing-stage">
<svg viewBox="0 0 520 640" role="img" aria-label="폼 하우스 1층 평면도. 남쪽으로 긴 거실과 북쪽 서비스 블록, 동쪽 마당으로 이루어져 있다.">
<path class="wall" d="M 80 60 H 440 V 580 H 80 Z M 88 68 H 432 V 572 H 88 Z" /><path class="wall inner" d="M 260 68 V 300 M 260 380 V 572 M 88 300 H 432 M 88 380 H 260" /><path class="door" d="M 260 340 h -36 M 260 340 a 36 36 0 0 1 36 -36" /><path class="column" d="M 176 130 h 10 v 10 h -10 z M 356 130 h 10 v 10 h -10 z M 176 250 h 10 v 10 h -10 z M 356 250 h 10 v 10 h -10 z" />
<g class="dim"><path d="M 80 610 H 440 M 80 602 v 16 M 440 602 v 16" /><path d="M 50 60 V 580 M 42 60 h 16 M 42 580 h 16" /><text x="260" y="632" text-anchor="middle">14,400</text><text x="30" y="325" text-anchor="middle" transform="rotate(-90 30 325)">9,600</text></g>
<g class="room-label"><text x="174" y="190">거실</text><text x="174" y="460">주방·식당</text><text x="350" y="455">서비스</text><text x="350" y="490">현관</text></g><g class="north"><path d="M 470 90 v -40 M 470 50 l -8 14 M 470 50 l 8 14" /><text x="470" y="110" text-anchor="middle">N</text></g>
</svg>
<p class="drawing-caption">폼 하우스 · 1층 평면도 · 가상 설계 도면 예시 · mm</p>
</div>
</div>
</section>
<!--
사진 — 흑백 3점. 그리드는 엄격하게, 크기는 사진이 요구하는 대로.
-->
<section id="photo" class="section photo" aria-labelledby="photo-h">
<div class="wrap">
<h2 id="photo-h" class="reveal">사진</h2>
<div class="wide-rail"><div class="section-head photo-head"><h2 id="photo-h">사진 시퀀스</h2><p>빛의 방향, 재료의 밀도, 비례의 검토를 순서대로 기록합니다.</p></div>
<div class="photo-grid">
<figure class="ph ph-a reveal">
<img
src="./assets/pavilion-bw.webp"
alt="긴 수평창이 낸 콘크리트 파빌리온에 벽 그림자가 지는 모습"
width="1440"
height="810"
loading="lazy"
/>
<figcaption>폼 하우스 · 남측 · 준공 후 첫 여름</figcaption>
</figure>
<figure class="ph ph-b reveal" style="--delay: 60ms">
<img
src="./assets/interior-bw.webp"
alt="천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실"
width="900"
height="1200"
loading="lazy"
/>
<figcaption>백령도 해녀기념관 · 제1전시실</figcaption>
</figure>
<figure class="ph ph-c reveal" style="--delay: 120ms">
<img
src="./assets/model-bw.webp"
alt="스터디 모델 — 직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습"
width="1100"
height="825"
loading="lazy"
/>
<figcaption>스터디 모델 07 · 석고</figcaption>
</figure>
<figure class="ph ph-wide"><img src="./assets/pavilion-bw.webp" alt="긴 수평창이 낸 콘크리트 파빌리온에 벽 그림자가 지는 모습" width="1440" height="810" loading="lazy" /><figcaption><span>폼 하우스 · 남측 · 준공 후 첫 여름</span><span>생성 이미지 · 실제 프로젝트 사진 아님</span></figcaption></figure>
<figure class="ph ph-narrow"><img src="./assets/interior-bw.webp" alt="천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실" width="900" height="1200" loading="lazy" /><figcaption><span>백령도 해녀기념관 · 제1전시실</span><span>생성 이미지 · 실제 프로젝트 사진 아님</span></figcaption></figure>
<figure class="ph ph-note"><p class="eyebrow">비례의 검토</p><p>빛과 재료의 표정은 사진으로, 공간의 관계는 도면으로 살펴봅니다.</p></figure>
<figure class="ph ph-model"><img src="./assets/model-bw.webp" alt="직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습" width="1100" height="825" loading="lazy" /><figcaption><span>스터디 모델 07 · 석고</span><span>생성 이미지 · 실제 프로젝트 사진 아님</span></figcaption></figure>
</div>
</div>
</section>
<!-- 연락 — 서류 같은 마무리 -->
<section id="contact" class="section contact" aria-labelledby="contact-h">
<div class="wrap contact-grid">
<h2 id="contact-h">연락</h2>
<address>
서울 종로구 자하문로 00길 00, 3층<br />
<a class="link" href="mailto:studio@form.kr">studio@form.kr</a>
</address>
<p class="contact-note">
신규 프로젝트는 분기에 두 건만 받습니다. 대지와 예산의 조건을 적어
보내주시면, 가능 여부를 2주 안에 답합니다.
</p>
</div>
</section>
<section id="contact" class="section contact" aria-labelledby="contact-h"><div class="rail contact-grid"><h2 id="contact-h">구성 안내</h2><address>가상 공간건축 포트폴리오<br />실제 연락처를 제공하지 않습니다.</address><p>이 페이지는 도면·사진·사양을 한 흐름으로 읽는 포트폴리오 형식의 예시입니다. 신규 프로젝트 상담이나 견적을 접수하지 않습니다.</p></div></section>
</main>
<footer class="site-foot">
<div class="wrap foot-inner">
<p>스튜디오 폼 · 공간건축연구소</p>
<p class="foot-meta">© 2014–2026</p>
</div>
</footer>
<script>
const io = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.classList.add("is-in");
io.unobserve(e.target);
}
},
{ rootMargin: "0px 0px -8% 0px", threshold: 0.05 },
);
for (const el of document.querySelectorAll(".reveal")) io.observe(el);
</script>
<footer class="site-foot"><div class="rail foot-inner"><p>스튜디오 폼 · 가상 공간건축 포트폴리오</p><p>설계 기록 형식 예시</p></div></footer>
</body>
</html>

View file

@ -1,497 +1,91 @@
@import url("./tokens.css");
@import url("./tokens.css?v=3");
/* ==========================================================================
스튜디오 폼 — swiss-minimal. 판맞춤 도면 묶음.
========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body { min-width: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); -webkit-font-smoothing: antialiased; }
img { display: block; max-width: 100%; height: auto; }
h1, h2 { font-weight: 600; line-height: var(--leading-tight); letter-spacing: -.025em; word-break: keep-all; overflow-wrap: break-word; text-wrap: balance; }
p, dd, dt, address, figcaption { word-break: keep-all; overflow-wrap: break-word; text-wrap: pretty; }
a { color: inherit; }
a:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }
.rail, .wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--rail)); margin-inline: auto; }
.wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--stage)); }
.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: var(--surface); padding: var(--space-3); }
.skip:focus { left: 0; }
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
.site-head { position: sticky; top: 0; z-index: 20; min-height: var(--header-h); background: color-mix(in srgb, var(--surface) 96%, transparent); border-bottom: 1px solid var(--line); backdrop-filter: blur(8px); }
.head-inner { min-height: var(--header-h); display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.wordmark { font-weight: 600; text-decoration: none; letter-spacing: -.02em; }
.wordmark span { margin-left: var(--space-1); color: var(--ink-faint); font-weight: 400; }
.nav { display: flex; gap: clamp(1rem, 2vw, 2.5rem); font-size: var(--step--1); }
.nav a { display: inline-flex; align-items: center; min-height: 2.75rem; text-decoration: none; color: var(--ink-muted); border-bottom: 1px solid transparent; transition: color var(--dur-quick) var(--ease), border-color var(--dur-quick) var(--ease); }
.nav a:hover { color: var(--ink); border-color: var(--ink); }
html {
-webkit-text-size-adjust: 100%;
scroll-behavior: smooth;
background: var(--surface);
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
.hero { padding: var(--space-6) 0 var(--space-5); border-bottom: 1px solid var(--line); }
.hero-top { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(18rem, .7fr); gap: var(--space-6); align-items: end; margin-bottom: var(--space-5); }
.eyebrow { font-size: var(--step--1); line-height: 1.5; color: var(--ink-muted); letter-spacing: .08em; }
.hero-title .eyebrow { margin-bottom: var(--space-3); }
h1 { font-size: var(--step-display); max-width: 10em; }
.hero-intro { max-width: 28rem; padding-bottom: var(--space-1); }
.hero-intro p { color: var(--ink-muted); }
.index-link { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 2.75rem; margin-top: var(--space-3); font-weight: 500; text-underline-offset: .35em; text-decoration-thickness: 1px; }
.index-link span { font-size: 1.25em; }
.hero-image img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; object-position: center 58%; filter: grayscale(1); }
figcaption { display: flex; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); line-height: 1.55; }
.hero-caption { margin-top: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); }
.hero-caption p { max-width: var(--measure); }
body {
background: var(--surface);
color: var(--ink);
font-family: var(--font-body);
font-size: var(--step-0);
line-height: var(--leading-normal);
-webkit-font-smoothing: antialiased;
}
.section { padding-block: var(--space-7); }
.section-head { display: grid; grid-template-columns: minmax(14rem, .7fr) minmax(0, 1fr); gap: var(--space-5); align-items: baseline; margin-bottom: var(--space-5); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); }
.section-head h2 { font-size: var(--step-1); letter-spacing: .03em; }
.section-head p { max-width: 40rem; color: var(--ink-muted); font-size: var(--step--1); }
.index { list-style: none; padding: 0; border-top: 1px solid var(--line-strong); }
.index li { border-bottom: 1px solid var(--line); }
.index a { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) minmax(8rem, .35fr) 4rem; gap: var(--space-3); align-items: baseline; padding: 1.15rem var(--space-2); text-decoration: none; transition: background var(--dur-quick) var(--ease), color var(--dur-quick) var(--ease); }
.index a:hover { background: var(--ink); color: var(--surface); }
.idx-no, .idx-loc, .idx-year { font-size: var(--step--1); color: var(--ink-faint); }
.idx-name { font-size: var(--step-1); font-weight: 500; }
.idx-year { font-variant-numeric: tabular-nums; text-align: right; }
.index a:hover .idx-no, .index a:hover .idx-loc, .index a:hover .idx-year { color: inherit; }
:where(h1, h2, h3) {
font-weight: 600;
line-height: var(--leading-tight);
letter-spacing: -0.01em;
word-break: keep-all;
overflow-wrap: break-word;
text-wrap: balance;
}
h1 {
font-size: var(--step-3);
}
h2 {
font-size: var(--step-2);
}
:where(p, li, dd, dt, th, td, address, figcaption) {
word-break: keep-all;
overflow-wrap: break-word;
line-height: var(--leading-normal);
text-wrap: pretty;
}
.drawing { border-top: 1px solid var(--line); background: var(--surface-raised); }
.drawing-grid { display: grid; grid-template-columns: minmax(17rem, .82fr) minmax(20rem, 1fr); gap: clamp(2.5rem, 6vw, 8rem); align-items: start; }
.drawing-notes { max-width: var(--measure); }
.drawing-notes .eyebrow { margin-bottom: var(--space-3); }
.drawing-notes h2 { font-size: var(--step-2); margin-bottom: var(--space-4); }
.drawing-notes p + p { margin-top: var(--space-3); }
.drawing-spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--line); }
.drawing-spec dt { font-size: var(--step--1); color: var(--ink-faint); }
.drawing-spec dd { margin-top: var(--space-1); font-size: var(--step--1); font-weight: 500; font-variant-numeric: tabular-nums; }
.drawing-stage { position: sticky; top: calc(var(--header-h) + var(--space-3)); }
.drawing-stage svg { display: block; width: 100%; height: auto; border: 1px solid var(--line); background: var(--surface); }
.wall { fill: none; stroke: var(--ink); stroke-width: 2; }
.wall.inner { stroke-width: 1.25; }
.door, .dim path, .north path { fill: none; stroke: var(--ink-muted); stroke-width: 1; }
.column { fill: var(--ink); }
.dim text, .north text { fill: var(--ink-faint); font-size: 12px; }
.room-label text { fill: var(--ink-muted); font-size: 13px; }
.drawing-caption { display: block; }
a {
color: inherit;
}
.link {
text-decoration: none;
border-bottom: 1px solid var(--line-strong);
transition: border-color var(--dur-quick) var(--ease-soft);
}
.link:hover {
border-color: var(--ink);
}
.photo { border-top: 1px solid var(--line); }
.photo-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(15rem, .55fr); gap: var(--space-5); }
.ph-wide { grid-column: 1 / -1; }
.ph-wide img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; filter: grayscale(1); }
.ph-narrow { align-self: start; }
.ph-narrow img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; filter: grayscale(1); }
.ph-model { align-self: end; }
.ph-model img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; filter: grayscale(1); }
.ph-note { align-self: end; border-top: 1px solid var(--line-strong); padding-top: var(--space-3); max-width: 30rem; }
.ph-note p:last-child { margin-top: var(--space-3); font-size: var(--step-1); line-height: 1.55; }
:focus-visible {
outline: 2px solid var(--ink);
outline-offset: 3px;
}
.contact { border-top: 1px solid var(--line); }
.contact-grid { display: grid; grid-template-columns: .7fr 1fr 1.2fr; gap: var(--space-5); align-items: start; }
.contact h2 { font-size: var(--step-1); letter-spacing: .03em; }
.contact address, .contact p { color: var(--ink-muted); font-style: normal; }
.site-foot { border-top: 1px solid var(--line-strong); padding-block: var(--space-4); font-size: var(--step--1); color: var(--ink-muted); }
.foot-inner { display: flex; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.skip {
position: absolute;
left: -9999px;
top: 0;
background: var(--ink);
color: var(--surface);
padding: var(--space-3) var(--space-4);
z-index: 100;
}
.skip:focus {
left: 0;
}
.wrap {
width: min(100% - 2 * var(--pad-inline), var(--container));
margin-inline: auto;
}
/* ── 판맞춤 표시 — 넓은 화면에서만, 내용과 겹치지 않는 곳에 ──────────── */
.corner {
position: fixed;
width: 18px;
height: 18px;
z-index: 40;
pointer-events: none;
}
.corner-tl {
top: 10px;
left: 10px;
border-top: 1px solid var(--line-strong);
border-left: 1px solid var(--line-strong);
}
.corner-tr {
top: 10px;
right: 10px;
border-top: 1px solid var(--line-strong);
border-right: 1px solid var(--line-strong);
}
.corner-bl {
bottom: 10px;
left: 10px;
border-bottom: 1px solid var(--line-strong);
border-left: 1px solid var(--line-strong);
}
.corner-br {
bottom: 10px;
right: 10px;
border-bottom: 1px solid var(--line-strong);
border-right: 1px solid var(--line-strong);
}
@media (max-width: 900px) {
.corner {
display: none;
}
}
/* ── 헤더 ─────────────────────────────────────────────────────────────── */
.site-head {
position: sticky;
top: 0;
z-index: 50;
background: color-mix(in srgb, var(--surface) 90%, transparent);
backdrop-filter: blur(6px);
border-bottom: 1px solid var(--line);
}
.head-inner {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
padding-block: var(--space-3);
}
.wordmark {
font-size: var(--step-1);
font-weight: 600;
text-decoration: none;
}
.wordmark-en {
color: var(--ink-faint);
font-weight: 400;
margin-left: var(--space-1);
}
.nav {
display: flex;
gap: clamp(var(--space-3), 2vw, var(--space-5));
font-size: var(--step--1);
}
.nav a {
text-decoration: none;
color: var(--ink-muted);
border-bottom: 1px solid transparent;
padding-block: 2px;
transition: color var(--dur-quick) var(--ease-soft),
border-color var(--dur-quick) var(--ease-soft);
}
.nav a:hover {
color: var(--ink);
border-color: var(--ink);
}
/* ── 서문 ─────────────────────────────────────────────────────────────── */
.hero {
padding-block: var(--space-8) var(--space-7);
border-bottom: 1px solid var(--line);
}
.hero-meta {
font-size: var(--step--1);
color: var(--ink-muted);
margin-bottom: var(--space-5);
}
.hero-statement {
margin-top: var(--space-5);
max-width: 30em;
}
.hero-facts {
margin-top: var(--space-6);
display: flex;
flex-wrap: wrap;
gap: var(--space-4) var(--space-7);
border-top: 1px solid var(--line);
padding-top: var(--space-4);
}
.hero-facts dt {
font-size: var(--step--1);
color: var(--ink-faint);
}
.hero-facts dd {
font-weight: 500;
}
/* ── 인덱스 ───────────────────────────────────────────────────────────── */
.section {
padding-block: var(--space-8);
}
.index-h {
font-size: var(--step--1);
font-weight: 500;
color: var(--ink-muted);
letter-spacing: 0.2em;
}
.index {
list-style: none;
margin-top: var(--space-3);
padding: 0;
border-top: 1px solid var(--line-strong);
}
.index-row a {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: var(--space-4) var(--space-6);
align-items: baseline;
padding-block: var(--space-3);
border-bottom: 1px solid var(--line);
text-decoration: none;
transition: background var(--dur-quick) var(--ease-soft);
}
.index-row a:hover {
background: var(--ink);
color: var(--surface);
}
.idx-name {
font-size: var(--step-1);
font-weight: 500;
}
.idx-loc {
color: var(--ink-muted);
font-size: var(--step--1);
}
.index-row a:hover .idx-loc {
color: inherit;
}
.idx-year {
font-variant-numeric: tabular-nums;
color: var(--ink-faint);
}
.index-row a:hover .idx-year {
color: inherit;
}
/* ── 도면(스티키) ─────────────────────────────────────────────────────── */
.drawing {
background: var(--surface);
}
.drawing-grid {
display: grid;
grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
gap: var(--space-6) var(--space-7);
}
.drawing-stage {
position: sticky;
top: calc(var(--space-5) + 56px);
align-self: start;
}
.drawing-stage svg {
display: block;
width: 100%;
height: auto;
border: 1px solid var(--line);
background: var(--surface-bright);
}
.wall {
fill: none;
stroke: var(--ink);
stroke-width: 2;
}
.wall.inner {
stroke-width: 1.25;
}
.door {
fill: none;
stroke: var(--ink-muted);
stroke-width: 1;
stroke-dasharray: none;
}
.column {
fill: var(--ink);
}
.dim path {
fill: none;
stroke: var(--line-strong);
stroke-width: 1;
}
.dim text {
font-size: 12px;
fill: var(--ink-faint);
font-variant-numeric: tabular-nums;
}
.room-label text {
font-size: 13px;
fill: var(--ink-muted);
}
.north path {
fill: none;
stroke: var(--ink);
stroke-width: 1.25;
}
.north text {
font-size: 12px;
fill: var(--ink-muted);
}
.drawing-caption {
margin-top: var(--space-2);
font-size: var(--step--1);
color: var(--ink-faint);
}
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.wall,
.wall.inner {
stroke-dasharray: 3400;
stroke-dashoffset: 3400;
animation: draw 1.4s linear both;
animation-timeline: --drawing;
animation-range: entry 10% contain 55%;
}
@keyframes draw {
to {
stroke-dashoffset: 0;
}
}
}
}
.drawing-notes h2 {
font-size: var(--step-2);
}
.drawing-notes p + p {
margin-top: var(--space-4);
}
.drawing-spec {
margin-top: var(--space-5);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: var(--space-3) var(--space-5);
border-top: 1px solid var(--line);
padding-top: var(--space-4);
}
.drawing-spec dt {
font-size: var(--step--1);
color: var(--ink-faint);
}
.drawing-spec dd {
margin-top: 2px;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
/* ── 사진 ─────────────────────────────────────────────────────────────── */
.photo {
border-top: 1px solid var(--line);
}
.photo h2 {
font-size: var(--step--1);
font-weight: 500;
color: var(--ink-muted);
letter-spacing: 0.2em;
}
.photo-grid {
margin-top: var(--space-4);
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-4);
}
.ph {
display: block;
}
.ph img {
display: block;
width: 100%;
height: auto;
filter: grayscale(1); /* 사진 파일이 흑백이 아니어도 페이지는 흑백이다 */
}
.ph-a {
grid-column: 1 / -1;
}
.ph figcaption {
margin-top: var(--space-2);
font-size: var(--step--1);
color: var(--ink-faint);
}
/* ── 연락 ─────────────────────────────────────────────────────────────── */
.contact {
border-top: 1px solid var(--line);
}
.contact-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
gap: var(--space-5) var(--space-7);
align-items: start;
}
.contact address {
font-style: normal;
color: var(--ink-muted);
}
.contact-note {
color: var(--ink-muted);
max-width: 26em;
}
/* ── 푸터 ─────────────────────────────────────────────────────────────── */
.site-foot {
border-top: 1px solid var(--line-strong);
padding-block: var(--space-4);
font-size: var(--step--1);
}
.foot-inner {
display: flex;
justify-content: space-between;
gap: var(--space-3) var(--space-5);
flex-wrap: wrap;
}
.foot-meta {
color: var(--ink-faint);
}
/* ── 등장 모션 ────────────────────────────────────────────────────────── */
.reveal {
opacity: 0;
translate: 0 10px;
transition: opacity var(--dur-normal) var(--ease-soft),
translate var(--dur-normal) var(--ease-soft);
transition-delay: var(--delay, 0ms);
}
.reveal.is-in {
opacity: 1;
translate: 0 0;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 1;
translate: 0 0;
transition: none;
}
}
/* ── 반응형 ───────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
.drawing-grid {
grid-template-columns: 1fr;
}
.drawing-stage {
position: static;
}
.contact-grid {
grid-template-columns: 1fr;
}
.hero,
.section {
padding-block: var(--space-6);
}
}
@media (max-width: 560px) {
.head-inner {
flex-direction: column;
align-items: flex-start;
gap: var(--space-1);
}
.nav {
width: 100%;
justify-content: space-between;
}
.index-row a {
grid-template-columns: 1fr auto;
}
.idx-loc {
grid-column: 1;
}
.idx-year {
grid-row: 1;
grid-column: 2;
}
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .nav a, .index a { transition: none; } }
@media (max-width: 48rem) { .hero-top, .drawing-grid { grid-template-columns: 1fr; } .hero-top { gap: var(--space-4); } .hero-intro { max-width: var(--measure); } .drawing-stage { position: static; grid-row: 1; } .drawing-notes { grid-row: 2; } .photo-grid { grid-template-columns: minmax(0, 1fr) minmax(11rem, .72fr); } .contact-grid { grid-template-columns: 1fr; gap: var(--space-3); } }
@media (max-width: 35rem) { .head-inner { padding-block: var(--space-2); align-items: flex-start; flex-direction: column; gap: 0; } .site-head { min-height: auto; } .nav { width: 100%; justify-content: space-between; gap: var(--space-2); } .nav a { min-height: 2.25rem; } .hero { padding-top: var(--space-5); } .hero-image img, .ph-wide img { aspect-ratio: 1.45 / 1; } .section-head { grid-template-columns: 1fr; gap: var(--space-2); } .index a { grid-template-columns: 2.25rem minmax(0, 1fr) auto; gap: var(--space-2); padding-inline: 0; } .idx-loc { grid-column: 2; } .idx-year { grid-column: 3; grid-row: 1; text-align: right; } .photo-grid { grid-template-columns: 1fr; } .ph-note { grid-row: 2; } .ph-narrow { grid-row: 3; max-width: 70%; } .ph-model { grid-row: 4; } .contact { padding-block: var(--space-6); } figcaption { flex-direction: column; gap: 0; } .drawing-spec { grid-template-columns: 1fr; } }

View file

@ -1,52 +1,34 @@
/* ==========================================================================
스튜디오 폼 — 디자인 토큰
프리셋: swiss-minimal
컨셉 한 문장: 판맞춤 도면 묶음 — 색이 아니라 선과 치수가 말한다.
레퍼런스: R1 johnpawson.com(실측: 724px 고정폭, 전 태그 동일 크기+색으로만
위계, 유일한 구분선 1px, 프로젝트는 제목/위치/연도 텍스트 행)
R2 cereal(웜 크림 3단 모노크롬 → 우리는 중성 회색으로 번안)
R3 CSS scroll-driven view()+dashoffset(이중 게이트, 폴백=완성 도면)
하지 않은 것: 색(무채색 100% — 강조색 없음, 링크만 밑줄), radius 전면 0,
사진 캐러셀, 서비스 아이콘. 이유: 도면의 권위는 선 하나에 있다.
========================================================================== */
:root {
/* 타입 — Pawson 교훈: 크기를 다양하게 쓰지 않는다. 위계는 무게와 색으로. */
--step--1: 0.8125rem; /* 13px — 캡션·치수 */
--step-0: 1rem; /* 16px — 본문 */
--step-1: 1.125rem; /* 18px — 소제목(본문과 1단계 차이만) */
--step-2: 1.375rem; /* 22px — 섹션 표제 */
--step-3: clamp(1.75rem, 1.3rem + 2.4vw, 2.75rem); /* 44px — 서문 하나만 */
--leading-tight: 1.35;
--leading-normal: 1.8;
--font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif;
/* 무채색 3단계 + 선 2단계. 강조색 없음. */
--font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif;
--surface: #fdfdfb;
--ink: #111111;
--ink-muted: #555555;
--ink-faint: #8a8a8a;
--line: #d9d9d5;
--line-strong: #999999; /* Pawson의 유일한 구분선 색 */
--surface-bright: #ffffff; /* index 행 hover 반전의 가장 밝은 단계 */ /* Pawson의 유일한 구분선 색 */
--surface-raised: #ffffff;
--ink: #171b19;
--ink-muted: #5b615d;
--ink-faint: #686e6a;
--line: #d8dbd7;
--line-strong: #a7aca7;
--step--1: 0.75rem;
--step-0: 1rem;
--step-1: 1.25rem;
--step-2: clamp(1.75rem, 1.3rem + 1.7vw, 2.5rem);
--step-display: clamp(2.5rem, 5.5vw, 5.5rem);
--leading-tight: 1.12;
--leading-normal: 1.75;
--measure: 45rem;
--rail: 78rem;
--stage: 112.5rem;
--pad: clamp(1.25rem, 1rem + 2vw, 3rem);
--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, 8.5rem);
--radius: 0; /* 이 페이지에는 모서리 둥금이 없다 */
--container: 1080px;
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
--space-6: clamp(3.5rem, 2.2rem + 4vw, 6rem);
--space-7: clamp(5rem, 3rem + 6vw, 9rem);
--header-h: 4rem;
--dur-quick: 140ms;
--dur-normal: 380ms;
--ease-soft: cubic-bezier(0.25, 1, 0.35, 1);
--ease: cubic-bezier(.25, 1, .35, 1);
}
@media (min-width: 120rem) { :root { --rail: 80rem; --stage: 112.5rem; } }
@media (max-width: 20rem) { :root { --pad: 1rem; } }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 481 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 481 KiB

Before After
Before After

View file

@ -36,6 +36,12 @@ const copiedLabel = lang === "ko" ? "복사했어요" : "Copied";
<path d="M3 5.5 8 10l5-4.5" />
</svg>
</a>
<a class="btn btn-text" href={showcasePath}>
{c.outputs.archiveCta}
<svg viewBox="0 0 16 16" width="15" height="15" aria-hidden="true">
<path d="M3 8h10M9 4l4 4-4 4" />
</svg>
</a>
</div>
</div>
@ -348,14 +354,22 @@ const copiedLabel = lang === "ko" ? "복사했어요" : "Copied";
.proof-facts > div + div { border-left: var(--line-width) solid var(--line); }
.proof-facts dt { display: grid; gap: var(--space-2); }
.proof-facts dt {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--space-2);
min-width: 0;
}
.proof-value {
color: var(--accent);
font-size: clamp(2.5rem, 2rem + 2vw, 4rem);
font-weight: var(--weight-medium);
line-height: 1;
overflow-wrap: anywhere;
}
.proof-facts dt > span { overflow-wrap: anywhere; }
.proof-facts dd {
margin-top: var(--space-4);
color: var(--ink-muted);

View file

@ -213,6 +213,7 @@ const copy = lang === "en"
.direction-stage {
position: relative;
display: flow-root;
aspect-ratio: 2 / 1;
overflow: clip;
background: var(--surface-soft);
@ -292,10 +293,11 @@ const copy = lang === "en"
.direction-axis-3 { right: 5%; bottom: 12%; text-align: right; }
.direction-statement {
position: absolute;
position: relative;
left: 48%;
top: 48%;
width: min(43%, 18rem);
margin-top: 24%;
margin-bottom: 5%;
color: var(--ink);
font-size: var(--step-1);
line-height: 1.38;
@ -637,6 +639,7 @@ const copy = lang === "en"
.specimen-tokens,
.specimen-proof { grid-column: 1; margin-top: 0; }
.direction-stage { aspect-ratio: 16 / 9; }
.direction-statement { margin-top: 27%; }
.type-stage { min-height: 24rem; }
.proof-stage { grid-template-columns: minmax(0, 1fr); }
.viewport-family { min-height: 18rem; border-right: 0; border-bottom: var(--line-width) solid var(--line); }

View file

@ -120,7 +120,7 @@ export const landingKo = {
hero: {
h1: "AI 코딩에\n디자인을 더합니다",
lead:
"레퍼런스를 조사해 방향을 정하고, 디자인 토큰부터 반응형 구현과 프리플라이트 검증까지 일관된 절차로 연결합니다.",
"웹 디자인 원리와 실제 사례를 조사해, 브랜드에 맞는 방향부터 반응형 구현과 실화면 검증까지 연결합니다.",
command: "npx designpaca",
primaryCta: "설치 명령 복사",
secondaryCta: "산출물 살펴보기",
@ -128,7 +128,7 @@ export const landingKo = {
outputs: {
h2: "정한 기준이 코드와 검사까지 이어집니다",
lead: "레퍼런스에서 고른 원칙은 결정 기록에 남고, 디자인 토큰과 구현 규칙이 되며, 마지막에는 반복 가능한 검사로 확인됩니다.",
archiveCta: "설계 예시 보기",
archiveCta: "만든 사이트 보기",
},
process: {
h2: "일곱 단계로 판단과 구현을 연결합니다",
@ -177,18 +177,18 @@ export const landingKo = {
facts: [
{
value: "7",
label: "설계 단계",
description: "브리프부터 결정 기록까지 0부터 6까지의 단계를 순서대로 수행합니다.",
label: "파이프라인",
description: "조사, 맥락 판단, 구현, 검증과 결정 기록을 0부터 6까지의 단계로 연결합니다.",
},
{
value: "15",
label: "하드 게이트",
description: "오버플로, 대비, 토큰, SEO와 죽은 코드를 완료 전에 확인합니다.",
value: "근거 · 맥락",
label: "기준을 구분하는 판단",
description: "보편적인 사용성 기준과 프로젝트에 맞는 스타일 결정을 분리해 적용합니다.",
},
{
value: "KO / EN",
label: "동등한 콘텐츠 구조",
description: "한국어와 영어가 같은 정보 구조와 주요 행동을 공유합니다.",
value: "실화면",
label: "두 종류의 검토",
description: "기능 검사는 자동화하고, 화면에서 읽히는 구성과 위계는 별도로 검토합니다.",
},
],
details: {
@ -198,9 +198,9 @@ export const landingKo = {
"제목, 본문과 버튼의 간격 및 정렬이 토큰 체계를 따르는지 확인합니다.",
"텍스트와 인터페이스 색의 대비를 측정합니다.",
"키보드만으로 내비게이션과 주요 행동을 완료할 수 있는지 확인합니다.",
"이미지가 목적에 맞게 contain 또는 cover로 표시되는지 확인합니다.",
"메타데이터, 언어, 대체 텍스트와 ARIA 라벨이 실제 화면과 일치하는지 확인합니다.",
"죽은 선택자, 정의되지 않은 토큰과 불필요한 스크립트를 찾습니다.",
"감소 모션과 200% 확대에서도 내용과 주요 행동이 유지되는지 확인합니다.",
"실제 전후 화면에서 구성, 위계와 반응형 동작을 함께 검토합니다.",
"출처, 메타데이터, 언어, 대체 텍스트와 ARIA 라벨을 화면과 연결하고 토큰·SEO·불필요한 코드를 점검합니다.",
],
},
},
@ -243,7 +243,7 @@ export const landingEn = {
hero: {
h1: "Design rigor\nfor AI coding",
lead:
"Research references, choose a direction, define tokens, build responsively, and verify the result through one repeatable process.",
"Research web design principles and real examples to connect a brand-fit direction with responsive implementation and rendered-screen verification.",
command: "npx designpaca",
primaryCta: "Copy install command",
secondaryCta: "See the deliverables",
@ -251,7 +251,7 @@ export const landingEn = {
outputs: {
h2: "Decisions carry through to code and checks",
lead: "Principles selected from research are recorded, translated into design tokens and implementation rules, then verified through repeatable checks.",
archiveCta: "Browse design examples",
archiveCta: "Explore example sites",
},
process: {
h2: "Connect judgment to implementation in seven stages",
@ -300,18 +300,18 @@ export const landingEn = {
facts: [
{
value: "7",
label: "Design stages",
description: "Move in order from stage 0, the brief, through stage 6, the decision record.",
label: "Pipeline stages",
description: "Connect research, contextual judgment, implementation, verification, and decision records from stage 0 through stage 6.",
},
{
value: "15",
label: "Hard gates",
description: "Check overflow, contrast, tokens, SEO, and dead code before declaring the work complete.",
value: "Evidence · context",
label: "Criteria kept distinct",
description: "Apply universal usability criteria separately from style decisions that fit the project.",
},
{
value: "KO / EN",
label: "Equivalent content structure",
description: "Korean and English share the same information architecture and primary actions.",
value: "Rendered screens",
label: "Two kinds of review",
description: "Automate functional checks, then separately review the composition and hierarchy users actually see.",
},
],
details: {
@ -321,9 +321,9 @@ export const landingEn = {
"Check that heading, body, and button spacing follows the token system.",
"Measure contrast for text and interface controls.",
"Complete navigation and primary actions with a keyboard.",
"Confirm each image uses contain or cover for its purpose.",
"Match metadata, language, alt text, and ARIA labels to the rendered page.",
"Flag dead selectors, undefined tokens, and unnecessary scripts.",
"Keep content and primary actions available with reduced motion and at 200% zoom.",
"Review real before-and-after screens for composition, hierarchy, and responsive behavior.",
"Connect sources, metadata, language, alt text, and ARIA labels to the rendered page, then check tokens, SEO, and unnecessary code.",
],
},
},