feat(site): rework 19 marketing showcases for composition diversity
Some checks are pending
ci / build (push) Waiting to run

Measured convergence before: split hero 13/19, top-bar nav 19/19,
one-line notice footer 14/19, body Pretendard 17/21, 10 similarity
pairs above 0.78. Each showcase got a pre-assigned hero family, nav
form, footer form, type pair, background, accent hue, radius, motion
grammar and material from outputs/rework-2026-10-06/plan.md; direction
cards and the chosen reason are recorded in every design.md.

After: 0 similarity warnings, 19 distinct display faces, 7 hero
families, 5 nav forms, 5 footer forms, no scroll reveals, 5 showcases
with SVG filter materials. Example contact values replace bracket
placeholders under the existing fictional-brand notices. Thumbnails,
OG images, card copy and three e2e assertions updated; dead CSS in
dure-enrollment and hannam-jari removed; similarity.mjs learns the new
font families.
This commit is contained in:
Yun Chan 2026-10-06 16:55:50 +09:00
parent 88c11682b1
commit 8e3cdb0d3b
296 changed files with 19131 additions and 17538 deletions

View file

@ -1,50 +1,87 @@
# 스튜디오 폼 — 디자인 결정 기록
# design.md — 스튜디오 폼
## 0. 브리프와 범위
## 진행 상태
- 무엇을: 빛·비례·구조를 사진과 도면으로 함께 읽는 가상 공간건축 포트폴리오 예시.
- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 인덱스로 이동하려는 방문자.
- 제약: 기존 흑백 사진·도면과 Pretendard를 보존한다. 실제 수행 이력·연락처로 오인될 수 없게 가상 사례와 생성 이미지라는 사실을 가까이에 표기한다. 기본 언어는 ko-KR 한국어이며, 영문은 고유 표기 FORM만 사용한다.
- 2026-10-06 전체(리디자인) 경로 0~6단계 완료. 재개할 일이 생기면 "검증"의 미검증 목록부터 본다.
경로: **연장**. 정적 HTML의 토큰·콘텐츠 구조를 재구성했으며 이 문서가 이후 작업의 국소 기준이 된다.
## 브리프
## 1. 조사와 관찰
- 무엇을: 주택·기념관·재생 건축 여섯 건을 사진과 평면도로 기록하는 가상 건축연구소 포트폴리오 한 장. 대표 기록은 폼 하우스(가평, 2025).
- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 색인으로 이동하려는 건축주·협력사. 목표 행동 없음(상담·견적을 받지 않는 가상 연구소).
- 제약: 정적 HTML·CSS, 기존 흑백 사진 3장과 평면도만 사용(새 이미지 없음), 기본 언어 ko-KR(상호 "스튜디오 폼"만 표기, 영문 FORM 미사용), 한자 금지, 가운뎃점 메타 문자열 금지, 폰트는 OFL·공개 배포본.
- 배정(outputs/rework-2026-10-06/plan.md): 표제 + 전폭 무대 · 측면 색인 · 큰 워드마크 한 줄 푸터 · Gothic A1 300 / Gothic A1 · 밝음 중립 #fcfcfb · 강조 없음 · radius 0 · scroll-driven 진행선 · 종이 결 feTurbulence · 극단 비율 파노라마 + 흑백.
- 화면: `index.html` 한 장(2차 페이지 없음).
| 출처 | 관찰 | 적용 범위 |
| --- | --- | --- |
| [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. |
## 미확정
표현을 복제하지 않았다. 이 페이지의 고유한 결합은 `제목 → 실제로 보이는 사진 → 도면의 이유`라는 건축 기록 순서다.
- 프로젝트 02~06의 평면도·사양·사진 — 없음. 색인 여섯 건 모두 같은 예시 평면도(`#drawing`)로 연결되며 레일 아래에 그 사실을 적었다.
- 연락처·주소 — 없음. 연구소 섹션과 `<address>`에 "연락처와 사무실 주소가 없습니다"로 표기.
- 방향 카드는 위임으로 골랐다(아래 "방향").
## 2. 수정 전 문제와 결정
## 레퍼런스
- 1440px 첫 화면에서 소개 문단이 약 80px 열에 갇혀 단어마다 줄바꿈됐고, 사진과 인덱스 행동이 첫 화면에 없었다.
- `설립 연도 예시`, `팀 구성 예시`처럼 작업 판독에 기여하지 않는 가상 수치를 제거했다.
- 기존 인덱스의 여섯 항목은 모두 같은 도면으로 연결됐지만 그 관계가 설명되지 않았다. 이제 목록 직전에 같은 가상 도면을 다른 관점에서 참조한다는 사실을 고지한다.
- 기존 생성 이미지는 유지하고 각 사진 캡션에 `생성 이미지 · 실제 프로젝트 사진 아님`을 남긴다.
| 슬롯 | 출처 | 가져온 원리 |
|---|---|---|
| R1 구조 | https://davidchipperfield.com/projects/neues-museum — 브라우저 실측 1440: h1 3줄(42px/700) 좌상단 → 바로 아래 전폭 무대 사진 1149×768 | 표제 블록 다음에 무대가 온다. 우리는 표제를 26px/300으로 낮추고 무대를 3.6:1로 늘렸다 |
| R1' 색인 | https://www.6a.co.uk/projects — "_Tate Liverpool_, Royal Albert Dock 2022 - 2027" | 색인 행 = 번호 / 이름 / 장소 연도 |
| R3 디테일 | https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline — sticky 우측 차례(폭 296, 링크 16/24, 행 32, 현재 항목 `aria-current` + 2px 막대 + 틴트) | 측면 색인의 현재 위치: `aria-current` + 왼쪽 2px 먹 막대. 틴트는 강조색 없음 규칙으로 뺐다 |
| R2 톤 | 비움 — 흑백 사진·먹 선이 톤을 이미 정했고, 배정이 서체·바탕·강조를 고정했다 | — |
| 미검증 | lacatonvassal.com(텍스트 페처 오판, 실측은 드롭다운 필터), sergisonbates.com 401, sanaa.co.jp 인증서 오류, pezo.cl 빈 응답 | 쓰지 않음 |
## 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`는 슬롯에서 정한 비율 안의 편집에만 사용한다.
지문: 표제+전폭 무대 · Gothic A1 300 / Gothic A1 400 · 밝음 중립 #fcfcfb · 강조 없음(먹만) · radius 0 · 흐름 [긴 문장 표제 → 3.6:1 흑백 파노라마 → 평면도(스크롤로 선이 그어짐)+사양 → 사진 2장(3:4, 2.4:1) → 연구소 → 큰 워드마크 푸터] · 측면 색인(≤64rem 상단 접는 차례) · 큰 워드마크 한 줄 푸터 · 극단 비율 파노라마 + 흑백, 캡션은 번호와 함께 · scroll-driven(진행선 1px, 도면 stroke-dashoffset), 등장 리빌 없음
성능 예산: 첫 화면 JS 0KB / 첫 인터랙션은 정적 링크 / 폰트 패밀리 1개. 이미지 생성·추가는 하지 않는다.
- 한 문장 컨셉: 건축주가 폼 하우스 한 건의 파노라마와 평면도를 사실 문장과 함께 읽고, 왼쪽 색인에서 다른 다섯 건으로 이동한다.
- 프리셋: swiss-minimal(평평한 종이, 재질 최소). 보조 축 없음. 종이 결은 재질 한 가지(종이)로 한정.
- 히어로 글 묶음: 라벨 없음 / 서너 줄 긴 문장 표제 / 리드 없음 / 행동 없음(다음 정보인 무대로 이어진다).
- 표현 리스크: 표제 26px vs 푸터 워드마크 176px(6.8배)의 크기 긴장. 1440·390 전체 캡처로 확인했다.
- 카드(위임, 구조가 다른 3장): A 도면집 한 장(좌측 색인 + 표제 3줄 → 파노라마) / B 긴 두루마리(사진 3장을 가로 스크롤 띠로) / C 평면도가 무대(전폭 평면도 먼저). **A 채택** — 설득 재료가 사진(재료·빛)과 평면도(배치·동선) 둘이고 R1이 사진을 표제 바로 아래 무대로 두며, 색인의 현재 위치 표시는 한 장을 읽는 동안에만 의미가 생긴다. B는 가로 스크롤이 세로 진행선과 충돌해 기각, C는 첫 화면에 사진이 없어 기각.
- 반사실: 첫 화면 고유 요소 3개 — 긴 수평창 파빌리온 파노라마(자산), 가평·백령도·성수 색인 6건, "사진과 평면도로 집을 왜 그렇게 앉혔는지" 방법 문장. 세무사무소로 치환하면 색인 레일·파노라마가 성립하지 않는다. 페이지 수렴: plan.md상 orbit-aerospace와 측면 색인·극단 비율·scroll-driven 도면 선 3축이 같다 — 글 묶음·푸터·바탕·서체로 갈라 둔 상태이고 섹션 흐름이 달라야 한다.
- 보존 자산: pavilion-bw·interior-bw·model-bw webp, 프로젝트 6건의 이름·위치·연도, 폼 하우스 사양 수치와 설계 이유 문장, `.index a` 6개 → `#drawing`.
## 4. 검증 추적
## 토큰과 예산
- 320, 390, 768, 1440, 1920, 2560px에서 문서 및 요소 가로 오버플로, h1/본문 가시성, 레일 상한, 이미지 로드, 탭 순서를 점검한다.
- `prefers-reduced-motion`과 효과 제거 상태에서도 콘텐츠가 같은 순서로 읽히는지 점검한다.
- 200% 텍스트 확대에서 본문·CTA가 사라지지 않는지 점검한다.
- 실행 결과와 화면 캡처는 `outputs/design-expertise-20260912/form/`에 남긴다.
값은 `styles/tokens.css`. 역할만 적는다.
## 2026-09-12 전체 쇼케이스 점검
- 서체 한 패밀리 두 웨이트: 300은 표제·h2·푸터 워드마크(가늘고 넓게), 400은 본문·색인. 500은 폰트 예산 때문에 뺐고 색인 이름은 색(ink)과 행 위치로 구분한다.
- 색: 바탕 #fcfcfb(배정 유지), 먹 #161716, 보조 #585b58(6.7:1), 흐림 #6b6e6b(5.0:1), 선 두 단계. 강조색 없음.
- 크기: 캡션 13px(한글 하한), 본문 16px, 표제·h2 clamp(20~26px) — "작은 표제", 워드마크 clamp(52~176px). 표제 measure 30em(1440에서 3줄), 본문 36em.
- 무대 비율: 3.6:1(≥64rem) / 2.4:1 / 16:10(≤40rem). 사진 띠 2.4:1 / 2:1 / 16:10.
- 예산(tokens §5 데모/포트폴리오): JS 인라인 1.2KB / 폰트 **254KB 실측 — 200KB를 올렸다**(Gothic A1 한글 슬라이스 두 웨이트; 본문까지 300으로 줄이면 가독성이 떨어진다) / LCP 348ms(1440)·252ms(390) 로컬 / CLS ≤0.001 / 첫 화면 전송 약 430KB.
- 기존 흑백 사진·도면 구도, 앵커와 긴 모바일 라벨의 줄바꿈 규칙을 보존했다. 로컬 MaruBuri 파일을 함께 제공하되 현재 Pretendard 단일 패밀리 본문 계약은 바꾸지 않았다.
- `verify-final` 18개 묶음은 16개가 즉시 통과했다. 전체 묶음의 invariant·visual 검사는 Gaon·Dure를 대상으로 하며, 나머지 2개는 수정 뒤 release 독립 재실행에서 ALL PASS·ALL MATCH였다.
- root는 28개 모바일, 21개 데스크톱, 7개 하위 경로와 30개 앱 상태를 직접 검토해 수용했다. stress 112건은 hard failure 0이며, 가로 넘침 4건은 targeted fix 뒤 390px 재측정에서 0이다.
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
## 이펙트와 폴백
- 도면 선 그리기: `.plan`에 `view-timeline: --plan`, 벽→내벽→창→문 순으로 `stroke-dashoffset 1 → 0`. `@supports (animation-timeline: view())` 안에만 초기 상태를 두어 미지원(Firefox)은 완성 상태. 감소 모션도 완성 상태.
- 진행선: 레일 오른쪽 1px 실선 위 먹 구간이 `scroll(root block)`로 자란다. 감소 모션은 가득 찬 상태(정보).
- 종이 결: feTurbulence 타일(220px, 2옥타브) SVG를 `body::before` 고정 레이어 배경으로, opacity 0.08. 런타임 필터 연산 없음. 끄면 바탕색만 남고 레이아웃은 그대로.
- 읽는 위치: IntersectionObserver가 "이 장" 링크에 `aria-current`를 준다. JS가 없으면 표시만 없고 색인은 항상 열린다.
- 좁은 화면 차례: `<details>`를 JS가 폭(64rem)에 따라 열고 닫는다. 링크를 누르면 닫힌다.
## 하지 않기로 한 것
- 상단 바·backdrop-filter·스크롤 리빌·eyebrow·리드·버튼 — 수렴 지문이라 뺐다.
- 영문 "FORM" 워드마크·영문 장식 라벨 — 기본 언어 규칙.
- 같은 사진을 두 번 싣기(옛 히어로·사진 섹션 중복) — R6 실패 신호.
- 프로젝트 02~06의 상세 — 사실이 없어 만들지 않았다(placeholder 대신 색인에서 같은 예시 평면도로 보낸다는 고지).
- 가운뎃점 메타 문자열, 캡션마다 붙던 "생성 이미지 · 실제 프로젝트 사진 아님" — 히어로 아래 고지 한 문단이 페이지 범위를 한 번 소유한다. 푸터는 브랜드 범위 한 줄만.
- MaruBuri 로컬 폰트 — 싣지 않는다(파일은 출처 기록과 함께 남겨 둠). `fonts.css`는 지웠다.
## 검증
- 검증 줄: html-validate 0 오류 · stylelint 0 · no-hanja PASS · showcase-audit PASS 6/0(320~2560) · design-gate 충족 13 / 미검증 0 / 실패 0(꺼짐 3: static·visual·webkit; 대비 AA 66요소, axe 0, 터치타깃·오버플로·죽은 선택자·색 리터럴 0) · archive-showcases-e2e 107/108(실패 1 = 390 접힌 차례 선택자, 제안 수정은 outputs 노트) · similarity 경고 없음.
- 시각 루브릭(약한 축부터): R3 1(레일 시작선 4개, 역할 설명 가능) · R6 2 · R1 2 · R2 2 · R4 2 · R5 2 · R7 2 · R8 2 · R9 2 · R10 2 = 19/20. 근거와 2점 반례는 `outputs/rework-2026-10-06/form-architecture.md`.
- 미검증: WebKit 렌더, 실기기 INP, Firefox 실제 실행(폴백 경로는 `@supports`로 보장).
- 캡처: 스크래치 `form/final-*.png`(1440·390 첫 화면·전체·회색조·흐림·감소 모션·도면 중간 상태·2x 크롭).
## 피드백 기록 · 보존 목록
- 보존: 흑백 사진 3장과 역할(남측 입면 파노라마 / 전시실 빛 / 석고 모형), 가로형 평면도, 색인 6건, 폼 하우스 사양.
- 피드백: 없음(2026-10-06 재작업 직후).
## 변경 기록
- 2026-10-06 · 전체 리디자인 · plan.md 배정(측면 색인·표제+전폭 무대·워드마크 푸터·Gothic A1·진행선·종이 결·파노라마)에 따라 구조·토큰·카피를 다시 썼다. 평면도를 가로형(14,400×9,600)으로 다시 그려 치수·비례 불일치와 "기둥 없음" 문장과의 모순을 없앴다. 가운뎃점 메타 문자열·eyebrow·리드·버튼·고지 띠 푸터 제거.
- 2026-10-02 · 카피 교정 · "빛이 머무는" 같은 무생물 주어·시구형 표제를 사실 문장으로.
- 2026-09-12 · 전체 쇼케이스 점검 · verify-final 묶음 통과, MaruBuri 파일 동봉(미사용).
- 2026-08 · 최초 작성 · Pretendard 단일 패밀리, 상단 바 + 두 줄 표제 + 2.35:1 사진 + 인덱스 + 도면 + 사진 시퀀스 + 상담 구성.

View file

@ -3,93 +3,162 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>스튜디오 폼 · 공간건축연구소</title>
<meta name="description" content="빛, 비례, 구조를 도면과 사진으로 읽는 가상 공간건축 포트폴리오의 기록 형식." />
<meta property="og:title" content="스튜디오 폼 · 공간건축연구소" />
<meta property="og:description" content="빛과 비례를 읽는 설계 아카이브." />
<title>스튜디오 폼 — 주택, 기념관, 재생 건축 설계</title>
<meta name="description" content="주택, 기념관, 오래된 건물 재생을 설계하는 가상 건축연구소 스튜디오 폼. 폼 하우스의 평면도와 사진, 프로젝트 여섯 건의 색인." />
<meta property="og:title" 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/fonts.css?v=20260912all" />
<link rel="stylesheet" href="./styles/main.css?v=20260912all" />
<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" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="og:image" content="./assets/pavilion-bw.webp" />
<meta name="theme-color" content="#fcfcfb" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Gothic+A1:wght@300;400&display=swap" />
<link rel="stylesheet" href="./styles/tokens.css?v=20261006" />
<link rel="stylesheet" href="./styles/main.css?v=20261006" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='5' y='9' width='22' height='14' fill='none' stroke='%23161716' stroke-width='2'/%3E%3Cpath d='M5 16h22' stroke='%23161716' stroke-width='1'/%3E%3C/svg%3E" />
</head>
<body>
<a class="skip" href="#main">본문으로 건너뛰기</a>
<header class="site-head">
<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>
</nav>
</div>
</header>
<main id="main">
<section class="hero" aria-labelledby="hero-h">
<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>
<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><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>
<div class="frame">
<header class="rail">
<a class="wordmark" href="#main">스튜디오 폼</a>
<details class="toc" id="toc" open>
<summary class="toc-summary"><span class="toc-open-label">차례</span><span class="toc-close-label">닫기</span></summary>
<nav class="toc-body" aria-label="프로젝트 색인">
<p class="toc-label" id="index-label">프로젝트</p>
<ol class="index" aria-labelledby="index-label">
<li><a href="#drawing" aria-current="true"><span class="idx-no">01</span><span class="idx-name">폼 하우스</span><span class="idx-meta"><span>가평</span><span>2025</span></span></a></li>
<li><a href="#drawing"><span class="idx-no">02</span><span class="idx-name">백령도 해녀기념관</span><span class="idx-meta"><span>백령도</span><span>2024</span></span></a></li>
<li><a href="#drawing"><span class="idx-no">03</span><span class="idx-name">은평 재생주거</span><span class="idx-meta"><span>서울 은평</span><span>2024</span></span></a></li>
<li><a href="#drawing"><span class="idx-no">04</span><span class="idx-name">제주 돌담 스튜디오</span><span class="idx-meta"><span>제주 애월</span><span>2023</span></span></a></li>
<li><a href="#drawing"><span class="idx-no">05</span><span class="idx-name">성수 인쇄공장 재생</span><span class="idx-meta"><span>서울 성수</span><span>2022</span></span></a></li>
<li><a href="#drawing"><span class="idx-no">06</span><span class="idx-name">폼 오피스</span><span class="idx-meta"><span>서울 종로</span><span>2019</span></span></a></li>
</ol>
<p class="toc-note">여섯 건 모두 같은 예시 평면도로 연결됩니다.</p>
<p class="toc-label" id="parts-label">이 장</p>
<ol class="parts" aria-labelledby="parts-label">
<li><a href="#drawing" data-part="drawing">도면</a></li>
<li><a href="#photo" data-part="photo">사진</a></li>
<li><a href="#studio" data-part="studio">연구소</a></li>
</ol>
</nav>
</details>
<div class="progress" aria-hidden="true"><span class="progress-fill"></span></div>
</header>
<section id="drawing" class="section drawing" aria-labelledby="drawing-h">
<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>
</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>
<main id="main" class="sheet">
<section class="hero" aria-labelledby="hero-h">
<h1 id="hero-h">스튜디오 폼은 주택, 기념관, 오래된 건물의 재생을 설계하는 건축연구소입니다. 프로젝트마다 완공 사진과 평면도를 함께 실어, 집을 왜 그렇게 앉혔는지&nbsp;설명합니다.</h1>
<figure class="stage">
<img src="./assets/pavilion-bw.webp" alt="긴 수평창이 난 콘크리트 파빌리온의 남측 벽에 그림자가 비스듬히 지는 모습" width="1440" height="810" fetchpriority="high" decoding="async" />
<figcaption class="stage-caption"><span class="cap-no">사진 01</span><span>폼 하우스 남측 진입부, 가평</span></figcaption>
</figure>
<p class="notice">이 페이지의 프로젝트명, 연도, 도면과 사진 세 장은 모두 예시입니다. 사진은 생성 이미지이고 실제 프로젝트 사진이 아닙니다.</p>
</section>
<section id="drawing" class="section drawing" aria-labelledby="drawing-h">
<h2 id="drawing-h">폼 하우스는 남쪽 소나무를 베지 않으려고 집을 동서로 길게&nbsp;앉혔습니다.</h2>
<figure class="plan">
<svg class="plan-svg" viewBox="0 0 900 640" role="img" aria-label="폼 하우스 1층 평면도. 동서 14.4미터, 남북 9.6미터. 서쪽은 기둥 없는 거실, 가운데는 동쪽 마당, 동쪽은 주방과 식당, 현관 블록이다.">
<defs><pattern id="court-hatch" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><path class="hatch" d="M 0 0 V 12" /></pattern></defs>
<rect class="court" x="486" y="78" width="108" height="444" fill="url(#court-hatch)" />
<g class="plan-draw">
<path class="wall" pathLength="1" d="M 60 60 H 780 V 540 H 60 Z" />
<path class="wall" pathLength="1" d="M 72 72 H 768 V 528 H 72 Z" />
<path class="wall inner" pathLength="1" d="M 480 72 V 528 M 600 72 V 528 M 600 300 H 768" />
<path class="window" pathLength="1" d="M 72 532 H 480 M 72 536 H 480 M 600 532 H 768 M 600 536 H 768" />
<path class="window" pathLength="1" d="M 474 132 V 342 M 478 132 V 342" />
<path class="door" pathLength="1" d="M 480 420 V 386 A 34 34 0 0 1 514 420" />
<path class="door" pathLength="1" d="M 600 440 V 474 A 34 34 0 0 1 566 440" />
</g>
<g class="dim"><path d="M 60 580 H 780 M 60 572 v 16 M 780 572 v 16" /><path d="M 30 60 V 540 M 22 60 h 16 M 22 540 h 16" /><text x="420" y="604" text-anchor="middle">14,400</text><text x="18" y="300" text-anchor="middle" transform="rotate(-90 18 300)">9,600</text></g>
<g class="room-label"><text x="270" y="300" text-anchor="middle">거실</text><text x="540" y="300" text-anchor="middle">마당</text><text x="684" y="190" text-anchor="middle">주방, 식당</text><text x="684" y="420" text-anchor="middle">현관</text></g>
<g class="north"><path d="M 840 110 v -50 M 840 60 l -8 14 M 840 60 l 8 14" /><text x="840" y="130" text-anchor="middle">N</text></g>
</svg>
<p class="drawing-caption">폼 하우스 · 1층 평면도 · 가상 설계 도면 예시 · mm</p>
<figcaption class="plan-caption"><span class="cap-no">도면 01</span><span>폼 하우스 1층 평면도, 단위 mm</span></figcaption>
</figure>
<div class="drawing-notes">
<div class="prose">
<p>대지 남쪽에 소나무가 있어 집을 동서로 길게 두고, 마당은 거실과 주방 사이 집 안쪽에 넣었습니다. 거실은 남북 9.6m를 보 하나로 건너 기둥이 없습니다.</p>
<p>창은 남쪽 긴 면 전체와 거실 동쪽 마당 쪽에 냈습니다. 아침 해는 마당 쪽 창으로, 낮 햇빛은 남쪽 창으로 들어옵니다. 벽 두께는 단열 기준에 맞춰 정했습니다.</p>
</div>
<dl class="spec">
<div><dt>구조</dt><dd>RC 라멘, 9.6m 스팬</dd></div>
<div><dt>면적</dt><dd>132.4 m² (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>
</section>
</section>
<section id="photo" class="section photo" aria-labelledby="photo-h">
<div class="wide-rail"><div class="section-head photo-head"><h2 id="photo-h">사진 시퀀스</h2><p>빛의 방향, 재료의 밀도, 비례의 검토를 순서대로 기록합니다.</p></div>
<section id="photo" class="section photo" aria-labelledby="photo-h">
<h2 id="photo-h">재료와 빛은 사진으로 봅니다. 백령도 해녀기념관 전시실과 석고 스터디 모형, 두 장을 실었습니다.</h2>
<div class="photo-grid">
<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>
<figure class="ph ph-tall">
<figcaption><span class="cap-no">사진 02</span><span>백령도 해녀기념관 제1전시실</span></figcaption>
<img src="./assets/interior-bw.webp" alt="천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실" width="900" height="1200" loading="lazy" decoding="async" />
</figure>
<figure class="ph ph-panel">
<figcaption><span class="cap-no">사진 03</span><span>스터디 모델 07, 석고</span></figcaption>
<img src="./assets/model-bw.webp" alt="직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습" width="1100" height="825" loading="lazy" decoding="async" />
</figure>
</div>
</div>
</section>
</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="rail foot-inner"><p>스튜디오 폼 · 가상 공간건축 포트폴리오</p><p>설계 기록 형식 예시</p></div></footer>
<section id="studio" class="section studio" aria-labelledby="studio-h">
<h2 id="studio-h">가상 연구소라 상담과 견적은 받지 않습니다.</h2>
<div class="studio-grid">
<div class="prose">
<p>스튜디오 폼은 이 쇼케이스를 위해 만든 가상 건축연구소입니다. 프로젝트 기록은 완공 사진, 1층 평면도, 설계 이유를 적은 문단 세 가지로 구성하고, 사진으로는 재료와 빛을, 평면도로는 방 배치와 동선을 보여 줍니다.</p>
</div>
<address class="studio-address">스튜디오 폼<br />연락처와 사무실 주소가 없습니다.</address>
</div>
</section>
</main>
<footer class="foot">
<p class="foot-mark" aria-hidden="true">스튜디오 폼</p>
<p class="foot-line">스튜디오 폼은 가상 브랜드 쇼케이스입니다. 실제 사무소가 아닙니다.</p>
</footer>
</div>
<script>
(function () {
var toc = document.getElementById("toc");
if (!toc) return;
var wide = window.matchMedia("(min-width: 64rem)");
function sync() { toc.open = wide.matches; }
sync();
wide.addEventListener("change", sync);
toc.addEventListener("click", function (event) {
if (!wide.matches && event.target.closest("a")) toc.open = false;
});
var links = document.querySelectorAll(".parts a[data-part]");
if (!links.length || !("IntersectionObserver" in window)) return;
var visible = {};
function mark() {
var current = null;
links.forEach(function (link) {
var id = link.getAttribute("data-part");
if (visible[id] && current === null) current = id;
});
links.forEach(function (link) {
if (link.getAttribute("data-part") === current) link.setAttribute("aria-current", "true");
else link.removeAttribute("aria-current");
});
}
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) { visible[entry.target.id] = entry.isIntersecting; });
mark();
}, { rootMargin: "-40% 0px -45% 0px" });
links.forEach(function (link) {
var target = document.getElementById(link.getAttribute("data-part"));
if (target) observer.observe(target);
});
})();
</script>
</body>
</html>

View file

@ -1,15 +0,0 @@
@font-face {
font-family: "MaruBuri";
src: url("../assets/fonts/MaruBuri-Regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "MaruBuri";
src: url("../assets/fonts/MaruBuri-SemiBold.woff2") format("woff2");
font-style: normal;
font-weight: 600;
font-display: swap;
}

View file

@ -1,91 +1,147 @@
@import url("./tokens.css?v=20260912all");
/* 스튜디오 폼 — 레이아웃·재질·모션. 값은 tokens.css 토큰만 쓴다. */
*, *::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; }
body { min-width: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); font-weight: var(--weight-body); font-size: var(--step-0); line-height: var(--leading-body); -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)); }
a:focus-visible, summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
/* 한글 조판(typography §6-5) */
:lang(ko) { word-break: keep-all; overflow-wrap: anywhere; line-break: strict; }
:lang(ko) :is(p, li, dd, dt, address, figcaption) { letter-spacing: 0; text-wrap: pretty; }
:lang(ko) :is(h1, h2) { text-wrap: pretty; }
:root { font-synthesis-style: none; }
:lang(ko) :is(em, i, cite, dfn, var, address) { font-style: normal; }
:lang(ko) a { text-underline-position: under; }
.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: var(--surface); padding: var(--space-3); }
.skip:focus { left: 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); }
/* 재질: 종이 결. 타일 한 장을 고정 레이어로 깐다. 필터 연산은 로드 때 한 번뿐이다 */
body::before { content: ""; position: fixed; inset: 0; z-index: 50; pointer-events: none; background-image: var(--grain); opacity: var(--grain-opacity); }
.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); }
/* ── 틀: 왼쪽 색인 레일 + 오른쪽 시트 ── */
.frame { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); column-gap: var(--space-6); padding-inline-start: var(--pad); }
.rail { position: sticky; top: 0; align-self: start; min-height: 100dvh; max-height: 100dvh; overflow-y: auto; padding: var(--space-4) var(--space-4) var(--space-5) 0; }
.sheet { min-width: 0; padding-inline-end: var(--pad); }
.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); }
.wordmark { display: inline-flex; align-items: center; min-height: 2.75rem; letter-spacing: -0.01em; text-decoration: none; white-space: nowrap; }
.toc-summary { display: none; }
.toc-label { margin-top: var(--space-5); font-size: var(--step--1); line-height: 1.5; color: var(--ink-faint); }
.toc-note { margin-top: var(--space-2); font-size: var(--step--1); line-height: 1.5; color: var(--ink-faint); }
.index, .parts { list-style: none; padding: 0; margin-top: var(--space-2); 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; }
.index a { display: grid; grid-template-columns: 1.75rem minmax(0, 1fr); column-gap: var(--space-2); min-height: 2.75rem; padding: 0.55rem 0 0.55rem 0.75rem; font-size: var(--step--1); line-height: 1.45; text-decoration: none; color: var(--ink-muted); transition: color var(--dur-quick) var(--ease); }
.index a:hover { color: var(--ink); }
.idx-no { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.idx-name { color: var(--ink); }
.idx-meta { grid-column: 2; display: flex; gap: var(--space-2); }
.idx-meta span:last-child { font-variant-numeric: tabular-nums; }
.index a[aria-current="true"] { border-inline-start: 2px solid var(--ink); padding-inline-start: 0.625rem; }
.index a[aria-current="true"] .idx-no { color: var(--ink); }
.parts a { display: block; min-height: 2.25rem; padding: 0.4rem 0 0.4rem 0.75rem; font-size: var(--step--1); line-height: 1.5; text-decoration: none; color: var(--ink-muted); transition: color var(--dur-quick) var(--ease); }
.parts a:hover { color: var(--ink); }
.parts a[aria-current="true"] { color: var(--ink); border-inline-start: 2px solid var(--ink); padding-inline-start: 0.625rem; }
.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; }
/* 진행선: 레일과 시트 사이 세로 실선. 먹 구간이 스크롤에 따라 아래로 자란다 */
.progress { position: absolute; inset-block: 0; right: 0; width: 1px; background: var(--line); }
.progress-fill { position: absolute; inset: 0; background: var(--ink); transform-origin: top; scale: 1 1; }
/* ── 히어로: 긴 문장 표제 + 전폭 무대 ── */
.hero { padding-top: var(--space-4); }
h1 { font-weight: var(--weight-light); font-size: var(--step-2); line-height: var(--leading-title); letter-spacing: 0; max-width: var(--measure-title); min-height: 2.75rem; padding-top: 0.45rem; }
.stage { margin-top: var(--space-6); margin-inline-end: calc(-1 * var(--pad)); }
.stage img { width: 100%; aspect-ratio: var(--stage-ratio); object-fit: cover; object-position: center 56%; filter: grayscale(1) contrast(1.04); }
figcaption { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); font-size: var(--step--1); line-height: 1.5; color: var(--ink-muted); }
.stage-caption { margin-top: var(--space-2); padding-inline-end: var(--pad); }
.cap-no { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.notice { margin-top: var(--space-4); max-width: var(--measure); font-size: var(--step--1); line-height: 1.6; color: var(--ink-muted); }
/* ── 본문 섹션 ── */
.section { padding-top: var(--space-7); max-width: 80rem; }
h2 { font-weight: var(--weight-light); font-size: var(--step-2); line-height: var(--leading-title); letter-spacing: 0; max-width: var(--measure); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); }
.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--space-3); }
.plan { margin-top: var(--space-5); max-width: var(--sheet-max); view-timeline: --plan block; }
.plan-svg { display: block; width: 100%; height: auto; background: var(--sheet); border: 1px solid var(--line); }
.plan-caption { margin-top: var(--space-2); }
.wall { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: miter; }
.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; }
.window, .door { fill: none; stroke: var(--ink-muted); stroke-width: 1; }
.hatch { fill: none; stroke: var(--line); stroke-width: 1; }
.dim path, .north path { fill: none; stroke: var(--ink-muted); stroke-width: 1; }
.dim text, .north text { fill: var(--ink-faint); font-family: inherit; font-size: 13px; }
.room-label text { fill: var(--ink); font-family: inherit; font-size: 14px; paint-order: stroke; stroke: var(--sheet); stroke-width: 6px; stroke-linejoin: round; }
.drawing-notes { display: grid; grid-template-columns: minmax(0, 1fr) minmax(14rem, 0.55fr); gap: var(--space-4) var(--space-6); margin-top: var(--space-5); max-width: var(--sheet-max); align-items: start; }
.spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); font-size: var(--step--1); line-height: 1.5; }
.spec dt { color: var(--ink-faint); }
.spec dd { margin-top: var(--space-1); font-variant-numeric: tabular-nums; }
.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; }
.photo-grid { display: grid; grid-template-columns: minmax(14rem, 0.5fr) minmax(0, 1fr); gap: var(--space-5); margin-top: var(--space-5); align-items: end; }
.ph figcaption { margin-bottom: var(--space-2); }
.ph img { width: 100%; filter: grayscale(1); }
.ph-tall img { aspect-ratio: 3 / 4; object-fit: cover; }
.ph-panel img { aspect-ratio: var(--panel-ratio); object-fit: cover; object-position: center 58%; }
.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; }
.studio-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(12rem, 0.45fr); gap: var(--space-4) var(--space-6); margin-top: var(--space-5); max-width: var(--sheet-max); align-items: start; }
.studio-address { font-style: normal; color: var(--ink-muted); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); font-size: var(--step--1); line-height: 1.6; }
@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; } }
/* ── 푸터: 큰 워드마크 한 줄 ── */
.foot { grid-column: 1 / -1; margin-top: var(--space-7); padding: var(--space-5) var(--pad) var(--space-4) 0; border-top: 1px solid var(--line-strong); }
.foot-mark { font-weight: var(--weight-light); font-size: var(--step-display); line-height: 1.1; letter-spacing: -0.02em; white-space: nowrap; }
.foot-line { margin-top: var(--space-3); max-width: var(--measure); font-size: var(--step--1); line-height: 1.6; color: var(--ink-muted); }
/* ── 모션: 스크롤 축 ── */
@supports (animation-timeline: view()) {
.plan-draw path { stroke-dasharray: 1; stroke-dashoffset: 1; animation: draw 1ms linear both; animation-timeline: --plan; animation-range: entry calc(10% + var(--i, 0) * 7%) cover calc(42% + var(--i, 0) * 7%); }
.plan-draw path:nth-child(2) { --i: 1; }
.plan-draw path:nth-child(3) { --i: 2; }
.plan-draw path:nth-child(4) { --i: 3; }
.plan-draw path:nth-child(5) { --i: 3; }
.plan-draw path:nth-child(6) { --i: 4; }
.plan-draw path:nth-child(7) { --i: 4; }
@keyframes draw { to { stroke-dashoffset: 0; } }
}
@supports (animation-timeline: scroll()) {
.progress-fill { scale: 1 0; animation: fill 1ms linear both; animation-timeline: scroll(root block); }
@keyframes fill { to { scale: 1 1; } }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.index a, .parts a { transition: none; }
/* 감소 모션은 완성 상태: 도면은 다 그어져 있고 진행선은 정보이므로 남긴다 */
.plan-draw path { animation: none; stroke-dashoffset: 0; }
.progress-fill { animation: none; scale: 1 1; }
}
/* ── 좁은 화면: 레일이 위쪽 접는 차례가 된다 ── */
@media (max-width: 64rem) {
.frame { display: block; padding-inline: var(--pad); }
.rail { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 0; max-height: none; overflow: visible; margin-inline: calc(-1 * var(--pad)); padding: var(--space-1) var(--pad); background: var(--surface); border-bottom: 1px solid var(--line); }
.progress { display: none; }
.toc-summary { display: inline-flex; align-items: center; min-height: 2.75rem; padding-inline: var(--space-2); cursor: pointer; list-style: none; font-size: var(--step--1); }
.toc-summary::-webkit-details-marker { display: none; }
.toc-summary span { border-bottom: 1px solid var(--ink); }
.toc-close-label, .toc[open] .toc-open-label { display: none; }
.toc[open] .toc-close-label { display: inline; }
.toc:not([open]) .toc-body { display: none; }
.toc-body { position: absolute; left: 0; right: 0; top: 100%; max-height: calc(100dvh - 3.5rem); overflow-y: auto; padding: var(--space-2) var(--pad) var(--space-4); background: var(--surface); border-bottom: 1px solid var(--line-strong); }
.toc-label { margin-top: var(--space-3); }
.sheet { padding-inline-end: 0; }
.hero { padding-top: var(--space-5); }
.stage { margin-inline: calc(-1 * var(--pad)); }
.stage-caption { padding-inline: var(--pad); }
.drawing-notes, .photo-grid, .studio-grid { grid-template-columns: 1fr; }
.photo-grid { align-items: start; }
.ph-tall { max-width: 24rem; }
.foot { padding-inline: 0; }
}
@media (max-width: 40rem) {
.spec { grid-template-columns: 1fr; }
.ph-tall { max-width: 100%; }
}

View file

@ -1,34 +1,65 @@
/* 스튜디오 폼 — 토큰. 색·크기·간격은 여기서만 정한다(05 Q-25). */
:root {
--font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif;
--surface: #fdfdfb;
--surface-raised: #ffffff;
--ink: #171b19;
--ink-muted: #5b615d;
--ink-faint: #686e6a;
--line: #d8dbd7;
--line-strong: #a7aca7;
--step--1: 0.75rem;
/* 서체: Gothic A1 한 패밀리, 두 웨이트. 표제·워드마크는 300(가늘고 넓게), 본문·색인은 400 */
--font-body: "Gothic A1", "Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
--weight-light: 300;
--weight-body: 400;
/* 색: 밝은 중립 종이 위에 먹만 쓴다. 강조색 없음(배정). */
--surface: #fcfcfb;
--sheet: #ffffff;
--ink: #161716;
--ink-muted: #585b58;
--ink-faint: #6b6e6b;
--line: #d9dad7;
--line-strong: #9a9c99;
--ink-rgb: 22 23 22;
/* 타입 스케일. 한글 캡션 하한 13px, 본문 16px(typography §6-5) */
--step--1: 0.8125rem;
--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);
--step-1: 1.125rem;
--step-2: clamp(1.25rem, 1.05rem + 0.7vw, 1.625rem);
--step-display: clamp(3.25rem, 1rem + 11vw, 11rem);
--leading-body: 1.7;
--leading-title: 1.55;
--leading-tight: 1.25;
/* 폭: 긴 문장 표제·본문 measure는 em(한글 32~40자) */
--measure: 36em;
--measure-title: 30em;
--rail-w: 15rem;
--sheet-max: 56rem;
--pad: clamp(1rem, 0.6rem + 1.6vw, 2.5rem);
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 1rem;
--space-4: 1.5rem;
--space-5: 2.5rem;
--space-6: clamp(3.5rem, 2.2rem + 4vw, 6rem);
--space-7: clamp(5rem, 3rem + 6vw, 9rem);
--header-h: 4rem;
--space-6: clamp(3rem, 2rem + 3vw, 5rem);
--space-7: clamp(5rem, 3rem + 7vw, 10rem);
/* 모서리: 도면·종이. 0 고정(배정) */
--radius: 0;
/* 무대 비율: 넓은 화면은 극단 파노라마, 세로 화면은 띠가 되지 않게 다시 정한다 */
--stage-ratio: 3.6 / 1;
--panel-ratio: 2.4 / 1;
/* 모션: 상태 전환만 시간 기반. 진행선·도면 선은 스크롤 축 */
--dur-quick: 140ms;
--ease: cubic-bezier(.25, 1, .35, 1);
--ease: cubic-bezier(0.25, 1, 0.35, 1);
/* 재질: 종이 결. feTurbulence 한 장을 타일로 굽는다(런타임 필터 없음). 아주 약하게 */
--grain-opacity: 0.08;
--grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.09 0 0 0 0 0.09 0 0 0 0 0.09 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
}
@media (min-width: 120rem) { :root { --rail: 80rem; --stage: 112.5rem; } }
@media (max-width: 20rem) { :root { --pad: 1rem; } }
@media (max-width: 64rem) {
:root { --stage-ratio: 2.4 / 1; --panel-ratio: 2 / 1; }
}
@media (max-width: 40rem) {
:root { --stage-ratio: 16 / 10; --panel-ratio: 16 / 10; }
}