feat: strengthen design skill and refresh all showcases
Some checks failed
ci / build (push) Has been cancelled
2
apps/site/.htmlvalidateignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Preserve the upstream NAVER license pages verbatim; these are font provenance, not showcase routes.
|
||||
public/work/*/assets/fonts/LICENSE.html
|
||||
|
|
@ -1159,6 +1159,83 @@ Pretendard `dynamic-subset` 은 유니코드 범위별로 파일을 쪼개고
|
|||
- 갤러리 카드 네 장과 공유 이미지 두 장은 확정 화면을 실제 Chrome으로 다시 캡처했다.
|
||||
- 구현 중 발견한 21개 교훈은 `research/showcase-duo-learning-ledger-2026-08-29.md`에 근거와 함께 남겼고, 재사용 가능한 규칙은 `packages/skill/references/trustworthy-showcases.md`와 관련 게이트 문서에 반영했다.
|
||||
|
||||
## 전 쇼케이스 반응형·여백 재감사 (2026-08-30)
|
||||
|
||||
### 연장 범위와 유지한 방향
|
||||
|
||||
이 판은 새 콘셉트로 갈아엎지 않는 **연장 경로**다. 21개 showcase의 서로 다른 프리셋과 실제/생성 이미지, 정보 구조는 유지하고, 화면이 커지거나 작아질 때 그 의도가 무너지는 지점만 고쳤다. 위의 "기존 16종에 남아 있는 것"은 이 항목으로 대체된다.
|
||||
|
||||
- **본문과 정보 표면을 분리했다.** 1920·2560px에서 문장·폼은 기존 `measure`를 지키고, CFD 사양·시세·원장·관리 작업대·히어로처럼 정보 밀도가 필요한 표면만 named wide container로 넓혔다. 전역 `max-width`를 풀어 빈 여백을 만든 곳은 없다.
|
||||
- **가시 내용의 경계를 고쳤다.** 버튼·검색·select·저장 동작·모바일 프리셋·지도 핀의 hitbox와 실제 텍스트에 여백을 주고, 첫/마지막 table/flex 셀의 0px padding을 제거했다. 입력 상자의 실제 겹침도 DOM rect로 검사한다.
|
||||
- **읽기와 접근성을 함께 고쳤다.** 저대비 보조 토큰을 WCAG AA에 맞게 조정하고, `transition: all`을 대상 속성으로 분리했다. 갤러리의 클릭 div는 overlay button으로 바꾸고, 표 header `scope`, skip link, input label/name/autocomplete, `prefers-reduced-motion`, icon button 이름, 이미지 intrinsic size를 보완했다.
|
||||
- **가상 흐름의 진실 범위를 가깝게 표시했다.** 청온·카스크·마테리아의 견적/예약/갤러리 예시는 행동 근처에서 예시·저장/전송 없음 범위를 알리고, 만들어 낸 연락처·기본 개인정보·가짜 평점은 제거했다.
|
||||
|
||||
### 감사기와 기준 화면
|
||||
|
||||
`tools/showcase-audit.mjs`는 기본 폭을 `320, 390, 768, 1440, 1920, 2560`으로 확장했다. 실제 렌더에서 가로 overflow, 대비, h1, 컨트롤 줄바꿈뿐 아니라 다음을 hard fail로 잰다.
|
||||
|
||||
- 버튼 라벨·field value·표/ARIA cell의 가시 좌우 inset(기본 8 CSS px)
|
||||
- visible input/select/textarea의 면적 겹침
|
||||
- native/ARIA table cell의 실제 글자와 셀 테두리 사이 여백
|
||||
|
||||
CSS `color-mix()`가 Chromium에서 반환하는 CSS Color 4 `color(srgb …)`와 반투명 background 합성을 파서에 반영했다. 또 sr-only/aria-hidden 장식/빈 overlay를 `Range.selectNodeContents()`로 가시 텍스트에 섞어 여백 실패를 만들던 측정 오류를 text node 단위로 고쳤다. 테스트 도구의 false RED를 UI 수정으로 덮지 않는다.
|
||||
|
||||
가온의 세 시각 기준은 44px 알림 hitbox와 캘린더·요약 표의 좌우 여백이라는 의도 변경을 실제 current/baseline 캡처로 비교한 뒤에만 갱신했다. `tools/visual.mjs --update-baseline=<이름,...>`로 대상 기준만 갱신할 수 있게 하여 다른 기준 화면을 재촬영으로 덮지 않는다.
|
||||
|
||||
### 6단계 증거
|
||||
|
||||
- 전 쇼케이스 정적 실렌더: **26개 HTML × 6개 폭 = 156/156 PASS**
|
||||
- 저장소 전체 `npm run verify`: L0~L6 **전 계층 PASS**. 여기에는 실렌더 156, 메인 디자인 게이트 178, 제품 게이트 15, 시각 회귀 30, WebKit 8, 최근 쇼케이스 E2E 115, TWENTYOZ E2E 148, 쇼케이스 듀오 90 scenario / 350 assertion / 12 capture가 포함된다.
|
||||
- `npm run build`: 성공. 배포 텍스트 한자 검사도 **98개 파일 PASS**.
|
||||
- 21개 카드의 현재 화면/보조 화면과 듀오 OG를 실제 Chromium으로 다시 캡처했다.
|
||||
|
||||
### 재사용 규칙 승격
|
||||
|
||||
문헌 근거와 이번의 RED→GREEN 증거는 designpaca skill의 `references/layout.md`, `references/preflight.md`, `references/audit-gate.md`에 반영했다. 초광폭 레이아웃을 별도 상태로 다루고, WCAG의 최소 target size와 프로젝트의 시각 여백 기본값을 구분하며, 반응형 표·폼·진실 라벨·측정기 정확도를 함께 게이트로 유지한다.
|
||||
|
||||
## 정적 쇼케이스 재구성 · 모달 위치 · 초광폭 이미지 계약 (2026-08-30)
|
||||
|
||||
### 이번 판의 판단
|
||||
|
||||
최근의 상태형 작업물(목요일 화원, 마테리아, 한남자리, 철판클럽, RFS, 새벽청과, TWENTYOZ)은 이미 각 도메인의 정보 구조와 상호작용이 충분히 구분되어 있어 새 문법으로 덮지 않았다. 대신 그 안의 팝업 위치·짧은 화면·초광폭 수축 결함만 고쳤다. 오래된 정적 쇼케이스는 다음 10개를 실제 정보 표면과 첫 화면의 판단 근거가 보이도록 재구성했다.
|
||||
|
||||
- `baekje-celadon`, `font-foundry`, `form-architecture`, `hyang-incense`: 전시·활자·도면·기록이 첫 시야에서 행동과 함께 읽히도록 재배치
|
||||
- `lumina-optics`, `orbit-aerospace`, `pulsegate`, `synapse-bci`: 빛/비행/접근/임상이라는 주장에 대응하는 실제 시험 조건·표본·한계를 판독 레지스터로 고정
|
||||
- `muwol-coffee`, `overtone-sound`: 장식적 영웅면 대신 로스터의 좌표/원장과 설치 아카이브의 조건을 정보 밀도로 구성
|
||||
|
||||
### 사용자 보고에서 고정한 계약
|
||||
|
||||
- 마테리아의 native dialog는 전역 reset이 UA `margin`을 지워 좌상단에 붙었던 결함이었다. 프로젝트 modal과 한남 듀플렉스 gallery lightbox에 `fixed + inset:0 + margin:auto`와 viewport 상한·내부 scroll owner를 명시했다. 390·1440·2560에서 중심 오차 0~0.008px, 배경 scroll lock을 자동 단언한다.
|
||||
- 가온의 알림 메뉴, 한남자리 공개 메뉴와 관리자 조사관은 390×320에서 화면 밖/0px scroll 상태가 되지 않도록 viewport-bound와 short-height 구성을 분리했다.
|
||||
- 철판클럽은 2560에서 hero `1680px`, copy `688px`, media `856.7px`, 다음 content rail `1400px`으로 고정했다. 사진을 화면 반까지 무한 확장하지 않으며, `stage-wide/media-wide/copy-wide/workbench-wide`를 구분한다.
|
||||
|
||||
### 재발 방지와 증거
|
||||
|
||||
`tools/audit.mjs`가 마테리아 두 modal의 중심/scroll lock을 390·1440·2560에서, 한남자리 짧은 화면 scroll owner와 가온 알림 메뉴를 390×320에서, 철판클럽 hero/media/content rail을 2560에서 hard fail로 검사한다. 이 규칙은 공통 스킬의 `layout.md`, `preflight.md`, `audit-gate.md`에 승격했다. 모달의 포커스·키보드·닫기 계약은 [WAI-ARIA APG](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)를 근거로 한다.
|
||||
|
||||
### 공개 캐시 확인
|
||||
|
||||
정적 CSS의 stable URL은 HTML만 새로 배포해도 사용자 브라우저에 이전 스타일을 남길 수 있다. 이번 수정의 `main/site/tokens/franchise/landing/admin` 링크와 CSS `@import` 버전을 함께 올렸다. 공개 검증은 HTTP 200이 아니라 새 stylesheet href와 새 토큰·핵심 computed value가 실제로 로드됐는지까지 확인한다.
|
||||
|
||||
### 후속 정착 프레임 캡처·초광폭 보정 (2026-08-30)
|
||||
|
||||
- 모든 쇼케이스 카드·OG 캡처 경로를 `tools/capture-stable.mjs`로 통합했다. `file://` 즉시 스크린샷 대신 no-store 로컬 HTTP에서 폰트·이미지 decode·페이지별 bounded settle을 기다린 뒤, reduced-motion 환경과 video/audio·WAAPI·rAF 정지로 완성된 한 프레임을 저장한다. local 4xx, pageerror, 깨진 이미지는 결과물 생성 실패로 취급한다.
|
||||
- 철판클럽의 2560px 히어로는 기존 1680px cap이 실제로 지나치게 좁아 양쪽에 맹탕을 만들었다. 히어로만 `2160px`, 카피 `900px`, 미디어 `1120px`으로 분리하고 메뉴/본문 레일은 `1400~1480px`에 남겼다. 넓은 화면은 하나의 전역 상한이 아니라, 시선의 중심이 되는 히어로와 읽기/정보 표면이 각각 다른 폭을 소유한다.
|
||||
- 이미지 생성은 제공된 실제 사진을 대체하지 않는다. 브랜드가 요구하는 정확한 장면·광학적 분위기 같은 시각 자산이 실제로 비어 있고, 생성물·연출 라벨을 함께 둘 수 있을 때만 이미지 바이블의 슬롯별로 생성한다. 이번 두 결함은 각각 레이아웃과 WebGL 역할의 문제라서 생성 이미지로 덮지 않는다.
|
||||
|
||||
### 사용자 보고로 보강한 과정 분할 계약 (2026-08-30)
|
||||
|
||||
- 철판클럽의 `process`는 hero와 별도의 텍스트/이미지 분할 표면이다. 기존 모바일/중간 폭의 `padding-left: calc((100vw - --wrap) / 2)`가 초광폭 규칙에 남아 2560px에서 실제 제목·설명 너비를 **12px**로 만들었다. hero만 통과한 것은 이 결함의 증거가 아니다.
|
||||
- 초광폭 과정 표면은 `--stage-wide` 1680px 안에서 copy **822px**, media **856px**로 제한하고 양쪽 logical padding을 함께 재설정했다. 제목은 의도한 2줄, 목록 문장은 읽는 폭으로 돌아왔으며 사진은 화면 절반까지 팽창하지 않는다.
|
||||
- `tools/audit.mjs`는 hero뿐 아니라 이처럼 DOM/여백 규칙이 다른 각 text-media split의 rail, copy/media 폭, 실제 제목 폭과 줄 수를 2560px에서 hard fail로 잠근다. 재사용 스킬의 preflight·layout·audit 규칙도 같은 원리로 보강했다.
|
||||
|
||||
### 사용자 코멘트로 정정한 제품 카피와 이미지 계약 (2026-08-30)
|
||||
|
||||
- 가상 흐름이라도 사용자가 하려는 일이 아닌 제작 목적을 문구로 노출하지 않는다. `화면 동작용`, `쇼케이스`, `저장`처럼 구현을 설명하는 말은 `실제 주문·결제·저장을 제공하지 않습니다`처럼 행동과 한계를 함께 이해할 수 있는 말로 바꾼다.
|
||||
- 조합 결과에 영속 동작이 없으면 저장 버튼이나 성공 상태를 만들지 않는다. 다음 행동은 실제로 가능한 범위 안에서 가상 매장 안내처럼 목적이 분명한 경로로 연결한다.
|
||||
- CSS 조각으로 제품 자체를 흉내 내지 않는다. 철판클럽의 싱글 버거는 전용 4:3 생성 이미지(`assets/single-smash-generated.webp`, `960×720`)로 바꾸고 카드 안에 `생성 이미지` 라벨을 붙였다. 제품 사진은 제품을 고르는 근거이고, DOM 도식은 비교·설명에만 쓴다.
|
||||
- 조합기처럼 반복되는 fieldset은 경계선만으로 묶지 않는다. 다음 fieldset 앞에 16px의 명시적 간격을 두고 1217px 실화면에서 legend와 이전 선택지가 붙어 보이지 않는지 확인한다.
|
||||
|
||||
## 홍보 사이트 전면 재설계 — Quiet Editorial Proof Surface (2026-08-30)
|
||||
|
||||
### 문제와 방향
|
||||
|
|
@ -1214,3 +1291,15 @@ Pretendard `dynamic-subset` 은 유니코드 범위별로 파일을 쪼개고
|
|||
- `2026-08-30` · 국소 — 히어로 아래 방향 결정·토큰 조판·검증 프레임과 7단계 진행 레일도 각각의 named CSS view timeline에 연결한다. 본문과 수치는 움직이지 않고 방향선·글립·토큰 표본·검사 화살표·짧은 진행선만 transform/opacity로 보간하며, 검증 프레임의 가장 넓은 판은 두 작은 판을 맞추는 고정 기준면으로 남긴다. 모바일 검증 완료 지점은 sticky GNB 아래 제목 여유 24px 이상에서 끝나도록 앞당기고, 미지원 브라우저·감소 모션·이펙트 제거 상태는 정적 완료 화면을 사용한다. KO/EN 전진·역스크롤 geometry 8개, 320~2560px 반응형, effects-off와 감소 모션을 80/80 시나리오·1000/1000 단언으로 고정한다.
|
||||
- `2026-08-30` · 국소 — 사용자 체감 실패를 반영해 방향 결정·토큰과 조판의 timeline subject를 article에서 실제 `.direction-stage`·`.type-stage`로 내리고, 방향 면·벡터·세 노드와 글립·세 토큰 표본을 각 자식의 첫 가시 시점 이후에 서로 다른 범위로 조립한다. 글자 대비는 opacity로 낮추지 않으며 미지원·감소 모션·이펙트 제거 상태는 정적 완료 화면이다. KO/EN × 390/1440에서 110px 실제 wheel 전진·역진, offscreen 진행 상한, 가시 구간 dwell, 독립 위상과 token stagger를 새 E2E 계약으로 추가했고 전체 85/85 시나리오·1094/1094 단언을 통과했다.
|
||||
- `2026-08-30` · 사용자 실화면 정정 — Direction의 한 줄짜리 독립 변형을 폐기하고, 정확한 세 점 좌표를 공유하는 두 SVG 선분으로 나눠 시작점을 고정한 채 스크롤로 길이만 드러낸다. 노드는 이동하지 않고 앞 선분이 좌표에 닿은 뒤 같은 중심에서 나타나며, 모바일도 `xMidYMid meet`로 비율을 보존한다. 마지막 선과 점은 `cover 80% / 86%` 안에 끝내 sticky GNB 아래에서 완성 상태를 읽을 시간을 남긴다. Tokens는 글립·기준선·세 역할 표본, Verification은 두 가변 프레임·네 검사 화살표, Process는 작은 행 장식 대신 목록 전체 높이의 연속 진행선을 각 named view timeline으로 보간한다. 실제 앱 브라우저 1316×1272에서 110px 전진·역진과 Direction endpoint 연결을 확인했고, KO/EN 320~2560px 전체 게이트는 86/86 시나리오·1122/1122 단언·8/8 캡처로 통과했다.
|
||||
|
||||
### 디자인 전문성 근거와 홍보 카피 정렬 (2026-09-12)
|
||||
|
||||
- Quiet Editorial Proof Surface의 밝은 Wanted Sans·신호 적색·이미지 없는 산출물 표본 세 개와 히어로의 양방향 스크롤 계약은 그대로 둔다. 고객 성과처럼 읽힐 수 있는 프로젝트 캡처를 홈에 되돌리지 않으며, 실제 사이트는 낮은 위계의 `/showcase/` 또는 `/en/showcase/` 링크로 직접 찾는다.
|
||||
- 히어로는 웹 디자인 원리와 실제 사례 조사, 브랜드 맥락에 맞는 방향 결정, 반응형 구현, 실화면 검증을 한 제품 흐름으로 설명한다. 이 순서는 [NN/g의 사용성 휴리스틱](https://www.nngroup.com/articles/ten-usability-heuristics/)으로 확인하는 보편 기준과, 프로젝트 목적에 맞춰 선택하는 시각 스타일을 구분하는 근거를 드러낸다.
|
||||
- 검증 근거는 수나 전환 성과로 포장하지 않는다. `7`은 0~6 파이프라인이고, 나머지 두 사실은 근거·맥락의 분리와 기능 검사/실화면 검토의 분리다. [Webby Awards 심사 기준](https://www.webbyawards.com/judging-criteria/)의 콘텐츠·구조와 내비게이션·시각 디자인·기능성·상호작용·전반 경험 구분도 화면의 인상만으로 품질을 단정하지 않는 보조 근거로 쓴다.
|
||||
- 세부 검사에는 320px, 대비, 키보드, 감소 모션, 200% 확대, 실제 전후 화면의 구성·위계·반응형 동작, 출처 연결을 포함한다. 기존의 토큰·SEO 검사는 유지하되, 프로젝트에 따라 달라지는 검증 범위를 단일 하드 게이트 수로 홍보하지 않는다.
|
||||
- 보존한 계약: 실제 고객 성과 주장 없음, 홈 산출물에는 이미지 없음, 히어로 그래픽과 모션은 변경하지 않음, 한국어·영어는 같은 정보 구조와 목적지 링크를 가짐. 빌드한 사이트 E2E는 86개 시나리오·1,122개 단언·8개 캡처를 통과했고, 개발 서버의 별도 증거는 KO/EN × 320·390·768·1440·1920·2560에서 overflow 0, 새 링크 12/12의 44px 이상 hit area와 키보드 목적지 2/2를 확인했다. 실제 클립보드 권한은 이 headless 환경에서 거절되어 두 언어 모두 선택 복사 안내와 전체 명령 선택으로 안전하게 전환됨을 확인했다. 새 캡처와 JSON은 `outputs/design-expertise-20260912/home/`에 보관한다.
|
||||
|
||||
### 확대 텍스트 표본 보정 (2026-09-12)
|
||||
|
||||
- 홈의 Direction 표본 문장은 절대 배치와 `overflow: clip` 조합에서 200% 확대 시 잘릴 수 있었다. 문장을 `position: relative`의 normal flow로 옮기고 기존 왼쪽 48%·너비를 유지했다. stage는 `display: flow-root`로 첫 자식 margin의 외부 collapse를 막는다. 기본 2:1 표본은 stage width 기준 `margin-top: 24%`, `margin-bottom: 5%`로 기존 중앙 위치를 보존하며, 긴 문장은 stage 높이를 자연스럽게 늘린다. 모바일 16:9에서는 `margin-top: 27%`, 28rem 이하에서는 기존 왼쪽 39%·너비 55%를 유지한다.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"report",
|
||||
"notice"
|
||||
],
|
||||
"h1": "h2.v-h",
|
||||
"h1": ".view.is-on h2.v-h",
|
||||
"brand": ".brand"
|
||||
},
|
||||
{
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
"archive",
|
||||
"notices"
|
||||
],
|
||||
"h1": "h2.v-h",
|
||||
"h1": ".view.is-on h2.v-h",
|
||||
"brand": ".brand"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"test:e2e:recent-showcases": "node tools/recent-showcases-e2e.mjs",
|
||||
"test:e2e:twentyoz": "node tools/twentyoz-e2e.mjs",
|
||||
"test:e2e:showcase-duo": "node tools/showcase-duo-e2e.mjs",
|
||||
"capture:showcase": "node tools/capture-shots.mjs && node tools/capture-work-shots.mjs && node tools/capture-new-shots.mjs && node tools/capture-showcase-duo.mjs",
|
||||
"capture:showcase-duo": "node tools/capture-showcase-duo.mjs",
|
||||
"capture:site-og": "node tools/capture-site-og.mjs",
|
||||
"prebuild": "npm run build:showcase-assets",
|
||||
|
|
|
|||
169
apps/site/public/work/baekje-celadon/assets/fonts/LICENSE.html
Normal file
13
apps/site/public/work/baekje-celadon/assets/fonts/SOURCES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
17
apps/site/public/work/baekje-celadon/design.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 비색의 침묵
|
||||
|
||||
암실의 청자 사진을 주연으로 유지하고 회색 카드나 격자는 추가하지 않는다. 제1·2실의 방 번호, 제목, 재질 설명, 매크로 캡션을 공통 레일로 정리했다. 첫 사진은 기존 4:5 비율을 보존하고 관람 구성 CTA와 가상 관람 안내를 인접하게 둔다.
|
||||
|
||||
| 판단 축 | 적용 | 한계 |
|
||||
| --- | --- | --- |
|
||||
| 구도 | Hofmann의 레일과 비대칭을 참고해 방 정보와 캡션을 같은 열에 맞췄다. | 전시 사진의 감상 효과를 보편적으로 예측하지 않는다. |
|
||||
| 타입 | Ruder를 참고해 방 번호와 제목·설명의 크기 차이를 명확히 했다. | 서체만으로 체류나 이해가 바뀐다고 단정하지 않는다. |
|
||||
| 색 | Albers를 참고해 어두운 바탕, 비색 강조, 밝은 CTA의 역할을 나눴다. | 실제 명도 대비는 브라우저 검증에서 확인한다. |
|
||||
|
||||
토큰은 `--surface`, `--ink`, `--accent`, `--container`, `--space-*`를 유지한다. 로컬 MaruBuri 400/600은 `styles/fonts.css`, Pretendard는 기존 CDN을 유지한다. 가상 전시·관람 안내와 기존 앵커는 보존했다.
|
||||
|
||||
## 2026-09-12 현재 검증 기록
|
||||
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -3,15 +3,15 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>비색의 침묵 · 고려청자 특별전</title>
|
||||
<title>비색의 침묵 · 고려청자 전시 구성</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="천 년의 비취색, 흙과 불이 빚은 침묵의 곡선. 고려청자 유약의 빙렬과 상감을 세 개의 방에서 감상하는 특별전. 2026년 가을, 서울."
|
||||
content="고려청자의 빙렬과 상감을 세 개의 방으로 읽는 가상 전시 구성입니다. 일정·장소·예약 정보는 예시입니다."
|
||||
/>
|
||||
<meta property="og:title" content="비색의 침묵 · 고려청자 특별전" />
|
||||
<meta property="og:title" content="비색의 침묵 · 고려청자 전시 구성" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="유약의 빙렬과 상감 문양을 세 개의 방에서 감상하는 특별전."
|
||||
content="유약의 빙렬과 상감 문양을 세 개의 방으로 읽는 가상 전시 구성."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="theme-color" content="#121514" />
|
||||
|
|
@ -19,13 +19,13 @@
|
|||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
href="./styles/fonts.css?v=20260912all"
|
||||
/>
|
||||
<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="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 width='32' height='32' fill='%23121514'/%3E%3Cpath d='M11 6h10c-1 3-1 5 0 7 2 3 3 5 3 8 0 4-3 7-8 7s-8-3-8-7c0-3 1-5 3-8 1-2 1-4 0-7z' fill='none' stroke='%236FA08F' stroke-width='1.5'/%3E%3C/svg%3E"
|
||||
|
|
@ -47,10 +47,6 @@
|
|||
</header>
|
||||
|
||||
<main id="main">
|
||||
<!--
|
||||
첫 방 — 암실. 전시 포스터가 아니라 첫 유물 하나.
|
||||
명암이 곧 구조다: 사진이 어두울수록 유약이 가까워진다.
|
||||
-->
|
||||
<section class="hero" aria-labelledby="hero-h">
|
||||
<div class="wrap hero-grid">
|
||||
<figure class="hero-figure reveal">
|
||||
|
|
@ -63,13 +59,13 @@
|
|||
fetchpriority="high"
|
||||
/>
|
||||
<figcaption>
|
||||
<span>청자 상감운학문 주자 · 12세기</span>
|
||||
<span>호암미술관 소장</span>
|
||||
<span>청자 주자 · 전시 연출 이미지</span>
|
||||
<span>가상 전시 소품</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<div class="hero-text">
|
||||
<p class="hero-kicker reveal" style="--delay: 100ms">
|
||||
특별전 · 2026 가을
|
||||
가상 전시 구성 · 고려청자
|
||||
</p>
|
||||
<h1 id="hero-h" class="reveal" style="--delay: 160ms">
|
||||
천 년의 비색은<br />말이 없다
|
||||
|
|
@ -80,17 +76,13 @@
|
|||
않고 세 개의 암실에 놓아 두었습니다.
|
||||
</p>
|
||||
<div class="hero-actions reveal" style="--delay: 300ms">
|
||||
<a class="btn" href="#visit">도슨트 예약</a>
|
||||
<p class="hero-note">9월 5일 – 11월 22일 · 국립중앙박물관 기획전시실</p>
|
||||
<a class="btn" href="#visit">관람 구성 보기</a>
|
||||
<p class="hero-note">일정·장소·예약 정보는 전시 구성 예시이며 실제 관람을 접수하지 않습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
첫 번째 방 — 빙렬. 유약이 식으며 스스로 그은 그림.
|
||||
풀블리드 판: 페이지에서 가장 큰 면적을 질감에 내어준다.
|
||||
-->
|
||||
<section id="room-1" class="section plate" aria-labelledby="room1-h">
|
||||
<div class="wrap plate-head">
|
||||
<p class="room-no reveal">제1실</p>
|
||||
|
|
@ -110,15 +102,11 @@
|
|||
loading="lazy"
|
||||
/>
|
||||
<figcaption class="wrap">
|
||||
빙렬 확대 · 12세기 청자 표면 · 측광
|
||||
<span>빙렬 확대 · 12세기 청자 표면 · 측광</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
두 번째 방 — 상감. 새기고 채우는 글자 없는 문장.
|
||||
사진은 절반만. 나머지 절반은 문양의 문법을 텍스트로.
|
||||
-->
|
||||
<section id="room-2" class="section" aria-labelledby="room2-h">
|
||||
<div class="wrap two">
|
||||
<div class="two-text">
|
||||
|
|
@ -151,35 +139,32 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
관람 — 예약과 안내. 마지막 방은 서류다.
|
||||
-->
|
||||
<section id="visit" class="section band" aria-labelledby="visit-h">
|
||||
<div class="wrap visit-grid">
|
||||
<div>
|
||||
<p class="room-no reveal">안내</p>
|
||||
<h2 id="visit-h" class="reveal" style="--delay: 60ms">세 개의 방, 예약제</h2>
|
||||
<h2 id="visit-h" class="reveal" style="--delay: 60ms">세 개의 방으로 보는 전시 구성</h2>
|
||||
<p class="reveal" style="--delay: 120ms">
|
||||
암실 조명 때문에 회당 인원을 30명으로 제한합니다. 도슨트는 하루 세 번,
|
||||
유약과 가마 이야기만 합니다.
|
||||
조명과 동선이 관람 경험을 어떻게 바꾸는지 보여 주기 위한 예시입니다.
|
||||
아래 일정과 해설 방식은 실제 전시 운영 정보가 아닙니다.
|
||||
</p>
|
||||
</div>
|
||||
<dl class="visit-dl reveal" style="--delay: 160ms">
|
||||
<div>
|
||||
<dt>기간</dt>
|
||||
<dt>전시 기간 예시</dt>
|
||||
<dd>2026년 9월 5일 – 11월 22일 · 월요일 휴관</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>시간</dt>
|
||||
<dt>관람 시간 예시</dt>
|
||||
<dd>10:00–18:00 · 금·토 21:00까지</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>도슨트</dt>
|
||||
<dt>해설 구성 예시</dt>
|
||||
<dd>11:00 / 14:00 / 16:00 · 회당 40분</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>예약</dt>
|
||||
<dd><a class="link" href="#visit">국립중앙박물관 예약 시스템</a></dd>
|
||||
<dd>실제 예약 시스템과 연결되지 않습니다.</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
@ -188,23 +173,10 @@
|
|||
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>비색의 침묵 · 고려청자 특별전</p>
|
||||
<p class="muted">국립중앙박물관 기획전시실 · 서울 용산</p>
|
||||
<p>비색의 침묵 · 고려청자 전시 구성</p>
|
||||
<p class="muted">가상 전시 프로젝트 · 장소와 소장 정보는 예시입니다.</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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
15
apps/site/public/work/baekje-celadon/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import url("./tokens.css");
|
||||
@import url("./tokens.css?v=20260912all");
|
||||
|
||||
/* ==========================================================================
|
||||
비색의 침묵 — editorial-archival. 암실의 세 개의 방.
|
||||
|
|
@ -132,6 +132,7 @@ a {
|
|||
}
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: clamp(var(--space-3), 2vw, var(--space-5));
|
||||
font-size: var(--step--1);
|
||||
}
|
||||
|
|
@ -151,13 +152,25 @@ a {
|
|||
/* ── 첫 방(히어로) ────────────────────────────────────────────────────── */
|
||||
|
||||
.hero {
|
||||
padding-block: var(--space-7);
|
||||
display: grid;
|
||||
align-items: center;
|
||||
min-block-size: min(46rem, calc(100svh - 4rem));
|
||||
padding-block: var(--space-6);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
|
||||
gap: var(--space-6) var(--space-7);
|
||||
align-items: end;
|
||||
grid-template-columns: minmax(15rem, 34rem) minmax(0, 1fr);
|
||||
gap: clamp(var(--space-5), 6vw, var(--space-8));
|
||||
align-items: center;
|
||||
}
|
||||
.hero-grid > *,
|
||||
.two > *,
|
||||
.visit-grid > * {
|
||||
min-width: 0;
|
||||
}
|
||||
.hero-figure {
|
||||
width: min(100%, 34rem);
|
||||
}
|
||||
.hero-figure img {
|
||||
display: block;
|
||||
|
|
@ -179,6 +192,10 @@ a {
|
|||
letter-spacing: 0.12em;
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.hero-text {
|
||||
max-width: 39rem;
|
||||
padding-block: var(--space-4);
|
||||
}
|
||||
.hero-text .lead {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
|
@ -194,7 +211,10 @@ a {
|
|||
color: var(--ink-faint);
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
padding-inline: var(--space-4);
|
||||
background: var(--ink);
|
||||
color: var(--surface);
|
||||
font-weight: 500;
|
||||
|
|
@ -221,6 +241,9 @@ a {
|
|||
.section {
|
||||
padding-block: var(--space-8);
|
||||
}
|
||||
.section[id] {
|
||||
scroll-margin-top: 6rem;
|
||||
}
|
||||
.band {
|
||||
background: var(--surface-raised);
|
||||
border-block: 1px solid var(--line);
|
||||
|
|
@ -229,16 +252,27 @@ a {
|
|||
/* ── 제1실 — 풀블리드 판 ──────────────────────────────────────────────── */
|
||||
|
||||
.plate-head {
|
||||
max-width: 40em;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 1fr) minmax(0, 5fr);
|
||||
column-gap: var(--space-5);
|
||||
max-width: var(--container);
|
||||
}
|
||||
.plate-head .room-no {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 2;
|
||||
margin: 0;
|
||||
}
|
||||
.plate-head h2 {
|
||||
grid-column: 2;
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
.plate-head .lead {
|
||||
grid-column: 2;
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
.plate-figure {
|
||||
margin-top: var(--space-6);
|
||||
width: min(100% - 2 * var(--pad-inline), var(--container-wide));
|
||||
margin: var(--space-6) auto 0;
|
||||
}
|
||||
.plate-figure img {
|
||||
display: block;
|
||||
|
|
@ -246,10 +280,16 @@ a {
|
|||
height: auto;
|
||||
}
|
||||
.plate-figure figcaption {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 1fr) minmax(0, 5fr);
|
||||
column-gap: var(--space-5);
|
||||
font-size: var(--step--1);
|
||||
color: var(--ink-faint);
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
.plate-figure figcaption span {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
/* ── 제2실 — 2단 ──────────────────────────────────────────────────────── */
|
||||
|
||||
|
|
@ -317,22 +357,22 @@ a {
|
|||
/* ── 등장 모션 ────────────────────────────────────────────────────────── */
|
||||
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
translate: 0 14px;
|
||||
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;
|
||||
}
|
||||
.two-text {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 1fr) minmax(0, 5fr);
|
||||
column-gap: var(--space-5);
|
||||
}
|
||||
.two-text .room-no {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 3;
|
||||
margin: 0;
|
||||
}
|
||||
.two-text h2,
|
||||
.two-text p {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
/* ── 반응형 ───────────────────────────────────────────────────────────── */
|
||||
|
|
@ -347,6 +387,13 @@ a {
|
|||
.hero-grid {
|
||||
align-items: start;
|
||||
}
|
||||
.hero {
|
||||
min-block-size: auto;
|
||||
}
|
||||
.hero-text {
|
||||
order: -1;
|
||||
padding-top: 0;
|
||||
}
|
||||
.hero-figure {
|
||||
max-width: 26rem;
|
||||
}
|
||||
|
|
@ -354,6 +401,24 @@ a {
|
|||
.section {
|
||||
padding-block: var(--space-6);
|
||||
}
|
||||
.plate-head,
|
||||
.two-text,
|
||||
.plate-figure figcaption {
|
||||
grid-template-columns: 1fr;
|
||||
row-gap: var(--space-3);
|
||||
}
|
||||
.plate-head .room-no,
|
||||
.plate-head h2,
|
||||
.plate-head .lead,
|
||||
.two-text .room-no,
|
||||
.two-text h2,
|
||||
.two-text p {
|
||||
grid-column: 1;
|
||||
grid-row: auto;
|
||||
}
|
||||
.plate-figure figcaption span {
|
||||
grid-column: 1;
|
||||
}
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.head-inner {
|
||||
|
|
@ -363,6 +428,6 @@ a {
|
|||
}
|
||||
.nav {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@
|
|||
--surface-raised: #191d1c;
|
||||
--ink: #eceae2; /* 백색이 아니라 골동지의 밝기 */
|
||||
--ink-muted: #a7a69b;
|
||||
--ink-faint: #7b7d76;
|
||||
--ink-faint: #878982;
|
||||
--line: #2b302e;
|
||||
--accent: #8fbcaa; /* 비색 — 텍스트용으로 밝힘. 배경 채움 금지 */
|
||||
--accent-dim: #5e8274;
|
||||
--accent-dim: #80ac99;
|
||||
--ink-bright: #ffffff; /* CTA hover */
|
||||
|
||||
--space-1: 0.25rem;
|
||||
|
|
@ -45,8 +45,11 @@
|
|||
|
||||
--radius: 2px;
|
||||
|
||||
--container: 1120px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
|
||||
/* 읽는 폭과 유물 판은 분리한다. 2560px에서도 문장이 늘어지지 않고,
|
||||
빙렬 이미지만 전시 벽처럼 넓어진다. */
|
||||
--container: 1200px;
|
||||
--container-wide: 1600px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem);
|
||||
|
||||
--dur-quick: 160ms;
|
||||
--dur-normal: 480ms;
|
||||
|
|
|
|||
169
apps/site/public/work/cask-archive/assets/fonts/LICENSE.html
Normal file
13
apps/site/public/work/cask-archive/assets/fonts/SOURCES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
17
apps/site/public/work/cask-archive/design.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 캐스크 아카이브 — 디자인 결정 기록
|
||||
|
||||
## 관찰과 방향
|
||||
|
||||
- 관찰: 어두운 앰버 표면의 병 사진은 이중 베젤과 떠 있는 태그 때문에 작은 카드처럼 보였다.
|
||||
- 원리 적용 범위: 병 사진은 4:5의 큰 표면으로 두고 캐스크 번호·종류·향미 조건은 바로 이어지는 원장형 캡션으로 묶는다. 검색·필터·도슨트 세션 선택은 같은 선과 상태 대비로 읽힌다.
|
||||
- 결정: 병 사진의 베젤을 제거하고 캡션을 사진 아래 정렬선에 맞췄다. 실제 수입·인증·예약을 뜻하는 문구는 예시 메타데이터와 세션 안내로 제한한다.
|
||||
- 기각 조건: 긴 캐스크 메타가 320px에서 넘치거나 필터·폼 상태가 앰버 대비에서 읽히지 않으면 캡션과 컨트롤의 줄바꿈 규칙을 조정한다.
|
||||
|
||||
## 토큰과 검증
|
||||
|
||||
- 타입: 로컬 `MaruBuri` 400·600과 기존 Pretendard·모노 역할을 유지한다.
|
||||
- 색: `--accent` 앰버, 어두운 `--surface`, 얇은 원장 선을 유지한다.
|
||||
- 구도: 넓은 화면의 hero는 5:7 텍스트/병 사진, 병은 4:5, 캡션은 사진과 같은 폭을 사용한다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -9,10 +9,7 @@
|
|||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/fonts.css?v=20260912all" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
|
|
@ -21,8 +18,9 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/tokens.css" />
|
||||
<link rel="stylesheet" href="../styles/main.css" />
|
||||
<link rel="stylesheet" href="../styles/tokens.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 width='32' height='32' rx='8' fill='%230B0B0D'/%3E%3Ccircle cx='16' cy='16' r='10' stroke='%23D99B38' stroke-width='2' fill='none'/%3E%3Cpath d='M16 8v16M8 16h16' stroke='%23D99B38' stroke-width='1.5'/%3E%3C/svg%3E" />
|
||||
<style>
|
||||
.breadcrumb-bar {
|
||||
padding-top: var(--space-4);
|
||||
|
|
@ -82,7 +80,7 @@
|
|||
|
||||
.course-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
|
||||
gap: var(--space-5);
|
||||
margin-top: var(--space-5);
|
||||
}
|
||||
|
|
@ -136,6 +134,7 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main-content">본문으로 건너뛰기</a>
|
||||
<!-- Header (GNB SSOT) -->
|
||||
<header class="site-head">
|
||||
<div class="wrap head-inner">
|
||||
|
|
@ -150,7 +149,7 @@
|
|||
<a href="../index.html#subscribe">뉴스레터</a>
|
||||
</nav>
|
||||
<div class="head-actions">
|
||||
<a href="#reservation-section" class="btn">도슨트 예약</a>
|
||||
<a href="#reservation-section" class="btn">도슨트 세션</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -209,7 +208,7 @@
|
|||
|
||||
<!-- Sensory Wheel & Flavor Science -->
|
||||
<section class="wrap" style="padding-bottom: var(--space-8);">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div class="frame">
|
||||
<img
|
||||
src="../assets/aroma-wheel.webp"
|
||||
|
|
@ -266,9 +265,9 @@
|
|||
<table class="lineup-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>순서</th>
|
||||
<th>라인업 구성</th>
|
||||
<th>ABV</th>
|
||||
<th scope="col">순서</th>
|
||||
<th scope="col">라인업 구성</th>
|
||||
<th scope="col">ABV</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -313,9 +312,9 @@
|
|||
<table class="lineup-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>순서</th>
|
||||
<th>라인업 구성</th>
|
||||
<th>ABV</th>
|
||||
<th scope="col">순서</th>
|
||||
<th scope="col">라인업 구성</th>
|
||||
<th scope="col">ABV</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -368,7 +367,7 @@
|
|||
|
||||
<!-- Gastronomy Pairing Atelier -->
|
||||
<section class="wrap" style="padding-bottom: var(--space-8);">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
|
||||
<div class="eyebrow">미식 페어링</div>
|
||||
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
|
||||
|
|
@ -393,14 +392,15 @@
|
|||
<!-- Live Reservation Form Section -->
|
||||
<section class="wrap" style="padding-bottom: var(--space-8);" id="reservation-section">
|
||||
<div class="card" style="max-width: 720px; margin: 0 auto;">
|
||||
<div class="eyebrow" style="color: var(--accent); margin-bottom: 4px;">예약 신청</div>
|
||||
<div class="eyebrow" style="color: var(--accent); margin-bottom: 4px;">세션 항목</div>
|
||||
<h2 style="font-size: var(--step-2); font-weight: 800; margin-bottom: var(--space-4);">
|
||||
도슨트 테이스팅 세션 실시간 예약
|
||||
도슨트 테이스팅 세션 선택
|
||||
</h2>
|
||||
<form onsubmit="event.preventDefault(); alert('도슨트 예약 신청이 완료되었습니다! 24시간 이내에 전담 마스터가 유선 안내를 드립니다.');" style="display: flex; flex-direction: column; gap: var(--space-4);">
|
||||
<p class="demo-notice" role="note">안내: 아래 예약 항목과 가격은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 예약으로 이어지지 않습니다.</p>
|
||||
<form onsubmit="event.preventDefault(); alert('선택한 도슨트 세션을 확인했습니다. 실제 예약은 접수되지 않았으며 입력 정보는 저장하거나 전송하지 않습니다.');" style="display: flex; flex-direction: column; gap: var(--space-4);">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="courseSelect">선택 코스</label>
|
||||
<select class="form-input" id="courseSelect" required>
|
||||
<select class="form-input" id="courseSelect" name="courseSelect" required>
|
||||
<option value="courseB">코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (120,000원)</option>
|
||||
<option value="courseA">코스 A. 독립병입자 입문 탐색 플라이트 (65,000원)</option>
|
||||
</select>
|
||||
|
|
@ -409,11 +409,11 @@
|
|||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="resDate">예약 일자</label>
|
||||
<input type="date" class="form-input" id="resDate" value="2026-08-28" required />
|
||||
<input type="date" class="form-input" id="resDate" name="reservationDate" autocomplete="off" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="resTime">예약 시간대</label>
|
||||
<select class="form-input" id="resTime" required>
|
||||
<select class="form-input" id="resTime" name="reservationTime" required>
|
||||
<option>19:00 (1부)</option>
|
||||
<option selected>20:30 (2부)</option>
|
||||
<option>22:00 (3부 - 심야 세션)</option>
|
||||
|
|
@ -424,21 +424,21 @@
|
|||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3);">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="userName">예약자 성함</label>
|
||||
<input type="text" class="form-input" id="userName" value="정재훈" required />
|
||||
<input type="text" class="form-input" id="userName" name="userName" autocomplete="name" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="userPhone">연락처</label>
|
||||
<input type="tel" class="form-input" id="userPhone" value="010-8831-2094" required />
|
||||
<input type="tel" class="form-input" id="userPhone" name="userPhone" autocomplete="tel" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="flavorNote">평소 선호하시는 위스키 취향 (선택)</label>
|
||||
<input type="text" class="form-input" id="flavorNote" placeholder="예: 아일라 피트 위스키, 셰리 버트 숙성 선호" value="셰리 버트 숙성 & 80ppm 이상 헤비 피트 선호" />
|
||||
<input type="text" class="form-input" id="flavorNote" name="flavorNote" autocomplete="off" placeholder="예: 아일라 피트 위스키, 셰리 버트 숙성 선호" />
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary" style="margin-top: var(--space-2); padding: 14px; font-size: var(--step-0);">
|
||||
위스키 도슨트 세션 예약 확정 →
|
||||
선택한 세션 내용 확인 →
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -448,9 +448,8 @@
|
|||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>캐스크 아카이브 · 서울 용산구 한남대로 27길 19 지하 1층</p>
|
||||
<p class="muted">매일 18:00 – 02:00 (사전 예약제)</p>
|
||||
<p class="muted">bottling@caskarchive.kr</p>
|
||||
<p>캐스크 아카이브 · 독립병입 위스키 안내</p>
|
||||
<p class="muted">이 페이지의 코스·가격·신청 흐름은 예시이며 입력 정보는 저장·전송되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,23 +3,20 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>캐스크 아카이브 (CASK ARCHIVE) · 독립병입자 위스키바 & 도슨트 큐레이션</title>
|
||||
<title>캐스크 아카이브 (CASK ARCHIVE) · 독립병입 위스키 안내</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Gordon & MacPhail, Signatory, SMWS 등 독립병입자(IB) 희귀 캐스크 공식 수입 인증 위스키바. 캐스크 넘버 원장과 1:1 위스키 도슨트 테이스팅 코스 예약."
|
||||
content="독립병입 위스키의 숙성 정보와 향미를 비교해 보는 안내입니다. 원장·가격·세션 정보는 예시이며 실제 예약을 받지 않습니다."
|
||||
/>
|
||||
<meta property="og:title" content="캐스크 아카이브 (CASK ARCHIVE) · 독립병입자 위스키바" />
|
||||
<meta property="og:description" content="시간과 배럴이 빚어낸 오직 단 하나의 원액. 공식 수입 인증 독립병입자 전문 아카이브." />
|
||||
<meta property="og:title" content="캐스크 아카이브 (CASK ARCHIVE) · 독립병입 위스키 안내" />
|
||||
<meta property="og:description" content="독립병입 위스키의 숙성 정보와 향미를 비교해 보는 안내입니다." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="theme-color" content="#0b0b0d" />
|
||||
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/fonts.css?v=20260912all" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
|
|
@ -28,7 +25,7 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=4" />
|
||||
<link rel="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 width='32' height='32' rx='8' fill='%230B0B0D'/%3E%3Ccircle cx='16' cy='16' r='10' stroke='%23D99B38' stroke-width='2' fill='none'/%3E%3Cpath d='M16 8v16M8 16h16' stroke='%23D99B38' stroke-width='1.5'/%3E%3C/svg%3E"
|
||||
|
|
@ -55,7 +52,7 @@
|
|||
|
||||
<div class="head-actions">
|
||||
<a href="#docent" class="btn">
|
||||
도슨트 예약
|
||||
도슨트 세션
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -67,29 +64,29 @@
|
|||
<div class="wrap hero-grid">
|
||||
<div class="hero-content">
|
||||
<div class="eyebrow-amber">
|
||||
<span>OFFICIALLY CERTIFIED INDEPENDENT BOTTLER BAR</span>
|
||||
<span>INDEPENDENT BOTTLER TASTING ARCHIVE</span>
|
||||
</div>
|
||||
<h1 id="heroHeading" class="hero-title">
|
||||
시간과 배럴이 빚어낸<br />
|
||||
오직 단 하나의 <span class="gold">원액(Single Cask)</span>
|
||||
시간과 배럴이 만든<br />
|
||||
단 하나의 <span class="gold">캐스크 원액</span>
|
||||
</h1>
|
||||
<p class="hero-desc">
|
||||
증류소의 기본 공식 라인업에서는 맛볼 수 없는 독립병입자(Independent Bottler)의 희귀 원액들. 공식 정식 수입 필증과 단일 캐스크 일련번호(Single Cask Number)를 보증하며, 전담 도슨트가 취향에 맞춘 위스키를 1:1로 큐레이션합니다.
|
||||
독립병입 위스키의 숙성 정보와 향미를 비교하며, 취향에 맞는 테이스팅 선택 방식을 살펴볼 수 있습니다.
|
||||
</p>
|
||||
<div class="hero-ctas">
|
||||
<a href="#ledger" class="btn-amber">
|
||||
실시간 캐스크 원장 확인
|
||||
캐스크 목록 살펴보기
|
||||
<span aria-hidden="true">↗</span>
|
||||
</a>
|
||||
<a href="./docent/index.html" class="btn-outline">
|
||||
1:1 도슨트 마스터클래스 상세 보기
|
||||
도슨트 세션 상세 보기
|
||||
</a>
|
||||
</div>
|
||||
<p class="demo-notice" role="note">안내: 이 페이지의 원장·인증 정보·가격·예약·구독은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 예약 또는 구독으로 이어지지 않습니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual" style="position: relative;">
|
||||
<div class="bezel-outer">
|
||||
<div class="hero-frame">
|
||||
<div class="hero-visual">
|
||||
<figure class="hero-frame">
|
||||
<img
|
||||
src="./assets/whisky-hero.webp"
|
||||
alt="차링된 오크 배럴 상판 위 싱글 캐스크 위스키 보틀과 테이스팅 글라스"
|
||||
|
|
@ -97,44 +94,44 @@
|
|||
height="1600"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-cask-tag">
|
||||
<figcaption class="hero-cask-tag">
|
||||
<div class="cask-tag-no">CASK #8821 · 58.4% ABV</div>
|
||||
<div class="cask-tag-sub">Signatory Vintage 1997 · Bottle 42 of 186</div>
|
||||
</div>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 공식 독립병입자 수입 인증 스트립 -->
|
||||
<section class="ib-cert-strip" aria-label="독립병입자 공식 인증">
|
||||
<section class="ib-cert-strip" aria-label="독립병입 위스키 정보 예시">
|
||||
<div class="wrap cert-grid">
|
||||
<div class="cert-item">
|
||||
<div class="cert-name">GORDON & MACPHAIL</div>
|
||||
<div class="cert-desc">1895년 설립 · 스코틀랜드 최고 권위 정식 수입</div>
|
||||
<div class="cert-desc">병입자 표기를 원장에 연결하는 예시</div>
|
||||
</div>
|
||||
<div class="cert-item">
|
||||
<div class="cert-name">SIGNATORY VINTAGE</div>
|
||||
<div class="cert-desc">언칠필터드 / 내추럴 컬러 캐스크 스트렝스 보증</div>
|
||||
<div class="cert-desc">숙성 조건과 도수 메타데이터 예시</div>
|
||||
</div>
|
||||
<div class="cert-item">
|
||||
<div class="cert-name">THE SCOTCH MALT WHISKY SOCIETY</div>
|
||||
<div class="cert-desc">SMWS 공식 파트너 베뉴 · 넘버링 큐레이션</div>
|
||||
<div class="cert-desc">캐스크 번호와 향미 기준의 연결 예시</div>
|
||||
</div>
|
||||
<div class="cert-item">
|
||||
<div class="cert-name">CADENHEAD'S</div>
|
||||
<div class="cert-desc">1842년 설립, 스코틀랜드에서 가장 오래된 독립병입자 라인업</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrap"><p class="demo-notice" role="note">아래 병입자·수입 정보는 예시입니다. 실제 인증이나 입고 현황을 뜻하지 않습니다.</p></div>
|
||||
</section>
|
||||
|
||||
<!-- 캐스크 원장 (Interactive Cask Ledger) -->
|
||||
<section class="ledger-section" id="ledger" aria-labelledby="ledgerHeading">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 id="ledgerHeading" class="section-title">독립병입자 캐스크 실시간 보유 원장</h2>
|
||||
<p class="section-sub">모든 병은 단일 캐스크(Single Cask)에서 추출되어 병입 수량이 한정되어 있습니다. 필터로 원하는 향미 스펙을 검색하십시오.</p>
|
||||
<h2 id="ledgerHeading" class="section-title">독립병입 캐스크 목록</h2>
|
||||
<p class="section-sub">아래 병입 정보·수량·인증 표시는 예시입니다. 향미 기준으로 원하는 캐스크를 비교해 보세요.</p>
|
||||
</div>
|
||||
|
||||
<div class="ledger-filter-bar">
|
||||
|
|
@ -149,7 +146,7 @@
|
|||
type="search"
|
||||
id="caskSearch"
|
||||
class="search-input"
|
||||
placeholder="증류소명 or 캐스크 번호 검색..."
|
||||
placeholder="증류소명 또는 캐스크 번호 검색"
|
||||
aria-label="캐스크 검색"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -158,14 +155,14 @@
|
|||
<table class="ledger-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>캐스크 번호</th>
|
||||
<th>증류소 & 원액 명칭</th>
|
||||
<th>독립병입자 (Bottler)</th>
|
||||
<th>숙성 배럴 / 년수</th>
|
||||
<th>도수 (ABV)</th>
|
||||
<th>한정 수량</th>
|
||||
<th>향미 노트 요약</th>
|
||||
<th>인증 정보</th>
|
||||
<th scope="col">캐스크 번호</th>
|
||||
<th scope="col">증류소 & 원액 명칭</th>
|
||||
<th scope="col">독립병입자 (Bottler)</th>
|
||||
<th scope="col">숙성 배럴 / 년수</th>
|
||||
<th scope="col">도수 (ABV)</th>
|
||||
<th scope="col">한정 수량</th>
|
||||
<th scope="col">향미 노트 요약</th>
|
||||
<th scope="col">인증 정보</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="caskTableBody">
|
||||
|
|
@ -177,7 +174,7 @@
|
|||
<td class="mono" style="color:var(--accent);font-weight:700;">54.2%</td>
|
||||
<td class="mono">180 Btls</td>
|
||||
<td>말린 무화과, 밀랍, 시나몬, 다크초콜릿</td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Clynelish 1996 #6512', 'Signatory Vintage', '24 Years (1996 Distilled / 2020 Bottled)', '54.2% Cask Strength', '식약처 정식통관 제2024-IB-0982호');">수입인증서</button></td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Clynelish 1996 #6512', 'Signatory Vintage', '24 Years (1996 Distilled / 2020 Bottled)', '54.2% Cask Strength', '식약처 정식통관 제2024-IB-0982호');">병입 정보 보기</button></td>
|
||||
</tr>
|
||||
<tr data-cat="peat cs">
|
||||
<td class="mono cask-badge">#29.281</td>
|
||||
|
|
@ -187,7 +184,7 @@
|
|||
<td class="mono" style="color:var(--accent);font-weight:700;">57.8%</td>
|
||||
<td class="mono">214 Btls</td>
|
||||
<td>모닥불 연기, 바다 소금, 레몬 제스트, 피트 45ppm</td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Laphroaig 2005 (SMWS 29.281)', 'The SMWS Partner', '15 Years (2005 Distilled)', '57.8% Cask Strength', '식약처 정식통관 제2025-IB-1102호');">수입인증서</button></td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Laphroaig 2005 (SMWS 29.281)', 'The SMWS Partner', '15 Years (2005 Distilled)', '57.8% Cask Strength', '식약처 정식통관 제2025-IB-1102호');">병입 정보 보기</button></td>
|
||||
</tr>
|
||||
<tr data-cat="sherry">
|
||||
<td class="mono cask-badge">#93/41</td>
|
||||
|
|
@ -197,7 +194,7 @@
|
|||
<td class="mono" style="color:var(--accent);font-weight:700;">51.6%</td>
|
||||
<td class="mono">142 Btls</td>
|
||||
<td>열대 과일 잼, 가죽, 부드러운 유황, 정향</td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Springbank 1993 #93/41', 'Gordon & MacPhail Reserve', '27 Years (1993 Distilled)', '51.6%', '식약처 정식통관 제2024-IB-0419호');">수입인증서</button></td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Springbank 1993 #93/41', 'Gordon & MacPhail Reserve', '27 Years (1993 Distilled)', '51.6%', '식약처 정식통관 제2024-IB-0419호');">병입 정보 보기</button></td>
|
||||
</tr>
|
||||
<tr data-cat="peat cs">
|
||||
<td class="mono cask-badge">#PX-88</td>
|
||||
|
|
@ -207,7 +204,7 @@
|
|||
<td class="mono" style="color:var(--accent);font-weight:700;">56.4%</td>
|
||||
<td class="mono">260 Btls</td>
|
||||
<td>스모크 베이컨, 건포도, 블랙베리, 훈제 파프리카</td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Caol Ila 2008 #PX-88', 'Cadenheads Authentic Collection', '14 Years', '56.4% Cask Strength', '식약처 정식통관 제2025-IB-0831호');">수입인증서</button></td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Caol Ila 2008 #PX-88', 'Cadenheads Authentic Collection', '14 Years', '56.4% Cask Strength', '식약처 정식통관 제2025-IB-0831호');">병입 정보 보기</button></td>
|
||||
</tr>
|
||||
<tr data-cat="bourbon cs">
|
||||
<td class="mono cask-badge">#98/12</td>
|
||||
|
|
@ -217,7 +214,7 @@
|
|||
<td class="mono" style="color:var(--accent);font-weight:700;">58.1%</td>
|
||||
<td class="mono">195 Btls</td>
|
||||
<td>바닐라 커스터드, 구운 코코넛, 청사과, 후추</td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Ben Nevis 1998 #98/12', 'Adelphi Selection', '22 Years', '58.1%', '식약처 정식통관 제2024-IB-0771호');">수입인증서</button></td>
|
||||
<td><button class="btn-outline" style="padding:4px 10px;font-size:11px;" onclick="openCertModal('Ben Nevis 1998 #98/12', 'Adelphi Selection', '22 Years', '58.1%', '식약처 정식통관 제2024-IB-0771호');">병입 정보 보기</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -262,12 +259,12 @@
|
|||
<!-- 예약 입력 폼 -->
|
||||
<div class="docent-booking-form">
|
||||
<h3 style="font-family: var(--font-serif); font-size: var(--step-1); margin-bottom: var(--space-4); color: var(--ink);">
|
||||
도슨트 프라이빗 세션 예약 신청
|
||||
도슨트 프라이빗 세션 항목
|
||||
</h3>
|
||||
<form id="docentForm" onsubmit="return false;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="bookingCourse">선택 코스</label>
|
||||
<select id="bookingCourse" class="form-select">
|
||||
<select id="bookingCourse" name="bookingCourse" class="form-select">
|
||||
<option value="A">코스 A. 독립병입자 입문 탐색 플라이트 (65,000원)</option>
|
||||
<option value="B" selected>코스 B. 캐스크 스트렝스 & 빈티지 마스터 플라이트 (120,000원)</option>
|
||||
</select>
|
||||
|
|
@ -276,11 +273,11 @@
|
|||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="bookingDate">예약 일자</label>
|
||||
<input type="date" id="bookingDate" class="form-input" value="2026-08-28" />
|
||||
<input type="date" id="bookingDate" name="bookingDate" autocomplete="off" class="form-input" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="bookingTime">예약 시간대</label>
|
||||
<select id="bookingTime" class="form-select">
|
||||
<select id="bookingTime" name="bookingTime" class="form-select">
|
||||
<option>19:00 (1부)</option>
|
||||
<option selected>20:30 (2부)</option>
|
||||
<option>22:00 (3부 심야)</option>
|
||||
|
|
@ -291,21 +288,21 @@
|
|||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="guestName">예약자 성함</label>
|
||||
<input type="text" id="guestName" class="form-input" placeholder="홍길동" value="정재훈" required />
|
||||
<input type="text" id="guestName" name="guestName" autocomplete="name" class="form-input" placeholder="성함" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="guestPhone">연락처</label>
|
||||
<input type="tel" id="guestPhone" class="form-input" placeholder="010-0000-0000" value="010-8831-2094" required />
|
||||
<input type="tel" id="guestPhone" name="guestPhone" autocomplete="tel" class="form-input" placeholder="연락처" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="guestPref">향미 선호도 메모</label>
|
||||
<input type="text" id="guestPref" class="form-input" placeholder="예: 셰리 밤 선호, 스모키 피트 선호 등" value="강렬한 셰리 버트와 헤비 피트 선호" />
|
||||
<input type="text" id="guestPref" name="guestPreference" autocomplete="off" class="form-input" placeholder="예: 셰리 밤 선호, 스모키 피트 선호 등" />
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-amber" id="btnBookDocent" style="width: 100%; padding: 14px; margin-top: 10px;">
|
||||
위스키 도슨트 세션 예약 확정
|
||||
선택한 세션 내용 확인
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -360,25 +357,28 @@
|
|||
신규 캐스크 입고 알림 & 인스타그램
|
||||
</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); max-width: 500px; margin-inline: auto;">
|
||||
수량이 극소량인 독립병입자 캐스크 오프닝 소식을 이메일 뉴스레터로 가장 먼저 발송해 드립니다.
|
||||
신규 캐스크와 테이스팅 콘텐츠를 둘러보세요.
|
||||
</p>
|
||||
<p class="demo-notice" role="note">이메일은 이 화면에 저장되지 않으며 실제 구독으로 등록되지 않습니다.</p>
|
||||
|
||||
<form class="newsletter-form" onsubmit="return false;">
|
||||
<input
|
||||
type="email"
|
||||
id="newsEmail"
|
||||
name="newsletterEmail"
|
||||
autocomplete="email"
|
||||
class="form-input"
|
||||
placeholder="귀하의 이메일 주소를 입력하세요"
|
||||
required
|
||||
/>
|
||||
<button type="button" class="btn-amber" id="btnSubscribe">
|
||||
구독 신청
|
||||
입력 내용 확인
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div style="margin-top: var(--space-5); display: inline-flex; align-items: center; gap: 8px;">
|
||||
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" class="btn-outline" style="font-size: 12px; padding: 6px 16px;">
|
||||
📸 인스타그램 @caskarchive.official 바로가기 ↗
|
||||
📸 인스타그램 살펴보기 ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -390,7 +390,7 @@
|
|||
<dialog id="certModal" class="cert-dialog" aria-labelledby="certTitle">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; border-bottom: 1px solid var(--line); padding-bottom: 0.75rem;">
|
||||
<h3 id="certTitle" style="font-family: var(--font-serif); font-size: var(--step-1); color: var(--accent);">
|
||||
공식 독립병입자 수입 보증서
|
||||
병입 정보 예시
|
||||
</h3>
|
||||
<button type="button" onclick="document.getElementById('certModal').close();" aria-label="닫기" style="font-size: 18px; font-weight: 700; color: var(--ink-muted);">✕</button>
|
||||
</div>
|
||||
|
|
@ -409,9 +409,8 @@
|
|||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>캐스크 아카이브 · 서울 용산구 한남대로 27길 19 지하 1층</p>
|
||||
<p class="muted">매일 18:00 – 02:00 (사전 예약제)</p>
|
||||
<p class="muted">bottling@caskarchive.kr</p>
|
||||
<p>캐스크 아카이브 · 독립병입 위스키 안내</p>
|
||||
<p class="muted">이 페이지의 원장·가격·신청 흐름은 예시이며 입력 정보는 저장·전송되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -458,7 +457,7 @@
|
|||
const date = document.getElementById("bookingDate").value;
|
||||
const time = document.getElementById("bookingTime").value;
|
||||
const course = document.getElementById("bookingCourse").value;
|
||||
alert(`[캐스크 아카이브] ${name}님의 1:1 위스키 도슨트 세션(코스 ${course})이 예약 접수되었습니다.\n일정: ${date} ${time}\n담당 도슨트가 당일 사전 안내 연락을 드립니다.`);
|
||||
alert(`${name}님이 선택한 도슨트 세션(코스 ${course})을 확인했습니다.\n실제 예약은 접수되지 않았으며 입력 정보는 저장하거나 전송하지 않습니다.`);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -471,7 +470,7 @@
|
|||
alert("유효한 이메일 주소를 입력해 주십시오.");
|
||||
return;
|
||||
}
|
||||
alert(`[캐스크 아카이브] ${email} 주소로 신규 독립병입자 캐스크 릴리즈 알림이 등록되었습니다.`);
|
||||
alert(`${email} 주소는 이 화면에 저장되지 않으며 실제 구독으로 등록되지 않습니다.`);
|
||||
document.getElementById("newsEmail").value = "";
|
||||
});
|
||||
}
|
||||
|
|
|
|||
15
apps/site/public/work/cask-archive/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import "./tokens.css";
|
||||
@import "./tokens.css?v=20260912all";
|
||||
|
||||
/* ==========================================================================
|
||||
캐스크 아카이브 (CASK ARCHIVE) — 메인 스타일시트 SSOT
|
||||
|
|
@ -266,7 +266,7 @@ button {
|
|||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.95fr);
|
||||
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||
gap: var(--space-7);
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -304,15 +304,8 @@ button {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.bezel-outer {
|
||||
background: rgba(217, 155, 56, 0.06);
|
||||
border: 1px solid var(--line);
|
||||
padding: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.hero-frame {
|
||||
border-radius: calc(var(--radius-md) - 4px);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 20px 48px -12px rgba(0, 0, 0, 0.7);
|
||||
|
|
@ -321,17 +314,15 @@ button {
|
|||
|
||||
.hero-frame img {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
aspect-ratio: 4/5;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-cask-tag {
|
||||
margin-top: var(--space-2);
|
||||
padding: 6px 12px;
|
||||
background: var(--surface-raised);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-3) 0 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
|
|
@ -390,9 +381,9 @@ button {
|
|||
}
|
||||
|
||||
.section-head {
|
||||
text-align: center;
|
||||
max-width: 680px;
|
||||
margin-inline: auto;
|
||||
text-align: left;
|
||||
max-width: 48rem;
|
||||
margin-inline: 0;
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
|
|
@ -439,7 +430,7 @@ button {
|
|||
color: var(--ink-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
transition: all var(--dur-quick);
|
||||
transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick);
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
|
|
@ -726,11 +717,14 @@ button {
|
|||
inset: 0;
|
||||
margin: auto;
|
||||
width: min(90vw, 480px);
|
||||
max-height: calc(100dvh - (var(--space-4) * 2));
|
||||
background: #141418;
|
||||
color: #f3f3f5;
|
||||
border: 1px solid rgba(217, 155, 56, 0.4);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: var(--space-5);
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.8);
|
||||
outline: none;
|
||||
}
|
||||
|
|
@ -784,3 +778,60 @@ button {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
html:has(dialog[open]) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 예약·원장 표면의 조작 여백과 키보드 상태를 토큰으로 맞춘다. */
|
||||
:where(a, button, input, select, textarea):focus-visible {
|
||||
outline: 3px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.btn-amber,
|
||||
.btn-outline,
|
||||
.head-actions .btn,
|
||||
.filter-btn,
|
||||
.search-input,
|
||||
.form-input,
|
||||
.form-select,
|
||||
.newsletter-form input {
|
||||
min-height: var(--control-block);
|
||||
padding-inline: var(--control-pad-inline);
|
||||
}
|
||||
|
||||
.ledger-table th,
|
||||
.ledger-table td {
|
||||
padding-inline: var(--control-pad-inline);
|
||||
}
|
||||
|
||||
.demo-notice {
|
||||
max-width: 58ch;
|
||||
margin-top: var(--space-4);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-left: 3px solid var(--accent);
|
||||
background: var(--accent-light);
|
||||
color: var(--ink-muted);
|
||||
font-size: var(--step--1);
|
||||
line-height: 1.55;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.head-actions .btn { display: none; }
|
||||
.hero-title { font-size: clamp(1.65rem, 7.8vw, 2.15rem); }
|
||||
.hero-ctas { align-items: stretch; }
|
||||
.hero-ctas > a { width: 100%; }
|
||||
.course-header { align-items: flex-start; flex-direction: column; gap: var(--space-2); }
|
||||
.docent-booking-form { padding: var(--space-5); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 1ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 1ms !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,8 +64,10 @@
|
|||
--space-8: clamp(4.5rem, 3rem + 6vw, 9rem);
|
||||
|
||||
/* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */
|
||||
--container: 1120px;
|
||||
--container: clamp(70rem, 62vw, 85rem);
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
|
||||
--control-block: 2.75rem;
|
||||
--control-pad-inline: 0.875rem;
|
||||
|
||||
/* 모션 */
|
||||
--dur-quick: 180ms;
|
||||
|
|
|
|||
169
apps/site/public/work/cheongon-care/assets/fonts/LICENSE.html
Normal file
13
apps/site/public/work/cheongon-care/assets/fonts/SOURCES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
17
apps/site/public/work/cheongon-care/design.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 청온 케어 — 디자인 결정 기록
|
||||
|
||||
## 관찰과 방향
|
||||
|
||||
- 관찰: 기존 첫 화면은 카드 안의 사진과 떠 있는 수치 배지가 서비스 범위보다 먼저 읽혔다.
|
||||
- 원리 적용 범위: 소개와 견적 행동은 왼쪽 읽기 폭에, 생활 공간 사진과 32개 검수 항목 예시는 오른쪽 한 장면에 둔다. 견적은 입력 작업표와 합계 명세를 분리한다.
|
||||
- 결정: 이중 프레임·떠 있는 배지를 제거하고 사진 캡션으로 검수 항목의 맥락을 연결했다. 성능 수치·인증·의학적 표현은 근거가 없으므로 작업표에 기록할 확인 절차로 바꿨다.
|
||||
- 기각 조건: 사진이 견적 행동을 밀거나 작은 폭에서 입력과 합계가 동시에 읽히지 않으면 1열 순서와 간격을 다시 조정한다.
|
||||
|
||||
## 토큰과 검증
|
||||
|
||||
- 타입: `MaruBuri` 로컬 400·600은 제목 역할, Pretendard는 본문·입력 역할이다. 본문은 `--step-0`, 보조 기록은 `--step--1`을 유지한다.
|
||||
- 색: 기존 청록 `--accent`, 밝은 표면과 선 토큰을 유지한다. 청록은 상태·행동과 검수 표시에만 쓴다.
|
||||
- 구도: 넓은 화면은 5:7 소개/사진, 견적은 입력/합계 두 열이며 860px 이하에서는 한 열이다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>청온 케어 · 프리미엄 주거·오피스 전문 위생 케어 & 관제 콘솔</title>
|
||||
<title>청온 케어 · 프리미엄 주거·오피스 위생 케어 안내</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="임상 수준의 6단계 위생 프로토콜과 100% 정찰제 견적. 독일 카처 4.0bar 고온스팀과 에코서트 인증 천연 세제. 실시간 견적 신청 및 관제 콘솔 지원."
|
||||
content="위생 케어 절차, 예상 견적, 운영 정보의 표시 방식을 소개합니다. 가격과 접수 정보는 예시이며 실제 신청은 진행되지 않습니다."
|
||||
/>
|
||||
<meta property="og:title" content="청온 케어 · 프리미엄 위생 케어" />
|
||||
<meta property="og:description" content="공간의 숨결을 되돌리는 6단계 프로토콜과 투명한 정찰제 견적." />
|
||||
|
|
@ -24,7 +24,8 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=1" />
|
||||
<link rel="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 width='32' height='32' rx='8' fill='%230D9488'/%3E%3Cpath d='M9 16.5l5 5 9-10' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E"
|
||||
|
|
@ -56,7 +57,7 @@
|
|||
</button>
|
||||
<button type="button" class="mode-btn" id="btnModeAdmin" title="단축키: Alt + A">
|
||||
<span class="badge-dot" aria-hidden="true"></span>
|
||||
관리자 콘솔
|
||||
운영 현황
|
||||
</button>
|
||||
</div>
|
||||
<a href="#calculator" class="btn client-only-btn">
|
||||
|
|
@ -76,43 +77,38 @@
|
|||
<div class="wrap hero-grid">
|
||||
<div class="hero-content">
|
||||
<div class="eyebrow">
|
||||
<span>임상 수준 위생 프로토콜</span>
|
||||
<span>현장 검수 절차 예시</span>
|
||||
</div>
|
||||
<h1 id="heroHeading" class="hero-title">
|
||||
보이지 않는 미세 오염까지,<br />
|
||||
<em>공간의 숨결</em>을 되돌립니다
|
||||
보이지 않는 오염까지,<br />
|
||||
<em>공간을 새롭게</em> 돌봅니다
|
||||
</h1>
|
||||
<p class="hero-desc">
|
||||
단순한 미화가 아닙니다. 독일 카처 4.0bar 고온 스팀과 에코서트(ECOCERT) 인증 친환경 세제로 새집증후군, 미세 분진, 알레르기 유발 물질을 근본적으로 제거합니다.
|
||||
공간 유형과 선택 항목을 바탕으로 케어 범위와 예상 금액을 살펴볼 수 있습니다.
|
||||
</p>
|
||||
<div class="hero-ctas">
|
||||
<a href="#calculator" class="btn-primary">
|
||||
실시간 정찰제 견적 계산하기
|
||||
항목별 예상 견적 보기
|
||||
<span class="btn-icon-circle" aria-hidden="true">↗</span>
|
||||
</a>
|
||||
<div class="hero-trust">
|
||||
<span class="hero-trust-stars">★★★★★</span>
|
||||
<span>누적 케어 4,820세대 · 만족도 99.4%</span>
|
||||
<span>선택 항목별 예상 금액을 확인하세요</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="demo-notice" role="note">안내: 이 페이지의 서비스·가격·운영 현황은 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 견적이나 신청으로 이어지지 않습니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual">
|
||||
<div class="bezel-outer">
|
||||
<div class="hero-frame">
|
||||
<img
|
||||
src="./assets/clean-hero.webp"
|
||||
alt="정밀 입주 케어가 완료된 미니멀한 프리미엄 주거 거실 전경"
|
||||
width="1200"
|
||||
height="900"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-stat-floating">
|
||||
<span class="stat-val">32 Point</span>
|
||||
<span class="stat-label">마스터 클리너<br />정밀 검수 체크리스트</span>
|
||||
</div>
|
||||
<figure class="hero-frame">
|
||||
<img
|
||||
src="./assets/clean-hero.webp"
|
||||
alt="정돈된 주거 공간의 거실 전경"
|
||||
width="1200"
|
||||
height="900"
|
||||
loading="eager"
|
||||
/>
|
||||
<figcaption>주거 공간 케어 예시 · 현장에서는 32개 검수 항목을 확인하는 흐름을 표시합니다.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -121,8 +117,9 @@
|
|||
<section class="calc-section" id="calculator" aria-labelledby="calcHeading">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 id="calcHeading" class="section-title">투명한 정찰제 실시간 견적 산정</h2>
|
||||
<p class="section-sub">추가금 없는 명확한 기준. 공간 면적과 맞춤 옵션을 선택하시면 즉시 예상 금액이 산출됩니다.</p>
|
||||
<p class="eyebrow">견적 작업표</p>
|
||||
<h2 id="calcHeading" class="section-title">항목별 예상 견적</h2>
|
||||
<p class="section-sub">공간 면적과 선택 항목을 기준으로 예상 금액의 구성 방식을 확인합니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="calc-grid">
|
||||
|
|
@ -153,6 +150,7 @@
|
|||
<input
|
||||
type="range"
|
||||
id="pyeongSlider"
|
||||
name="spaceSize"
|
||||
class="range-input"
|
||||
min="12"
|
||||
max="75"
|
||||
|
|
@ -167,11 +165,11 @@
|
|||
<div class="addon-grid">
|
||||
<label class="addon-label">
|
||||
<input type="checkbox" name="addon" value="steam" data-price="45000" checked />
|
||||
<span>160℃ 고온 살균 스팀 (+45,000원)</span>
|
||||
<span>고온 스팀 작업 기록 (+45,000원)</span>
|
||||
</label>
|
||||
<label class="addon-label">
|
||||
<input type="checkbox" name="addon" value="phytoncide" data-price="35000" checked />
|
||||
<span>편백 피톤치드 훈증 (+35,000원)</span>
|
||||
<span>향·마감 작업 기록 (+35,000원)</span>
|
||||
</label>
|
||||
<label class="addon-label">
|
||||
<input type="checkbox" name="addon" value="aircon" data-price="80000" />
|
||||
|
|
@ -187,30 +185,36 @@
|
|||
<!-- 예약 정보 입력 -->
|
||||
<div class="form-group" style="margin-top: 1.5rem;">
|
||||
<span class="form-label">신청자 정보</span>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.75rem;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0.75rem;">
|
||||
<label class="sr-only" for="clientName">신청자 성함</label>
|
||||
<input
|
||||
type="text"
|
||||
id="clientName"
|
||||
name="clientName"
|
||||
autocomplete="name"
|
||||
placeholder="성함 / 고객명"
|
||||
style="width: 100%; min-width: 0; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
|
||||
value="이서연"
|
||||
required
|
||||
/>
|
||||
<label class="sr-only" for="clientPhone">신청자 연락처</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="clientPhone"
|
||||
name="clientPhone"
|
||||
autocomplete="tel"
|
||||
placeholder="연락처 (010-0000-0000)"
|
||||
style="width: 100%; min-width: 0; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
|
||||
value="010-4821-9930"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<label class="sr-only" for="clientAddress">케어 현장 주소</label>
|
||||
<input
|
||||
type="text"
|
||||
id="clientAddress"
|
||||
name="clientAddress"
|
||||
autocomplete="off"
|
||||
placeholder="케어 현장 주소 (예: 서울시 마포구 공덕동 120)"
|
||||
style="width: 100%; min-width: 0; margin-top: 0.5rem; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--step--1);"
|
||||
value="서울시 성동구 성수동2가 아크로포레스트 102동"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -219,7 +223,7 @@
|
|||
|
||||
<!-- 요약 및 접수 CTA -->
|
||||
<div class="calc-summary">
|
||||
<h3 class="summary-title">실시간 견적 명세서</h3>
|
||||
<h3 class="summary-title">예상 견적 명세</h3>
|
||||
<div class="summary-row">
|
||||
<span>선택 공간</span>
|
||||
<span id="sumSpaceType" class="mono">주거 입주/이사</span>
|
||||
|
|
@ -242,10 +246,10 @@
|
|||
</div>
|
||||
|
||||
<button type="button" class="calc-submit" id="btnSubmitQuote">
|
||||
이 견적으로 신청 접수하기
|
||||
예상 견적 보기
|
||||
</button>
|
||||
<p style="font-size: 12px; color: var(--ink-inverse-muted); margin-top: 10px; text-align: center;">
|
||||
✓ 접수 즉시 배차 팀장이 연락 후 사전 현장 조율을 진행합니다.
|
||||
<p style="font-size: 12px; color: var(--ink-muted); margin-top: 10px; text-align: center;">
|
||||
선택 항목에 따른 예상 금액을 확인할 수 있습니다. 실제 견적·신청은 진행되지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -257,11 +261,11 @@
|
|||
<div class="wrap">
|
||||
<div class="section-head" style="display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 12px;">
|
||||
<div>
|
||||
<h2 id="protoHeading" class="section-title">임상 수준의 6단계 청온 케어 프로토콜</h2>
|
||||
<p class="section-sub">표면만 닦는 청소가 아닌, 미세 분진과 세균 번식의 원인을 제거하는 체계적인 과정입니다.</p>
|
||||
<h2 id="protoHeading" class="section-title">6단계 현장 검수 절차 예시</h2>
|
||||
<p class="section-sub">작업 순서와 확인 항목을 기록하는 방식의 예시입니다. 결과나 성능을 보증하지 않습니다.</p>
|
||||
</div>
|
||||
<a href="./protocol/index.html" class="action-btn" style="text-decoration: none; padding: 10px 20px; font-size: 14px; background: var(--surface-card); color: var(--accent); border: 1px solid var(--accent);">
|
||||
나인원한남 ATP 측정 리포트 보기 →
|
||||
검수 절차 예시 보기 →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -269,37 +273,37 @@
|
|||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 01</div>
|
||||
<h3>건식 초미세 집진</h3>
|
||||
<p>H14 헤파 필터 흡진기로 벽면, 천장 몰딩, 바닥의 시공 미세분진 및 부유 입자를 99.97% 1차 포집합니다.</p>
|
||||
<p>벽면, 천장 몰딩, 바닥의 시공 잔여물을 확인하고 흡진 작업 여부를 기록합니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 02</div>
|
||||
<h3>160℃ 고온 스팀 살균</h3>
|
||||
<p>배수구, 욕실 타일 틈새, 주방 후드 기름때를 화학 세제 없이 4.0bar 고온 증기로 녹여 살균합니다.</p>
|
||||
<h3>스팀 장비 설정 확인</h3>
|
||||
<p>배수구, 욕실 타일 틈새, 주방 후드처럼 확인이 필요한 면과 장비 설정을 작업표에 기록합니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 03</div>
|
||||
<h3>약알칼리 친환경 분해</h3>
|
||||
<p>에코서트(ECOCERT) 인증 천연 식물성 세정제를 도포하여 자재 손상 없이 찌든 때를 안전하게 중화합니다.</p>
|
||||
<h3>표면별 세정 확인</h3>
|
||||
<p>표면 재질과 사용 제품 정보를 작업표에 기록하고, 적용 전후 상태를 함께 확인합니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 04</div>
|
||||
<h3>서랍·후드 전면 탈거 세척</h3>
|
||||
<p>보이지 않는 싱크대 걸레받이, 전등갓, 수납장 서랍 레일을 전면 탈거하여 내측 분진을 완벽 제거합니다.</p>
|
||||
<p>필요한 분리 작업의 범위와 접근 가능한 안쪽 면을 확인 항목으로 남깁니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 05</div>
|
||||
<h3>편백 피톤치드 연무 훈증</h3>
|
||||
<p>순수 제주 편백 오일 연무로 벽지 속 새집증후군 원인 물질(포름알데히드, VOCs)을 중화하고 탈취합니다.</p>
|
||||
<h3>환기와 마감 확인</h3>
|
||||
<p>환기와 마감 상태를 확인하고, 사용한 향·마감 제품이 있다면 작업표에 표시합니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-card">
|
||||
<div class="protocol-num">STEP 06</div>
|
||||
<h3>32개 항목 마스터 검수</h3>
|
||||
<p>현장 팀장과 고객이 함께 32개 체크리스트를 1:1 대조하고, 미흡 지점 발생 시 즉시 재케어 후 인도합니다.</p>
|
||||
<p>현장 담당자와 의뢰자가 검수 항목을 함께 확인하는 흐름을 예시로 표시합니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -309,27 +313,27 @@
|
|||
<section class="equipment-section" id="equipment" aria-labelledby="equipHeading">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 id="equipHeading" class="section-title">전문 위생 장비 및 검수 스펙</h2>
|
||||
<p class="section-sub">가정용 장비가 아닌 독일 카처 정품 산업용 위생 기기를 전 현장에 투입합니다.</p>
|
||||
<h2 id="equipHeading" class="section-title">작업 장비와 검수 기록</h2>
|
||||
<p class="section-sub">현장에서 확인할 장비·필터·제품·마감 항목을 작업표에 표시하는 예시입니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="equip-grid">
|
||||
<div class="equip-specs">
|
||||
<div class="spec-item">
|
||||
<span class="spec-key">고온 스팀 압력</span>
|
||||
<span class="spec-val">Kärcher SG 4/4 (4.0 bar · 160℃)</span>
|
||||
<span class="spec-key">장비 기록</span>
|
||||
<span class="spec-val">사용 장비와 설정값을 작업표에 표시</span>
|
||||
</div>
|
||||
<div class="spec-item">
|
||||
<span class="spec-key">미세먼지 집진 등급</span>
|
||||
<span class="spec-val">HEPA H14 (0.3㎛ 99.995% 포집)</span>
|
||||
<span class="spec-key">필터 확인</span>
|
||||
<span class="spec-val">필터 종류와 교체 상태를 현장에서 확인</span>
|
||||
</div>
|
||||
<div class="spec-item">
|
||||
<span class="spec-key">사용 세제 인증</span>
|
||||
<span class="spec-val">ECOCERT 100% 생분해 식물성</span>
|
||||
<span class="spec-key">사용 제품</span>
|
||||
<span class="spec-val">제품명과 적용 표면을 작업표에 기록</span>
|
||||
</div>
|
||||
<div class="spec-item">
|
||||
<span class="spec-key">포름알데히드 저감</span>
|
||||
<span class="spec-val">한국화학융합시험연구원 인증 94.2%</span>
|
||||
<span class="spec-key">마감 확인</span>
|
||||
<span class="spec-val">환기·표면 상태를 검수 항목으로 확인</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -341,8 +345,8 @@
|
|||
height="600"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="ba-badge before">정밀 장비 규격</span>
|
||||
<span class="ba-badge after">QC 완결 보증</span>
|
||||
<span class="ba-badge before">작업 장비 예시</span>
|
||||
<span class="ba-badge after">검수 항목 확인</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -350,16 +354,17 @@
|
|||
</div>
|
||||
|
||||
<!-- ===================================================================
|
||||
2. 관리자 관제 콘솔 모드 (Admin Dashboard Mockup)
|
||||
2. 운영 현황 보기
|
||||
=================================================================== -->
|
||||
<section class="admin-view wrap" aria-label="청온 케어 관리자 콘솔">
|
||||
<section class="admin-view wrap" aria-label="청온 케어 운영 현황">
|
||||
<div class="admin-header">
|
||||
<div>
|
||||
<div class="eyebrow" style="margin-bottom: 4px;">배차 관제 콘솔</div>
|
||||
<h2 style="font-size: var(--step-2); font-weight: 800;">실시간 배차 및 현장 관제 현황</h2>
|
||||
<div class="eyebrow" style="margin-bottom: 4px;">운영 현황</div>
|
||||
<h2 style="font-size: var(--step-2); font-weight: 800;">오늘의 케어 현황</h2>
|
||||
<p class="demo-notice" role="note">아래 접수·배차 정보는 운영 화면 예시입니다. 실제 고객 정보나 접수 내역이 아닙니다.</p>
|
||||
</div>
|
||||
<button type="button" class="btn-primary" id="btnOpenQcModal">
|
||||
32개 현장 검수표 열기
|
||||
32개 검수 항목 보기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -386,7 +391,7 @@
|
|||
<!-- 실시간 접수 및 배차 원장 테이블 -->
|
||||
<div class="admin-table-card">
|
||||
<div class="table-header">
|
||||
<span class="table-title">실시간 예약 및 케어 원장 (Live Ledger)</span>
|
||||
<span class="table-title">접수·배차 현황</span>
|
||||
<div class="table-filter-tabs">
|
||||
<button class="filter-tab is-active" data-filter="all">전체</button>
|
||||
<button class="filter-tab" data-filter="new">신규접수</button>
|
||||
|
|
@ -399,14 +404,14 @@
|
|||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>접수번호</th>
|
||||
<th>고객명 / 연락처</th>
|
||||
<th>공간 / 면적</th>
|
||||
<th>현장 주소</th>
|
||||
<th>견적액</th>
|
||||
<th>배정 팀장</th>
|
||||
<th>상태</th>
|
||||
<th>조치</th>
|
||||
<th scope="col">접수번호</th>
|
||||
<th scope="col">고객명 / 연락처</th>
|
||||
<th scope="col">공간 / 면적</th>
|
||||
<th scope="col">현장 주소</th>
|
||||
<th scope="col">견적액</th>
|
||||
<th scope="col">배정 팀장</th>
|
||||
<th scope="col">상태</th>
|
||||
<th scope="col">조치</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="adminTableBody">
|
||||
|
|
@ -418,7 +423,7 @@
|
|||
<td class="mono" style="font-weight:700;">432,000원</td>
|
||||
<td>김정온 마스터</td>
|
||||
<td><span class="status-pill new">신규 접수</span></td>
|
||||
<td><button class="action-btn" onclick="alert('배차 확인 알림톡이 고객님께 전송되었습니다.');">배차 확정</button></td>
|
||||
<td><button class="action-btn" onclick="alert('배차 상태를 확인했습니다. 이 화면의 접수 정보는 예시이며 실제 알림은 전송되지 않습니다.');">배차 상태 보기</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">#CO-2026-0818</td>
|
||||
|
|
@ -428,7 +433,7 @@
|
|||
<td class="mono" style="font-weight:700;">520,000원</td>
|
||||
<td>박준호 팀장 (2팀)</td>
|
||||
<td><span class="status-pill progress">스팀 케어 중</span></td>
|
||||
<td><button class="action-btn" onclick="document.getElementById('qcModal').showModal();">실시간 검수</button></td>
|
||||
<td><button class="action-btn" onclick="document.getElementById('qcModal').showModal();">검수 항목 보기</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">#CO-2026-0817</td>
|
||||
|
|
@ -438,7 +443,7 @@
|
|||
<td class="mono" style="font-weight:700;">841,000원</td>
|
||||
<td>최수진 닥터 (1팀)</td>
|
||||
<td><span class="status-pill done">검수 승인 완료</span></td>
|
||||
<td><button class="action-btn" onclick="alert('완료 검수 리포트가 PDF로 발행되었습니다.');">리포트 다운</button></td>
|
||||
<td><button class="action-btn" onclick="alert('검수 리포트 내용을 확인했습니다. 실제 발행은 진행되지 않습니다.');">리포트 보기</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -458,23 +463,22 @@
|
|||
<li class="qc-item"><input type="checkbox" checked /> <span>02. 창틀 상하좌우 틈새 시멘트 가루 및 찌든때 제거</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>03. 싱크대 하부 걸레받이 전면 탈거 및 분진 집진</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>04. 가스레인지/인덕션 상판 및 후드 필터 기름때 고온 스팀 분해</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>05. 욕실 벽타일 메지 및 배수구 트랩 고온 살균 세척</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>05. 욕실 벽타일 메지 및 배수구 트랩 스팀 작업 확인</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>06. 수납장 모든 서랍 완전 탈거 후 레일 틈새 분진 청소</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>07. 베란다 및 다용도실 바닥 타일 약알칼리 세정</span></li>
|
||||
<li class="qc-item"><input type="checkbox" /> <span>08. 전 실 편백 피톤치드 훈증 및 공기질 측정기 수치 확인</span></li>
|
||||
<li class="qc-item"><input type="checkbox" checked /> <span>07. 베란다 및 다용도실 바닥 타일 세정 작업 확인</span></li>
|
||||
<li class="qc-item"><input type="checkbox" /> <span>08. 전 실 환기와 마감 상태 확인</span></li>
|
||||
</ul>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 8px;">
|
||||
<button type="button" class="action-btn" onclick="document.getElementById('qcModal').close();">닫기</button>
|
||||
<button type="button" class="btn-primary" style="padding: 6px 14px; font-size: 13px;" onclick="alert('검수 승인이 완료되었습니다.'); document.getElementById('qcModal').close();">최종 검수 서명 승인</button>
|
||||
<button type="button" class="btn-primary" style="padding: 6px 14px; font-size: 13px;" onclick="alert('검수 항목을 확인했습니다. 이 화면에서 실제 승인 처리는 진행되지 않습니다.'); document.getElementById('qcModal').close();">검수 항목 확인</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>청온 케어 · 서울 마포구 마포대로 109 청온랩 4층</p>
|
||||
<p class="muted">연중무휴 08:00 – 20:00 (사전 예약제)</p>
|
||||
<p class="muted">care@cheongon.kr</p>
|
||||
<p>청온 케어 · 위생 케어 안내</p>
|
||||
<p class="muted">이 페이지의 가격·접수 정보는 예시이며 입력 정보는 저장·전송되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -569,9 +573,9 @@
|
|||
let submissionCount = 6;
|
||||
|
||||
btnSubmit.addEventListener("click", () => {
|
||||
const name = document.getElementById("clientName").value || "고객님";
|
||||
const phone = document.getElementById("clientPhone").value || "010-0000-0000";
|
||||
const address = document.getElementById("clientAddress").value || "서울시 현장 주소";
|
||||
const name = document.getElementById("clientName").value || "이름 미입력";
|
||||
const phone = document.getElementById("clientPhone").value || "입력 없음";
|
||||
const address = document.getElementById("clientAddress").value || "입력 없음";
|
||||
const pyeong = pyeongSlider.value;
|
||||
const totalStr = sumTotal.textContent;
|
||||
|
||||
|
|
@ -593,7 +597,7 @@
|
|||
`;
|
||||
tableBody.insertBefore(newRow, tableBody.firstChild);
|
||||
|
||||
alert(`[청온 케어] ${name}님의 견적(${totalStr}) 신청이 성공적으로 접수되었습니다.\n관리자 콘솔에 실시간 등록되었습니다.`);
|
||||
alert(`${name}님의 예상 견적은 ${totalStr}입니다.\n실제 견적·신청은 진행되지 않았으며 입력 정보는 저장하거나 전송하지 않습니다.`);
|
||||
});
|
||||
|
||||
// 모달 열기
|
||||
|
|
|
|||
|
|
@ -3,16 +3,13 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>임상 6단계 위생 프로토콜 & 한남 케이스 리포트 — 청온 케어</title>
|
||||
<meta name="description" content="청온 케어의 병원급 6단계 정밀 위생 프로토콜 및 ATP 생체 오염도 측정 검증 리포트. 나인원한남 86평형 준공 청소 케이스 스터디 및 32개 정밀 QC 체크리스트." />
|
||||
<title>6단계 위생 케어 절차 · 예시 리포트 — 청온 케어</title>
|
||||
<meta name="description" content="청온 케어의 현장 검수 절차와 기록 표시 방식을 소개합니다. 아래 사례와 값은 예시입니다." />
|
||||
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/fonts.css?v=20260912all" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
|
|
@ -21,8 +18,9 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/tokens.css" />
|
||||
<link rel="stylesheet" href="../styles/main.css" />
|
||||
<link rel="stylesheet" href="../styles/tokens.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 width='32' height='32' rx='8' fill='%230D9488'/%3E%3Cpath d='M9 16.5l5 5 9-10' stroke='%23ffffff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E" />
|
||||
<style>
|
||||
.breadcrumb-bar {
|
||||
padding-top: var(--space-4);
|
||||
|
|
@ -96,7 +94,7 @@
|
|||
font-weight: 800;
|
||||
}
|
||||
.atp-val.before {
|
||||
color: #ef4444;
|
||||
color: #b91c1c;
|
||||
}
|
||||
.atp-val.after {
|
||||
color: var(--accent);
|
||||
|
|
@ -127,6 +125,7 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main-content">본문으로 건너뛰기</a>
|
||||
<!-- Top Nav (GNB SSOT) -->
|
||||
<header class="site-head">
|
||||
<div class="wrap head-inner">
|
||||
|
|
@ -153,28 +152,29 @@
|
|||
<div class="breadcrumb-bar" style="margin-bottom: var(--space-4); display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); letter-spacing: 0.04em;">
|
||||
<a href="../index.html">CHEONG-ON CARE</a>
|
||||
<span class="sep">/</span>
|
||||
<span class="current" style="color: var(--ink); font-weight: 600;">CLINICAL HYGIENE PROTOCOL & CASE STUDY</span>
|
||||
<span class="current" style="color: var(--ink); font-weight: 600;">CARE CHECKLIST & CASE EXAMPLE</span>
|
||||
</div>
|
||||
|
||||
<div class="protocol-hero-head">
|
||||
<p class="hero-label">HOSPITAL-GRADE SANITATION STANDARDS</p>
|
||||
<h1 class="hero-title" style="margin-bottom: var(--space-2);">단순한 청소를 넘어<br /><span style="color: var(--accent);">병원급 6단계 임상 위생 프로토콜</span></h1>
|
||||
<p class="hero-label">CARE CHECKLIST EXAMPLE</p>
|
||||
<h1 class="hero-title" style="margin-bottom: var(--space-2);"><span style="color: var(--accent);">6단계 위생 케어 절차</span></h1>
|
||||
<p class="hero-lead" style="font-size: var(--step-0); color: var(--ink-muted); max-width: var(--measure); margin-bottom: var(--space-5);">
|
||||
독일 카처 산업용 스팀과 H14 헤파 필터 흡진, ATP 생체 오염도 계측을 통해 인체에 무해한 안심 공간을 과학적으로 보증합니다.
|
||||
현장 장비, 필터, 표면, 마감 상태를 작업표에 기록하는 절차와 확인 항목을 소개합니다.
|
||||
</p>
|
||||
<p class="demo-notice" role="note">안내: 아래 절차와 측정 수치는 예시입니다. 실제 시공 결과를 보장하거나 예약을 접수하지 않습니다.</p>
|
||||
|
||||
<dl class="protocol-specs" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6);">
|
||||
<div>
|
||||
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); letter-spacing: 0.02em; margin-bottom: 2px;">Steam Temp</dt>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">160℃ High-Temp</dd>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">현장 장비 설정 기록</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); letter-spacing: 0.02em; margin-bottom: 2px;">Filtration</dt>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">0.3㎛ HEPA H14 (99.995%)</dd>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">필터 종류·상태 확인</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); letter-spacing: 0.02em; margin-bottom: 2px;">Detergent</dt>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">ECOCERT 100% 식물성</dd>
|
||||
<dd class="spec-val" style="font-size: var(--step-0); font-weight: 800; color: var(--ink);">사용 제품·표면 기록</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="spec-label" style="font-family: var(--font-mono); font-size: 11px; color: var(--accent); letter-spacing: 0.02em; margin-bottom: 2px;">QC Points</dt>
|
||||
|
|
@ -186,49 +186,48 @@
|
|||
<figure class="hero-photo" style="border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--surface-card);">
|
||||
<img
|
||||
src="../assets/clean-hero.webp"
|
||||
alt="완벽하게 정돈되고 살균된 프리미엄 주거 인테리어 공간"
|
||||
alt="정돈된 주거 공간의 실내 전경"
|
||||
width="1600"
|
||||
height="900"
|
||||
style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
|
||||
/>
|
||||
<figcaption style="padding: 10px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); border-top: 1px solid var(--line); background: var(--surface-raised);">
|
||||
나인원한남 86평형 · 6단계 정밀 프로토콜 시공 완료
|
||||
주거 공간 · 6단계 절차 예시
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ATP Measurement Case Report -->
|
||||
<!-- Inspection Record Layout -->
|
||||
<section class="wrap" style="padding-bottom: var(--space-8);">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: var(--space-8); align-items: center;">
|
||||
<div class="frame">
|
||||
<img
|
||||
src="../assets/atp-tester.webp"
|
||||
alt="ATP 생체 오염도 측정기 계측 화면 (12 RLU)"
|
||||
alt="검수 기록에 사용하는 계측 장비 화면 예시"
|
||||
style="width: 100%; display: block;"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
|
||||
<div class="eyebrow">ATP 데이터 검증</div>
|
||||
<div class="eyebrow">검수 기록 표시 예시</div>
|
||||
<h2 style="font-size: var(--step-2); font-weight: 800; line-height: 1.25;">
|
||||
나인원한남 86평형<br />
|
||||
<span style="color: var(--accent);">ATP 생체 오염도 99.1% 저감 리포트</span>
|
||||
주거 공간 예시<br />
|
||||
<span style="color: var(--accent);">점검값 기록 방식</span>
|
||||
</h2>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); line-height: 1.7;">
|
||||
실제 한남동 나인원한남 입주 청소 현장에서 주방 싱크대 상판 및 욕실 수전 부위의 미생물 오염도(ATP RLU)를 측정한 결과입니다.
|
||||
160℃ 고온 스팀과 에코서트 효소 분해 세정 후 병원 무균실 기준(30 RLU 이하)을 상회하는 12 RLU 수준으로 완벽하게 저감되었습니다.
|
||||
아래 값은 점검 기록이 표시되는 방식을 설명하기 위한 예시입니다. 실제 현장 측정값이나 시공 결과를 뜻하지 않습니다.
|
||||
</p>
|
||||
|
||||
<div class="atp-compare-box">
|
||||
<div>
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">시공 전 (BEFORE)</div>
|
||||
<div class="atp-val before">1,420 <span style="font-size: 14px;">RLU</span></div>
|
||||
<div style="font-size: 12px; color: #ef4444; margin-top: 4px;">심각한 미세 세균 번식</div>
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">점검 전 기록</div>
|
||||
<div class="atp-val before">기록값 A</div>
|
||||
<div style="font-size: 12px; color: #b91c1c; margin-top: 4px;">확인할 항목 표시 예시</div>
|
||||
</div>
|
||||
<div style="border-left: 1px solid var(--line);">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">시공 후 (AFTER)</div>
|
||||
<div class="atp-val after">12 <span style="font-size: 14px;">RLU</span></div>
|
||||
<div style="font-size: 12px; color: var(--accent); margin-top: 4px;">병원 수술실 기준 패스 (99.1% 저감)</div>
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: 4px;">점검 후 기록</div>
|
||||
<div class="atp-val after">기록값 B</div>
|
||||
<div style="font-size: 12px; color: var(--accent); margin-top: 4px;">확인 완료 표시 예시</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -239,16 +238,16 @@
|
|||
<section class="wrap" style="padding-bottom: var(--space-8);">
|
||||
<div class="eyebrow" style="text-align: center;">단계별 시공 절차</div>
|
||||
<h2 style="font-size: var(--step-3); font-weight: 800; text-align: center; margin-top: 6px;">
|
||||
청온 케어 임상 6단계 시공 체계
|
||||
청온 케어 6단계 검수 절차 예시
|
||||
</h2>
|
||||
|
||||
<div class="step-deep-list">
|
||||
<div class="step-deep-item">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 01</div>
|
||||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">H14 헤파 필터 건식 초미세 흡진</h3>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">필터 흡진 작업 확인</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
벽면 실크벽지 미세 분진, 천장 우물 몰딩, 조명 레일 내부의 0.3㎛ 부유 입자를 99.995% 집진하여 공기 중 재비산을 완벽 차단합니다.
|
||||
벽면, 천장 몰딩, 조명 레일의 작업 범위를 확인하고 흡진 작업 여부를 작업표에 기록합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -256,9 +255,9 @@
|
|||
<div class="step-deep-item">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 02</div>
|
||||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">160℃ 4.0bar 고온 스팀 살균</h3>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">스팀 장비 설정 확인</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
배수구 유가, 변기 틈새, 주방 후드 필터에 화학 세제 없이 4.0bar 고압 증기를 분사하여 기름때를 녹이고 99.9% 멸균합니다.
|
||||
배수구, 욕실 틈새, 주방 후드처럼 확인이 필요한 면을 구분하고 사용 장비 설정을 남깁니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -266,9 +265,9 @@
|
|||
<div class="step-deep-item">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 03</div>
|
||||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">약알칼리 에코서트 친환경 효소 분해</h3>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">표면별 세정 확인</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
원목 마루 및 천연 대리석 자재에 손상을 주지 않는 100% 생분해성 식물 세정제를 도포하여 찌든 산화 피막을 안전하게 중화합니다.
|
||||
바닥과 석재의 재질을 확인한 뒤 사용 제품과 적용 범위를 작업표에 표시합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -278,7 +277,7 @@
|
|||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">서랍장·걸레받이 전면 탈거 정밀 세척</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
싱크대 하부 걸레받이와 수납장 레일을 전량 분리 탈거하여, 보이지 않는 시공 톱밥 및 유해 분진을 내측까지 완전 제거합니다.
|
||||
싱크대 하부와 수납장 레일의 접근 가능 여부를 확인하고, 분리 작업 범위를 기록합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -286,9 +285,9 @@
|
|||
<div class="step-deep-item">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 05</div>
|
||||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">제주 편백 피톤치드 연무 훈증</h3>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">환기와 마감 확인</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
새집증후군 유발 물질인 포름알데히드 및 휘발성유기화합물(VOCs)을 중화하고 실내 숲속 공기질을 복원합니다.
|
||||
환기와 마감 상태를 확인하고, 향·마감 제품을 사용했다면 제품명과 적용 범위를 기록합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -296,9 +295,9 @@
|
|||
<div class="step-deep-item">
|
||||
<div style="font-family: var(--font-mono); font-size: var(--step-2); font-weight: 800; color: var(--accent);">STEP 06</div>
|
||||
<div>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">32개 항목 마스터 닥터 1:1 검수 및 디지털 인증</h3>
|
||||
<h3 style="font-size: var(--step-1); font-weight: 700;">32개 항목 현장 검수 확인</h3>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin-top: 6px; line-height: 1.6;">
|
||||
배차 현장 팀장과 고객이 함께 32개 정밀 체크리스트를 1:1 대조하고, 모바일 리포트를 발행하여 최종 승인합니다.
|
||||
현장 담당자와 의뢰자가 32개 체크리스트를 함께 확인하는 흐름을 예시로 표시합니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -310,7 +309,7 @@
|
|||
<div class="frame">
|
||||
<img
|
||||
src="../assets/steam-qc.webp"
|
||||
alt="독일 카처 4.0bar 정밀 스팀 위생 장비와 멸균 키트"
|
||||
alt="작업 장비와 검수 키트를 보여 주는 예시 이미지"
|
||||
style="width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover;"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -324,10 +323,10 @@
|
|||
우리 집 공간의 정찰제 견적을 확인하세요
|
||||
</h2>
|
||||
<p style="font-size: var(--step--1); color: var(--ink-muted); margin: 12px 0 24px;">
|
||||
추가금 없는 평당 11,000원 명확한 기준. 1분 만에 실시간 견적 산출이 가능합니다.
|
||||
선택 항목을 기준으로 예상 금액을 확인할 수 있습니다. 실제 견적이나 신청은 진행되지 않습니다.
|
||||
</p>
|
||||
<a href="../index.html#quote" class="btn-primary" style="display: inline-block; padding: 14px 28px; text-decoration: none; font-size: var(--step-0);">
|
||||
실시간 정찰제 견적 계산하기 →
|
||||
<a href="../index.html#calculator" class="btn-primary" style="display: inline-block; padding: 14px 28px; text-decoration: none; font-size: var(--step-0);">
|
||||
항목별 예상 견적 보기 →
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -336,9 +335,8 @@
|
|||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>청온 케어 · 서울 마포구 마포대로 109 청온랩 4층</p>
|
||||
<p class="muted">연중무휴 08:00 – 20:00 (사전 예약제)</p>
|
||||
<p class="muted">care@cheongon.kr</p>
|
||||
<p>청온 케어 · 위생 케어 안내</p>
|
||||
<p class="muted">이 페이지의 절차와 수치는 예시이며 실제 예약은 접수되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
|||
15
apps/site/public/work/cheongon-care/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import "./tokens.css";
|
||||
@import "./tokens.css?v=20260912all";
|
||||
|
||||
/* ==========================================================================
|
||||
청온 케어 (CHEONG-ON CARE) — 메인 스타일시트 SSOT
|
||||
|
|
@ -193,7 +193,7 @@ button {
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: all var(--dur-quick);
|
||||
transition: background-color var(--dur-quick), color var(--dur-quick), box-shadow var(--dur-quick);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ button {
|
|||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.95fr);
|
||||
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
||||
gap: var(--space-7);
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -351,41 +351,20 @@ button {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.bezel-outer {
|
||||
background: rgba(15, 118, 110, 0.05);
|
||||
border: 1px solid var(--line);
|
||||
padding: 6px;
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.hero-frame {
|
||||
border-radius: calc(var(--radius-md) - 4px);
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.08);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.hero-frame img {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
aspect-ratio: 5/4;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-stat-floating {
|
||||
position: absolute;
|
||||
bottom: -14px;
|
||||
left: 16px;
|
||||
background: var(--surface-raised);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 8px 14px;
|
||||
box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.hero-frame figcaption { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--line); color: var(--ink-muted); font-size: var(--step--1); line-height: 1.55; }
|
||||
|
||||
/* ── 투명 정찰제 실시간 견적 산정 섹션 ── */
|
||||
.calculator-section {
|
||||
|
|
@ -393,10 +372,10 @@ button {
|
|||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.section-head {
|
||||
text-align: center;
|
||||
max-width: 680px;
|
||||
margin-inline: auto;
|
||||
.calc-section .section-head {
|
||||
text-align: left;
|
||||
max-width: 48rem;
|
||||
margin-inline: 0;
|
||||
margin-bottom: var(--space-6);
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +442,7 @@ button {
|
|||
font-weight: 600;
|
||||
font-size: var(--step--1);
|
||||
text-align: center;
|
||||
transition: all var(--dur-quick);
|
||||
transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick);
|
||||
white-space: normal;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
|
@ -513,7 +492,7 @@ button {
|
|||
font-size: var(--step--1);
|
||||
color: var(--ink-muted);
|
||||
cursor: pointer;
|
||||
transition: all var(--dur-quick);
|
||||
transition: background-color var(--dur-quick), border-color var(--dur-quick), color var(--dur-quick);
|
||||
}
|
||||
|
||||
.addon-label:has(input:checked) {
|
||||
|
|
@ -850,7 +829,7 @@ button {
|
|||
font-weight: 600;
|
||||
background: var(--surface);
|
||||
cursor: pointer;
|
||||
transition: all var(--dur-quick);
|
||||
transition: border-color var(--dur-quick), color var(--dur-quick);
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
|
|
@ -864,10 +843,13 @@ button {
|
|||
inset: 0;
|
||||
margin: auto;
|
||||
width: min(90vw, 560px);
|
||||
max-height: calc(100dvh - (var(--space-4) * 2));
|
||||
background: #ffffff;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-5);
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.15);
|
||||
outline: none;
|
||||
}
|
||||
|
|
@ -923,3 +905,75 @@ button {
|
|||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
html:has(dialog[open]) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 접근성·초광폭 프리플라이트: 컨트롤의 독립성과 실제 표면 여백을 같은 토큰으로 유지한다. */
|
||||
:where(a, button, input, select, textarea):focus-visible {
|
||||
outline: 3px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-outline,
|
||||
.calc-submit,
|
||||
.type-btn,
|
||||
.addon-label,
|
||||
.action-btn,
|
||||
.filter-tab,
|
||||
.mode-btn,
|
||||
.head-actions .btn,
|
||||
input:not([type="checkbox"]),
|
||||
select,
|
||||
textarea {
|
||||
min-height: var(--control-block);
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-outline,
|
||||
.calc-submit,
|
||||
.type-btn,
|
||||
.addon-label,
|
||||
.action-btn,
|
||||
.filter-tab,
|
||||
.mode-btn,
|
||||
.head-actions .btn {
|
||||
padding-inline: var(--control-pad-inline);
|
||||
}
|
||||
|
||||
.form-label + input,
|
||||
.form-label + select,
|
||||
.form-label + textarea {
|
||||
padding-inline: var(--control-pad-inline);
|
||||
}
|
||||
|
||||
.demo-notice {
|
||||
max-width: 58ch;
|
||||
margin-top: var(--space-4);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-left: 3px solid var(--accent);
|
||||
background: var(--accent-light);
|
||||
color: var(--ink-muted);
|
||||
font-size: var(--step--1);
|
||||
line-height: 1.55;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.head-actions .client-only-btn { display: none; }
|
||||
.hero-ctas { align-items: stretch; }
|
||||
.hero-ctas .btn-primary { width: 100%; }
|
||||
.hero-title { font-size: clamp(1.65rem, 7.8vw, 2.15rem); }
|
||||
.hero-trust { align-items: flex-start; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 1ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 1ms !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* ==========================================================================
|
||||
청온 케어 (CHEONG-ON CARE) — 디자인 토큰 SSOT
|
||||
프리셋: ledger-console + swiss-minimal
|
||||
컨셉: 임상 수준의 위생 프로토콜과 정직한 정찰제 — 공간의 숨결을 되돌리는 케어
|
||||
컨셉: 현장 검수 절차와 정직한 정찰제 — 공간의 상태를 기록하는 케어
|
||||
단일 강조색: 에메랄드 틸 (#0D9488)
|
||||
========================================================================== */
|
||||
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
--line-inverse: #334155;
|
||||
|
||||
/* 단일 강조색 (에메랄드 틸) */
|
||||
--accent: #0d9488;
|
||||
--accent-hover: #0f766e;
|
||||
--accent: #0f766e;
|
||||
--accent-hover: #115e59;
|
||||
--accent-light: #f0fdfa;
|
||||
--accent-surface: #ccfbf1;
|
||||
--accent-ink: #ffffff;
|
||||
|
|
@ -63,8 +63,10 @@
|
|||
--space-8: clamp(4.5rem, 3rem + 6vw, 9rem);
|
||||
|
||||
/* 컨테이너 및 패딩 — 전체 쇼케이스 절대 통일 SSOT */
|
||||
--container: 1120px;
|
||||
--container: clamp(70rem, 62vw, 85rem);
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
|
||||
--control-block: 2.75rem;
|
||||
--control-pad-inline: 0.875rem;
|
||||
|
||||
/* 모션 */
|
||||
--dur-quick: 180ms;
|
||||
|
|
|
|||
24
apps/site/public/work/dure-enrollment/design.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# 두레대학 수강신청 디자인 기록
|
||||
|
||||
## 브리프
|
||||
|
||||
- 무엇을: 과목 목록과 내 시간표를 한 작업면에서 비교하며 신청하는 화면.
|
||||
- 누구에게: 과목명·요일·학점·여석과 신청 결과를 대조하는 학생.
|
||||
- 제약: 과목·정원 데이터, 신청 계산, 취소·실행취소, 단축키, 인쇄와 모바일 하단 내비를 보존한다.
|
||||
|
||||
## 판단 기준
|
||||
|
||||
| 축 | 질문 | 적용 |
|
||||
| --- | --- | --- |
|
||||
| Warde · 정보 전달 | 필터 뒤의 과목 목록과 시간표가 한 작업 흐름으로 읽히는가 | 공통 작업면과 한 단계 낮춘 중복 테두리 |
|
||||
| Ruder · 비교 변수 | 과목명·시간·학점·여석을 같은 시선 흐름에서 비교하는가 | 행 메타와 우측 학점·여석의 정렬, tabular figures |
|
||||
| Albers · 상태 맥락 | 관심·신청·여석 부족 상태가 텍스트와 색으로 함께 구분되는가 | 상태 텍스트를 남기고 색은 상태 보조로만 사용 |
|
||||
|
||||
Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하는 참고 질문이며 인과나 성능 법칙이 아니다.
|
||||
|
||||
## 역할 토큰과 검증
|
||||
|
||||
- `--workspace-radius`, `--workspace-rule`, `--workspace-body-size`, `--workspace-meta-size`가 작업면과 정보 위계를 관리한다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="twitter:image" content="https://designpaca.chanpaca.net/work/dure-enrollment/og.png" />
|
||||
<meta
|
||||
name="description"
|
||||
content="과목을 고르면 시간표에 얹히는 수강신청 데모. 관심 과목, 신청 내역, 이수 현황까지 여섯 화면. 시간 겹침과 학점 초과를 실제로 검사합니다."
|
||||
content="과목 선택과 시간표를 함께 살펴보는 수강신청 화면입니다. 관심 과목, 신청 내역, 이수 현황을 한 흐름으로 확인할 수 있습니다."
|
||||
/>
|
||||
<meta property="og:title" content="두레대학 수강신청 · 시간표" />
|
||||
<meta property="og:description" content="시간표에 얹히는 수강신청." />
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=IBM+Plex+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=23" />
|
||||
<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 width='32' height='32' rx='7' fill='%23fafafa'/%3E%3Crect x='6.5' y='6.5' width='19' height='19' rx='4' fill='%23c2410c'/%3E%3Crect x='9.5' y='10' width='5.5' height='4.5' rx='1' fill='%23ffffff'/%3E%3Crect x='16' y='10' width='6.5' height='4.5' rx='1' fill='%23ffffff' opacity='0.8'/%3E%3Crect x='9.5' y='15.8' width='5.5' height='9.2' rx='1' fill='%23ffffff' opacity='0.9'/%3E%3Crect x='16' y='15.8' width='6.5' height='6.2' rx='1' fill='%23ffffff' opacity='0.75'/%3E%3C/svg%3E"
|
||||
|
|
@ -77,7 +77,8 @@
|
|||
<section class="view is-on" id="view-catalog" aria-labelledby="cat-h" tabindex="-1">
|
||||
<section class="catalog" aria-labelledby="cat-h">
|
||||
<h2 class="v-h" id="cat-h">2026학년도 1학기 과목</h2>
|
||||
<p class="view-sub">과목을 누르면 강의계획과 신청 버튼이 열립니다. 신청하면 시간표에 얹힙니다.</p>
|
||||
<p class="view-sub">과목을 누르면 강의계획과 신청 버튼이 열립니다. 선택한 과목은 시간표에서 확인할 수 있습니다.</p>
|
||||
<p class="view-sub" role="note">이 페이지의 과목·정원·이수 현황은 예시입니다. 선택한 과목은 새로고침하면 초기화됩니다.</p>
|
||||
|
||||
<div class="sheet">
|
||||
<div class="toolbar" role="search">
|
||||
|
|
@ -117,7 +118,7 @@
|
|||
<!-- ── 관심 과목 ── -->
|
||||
<section class="view" id="view-starred" aria-labelledby="st-h" tabindex="-1">
|
||||
<h2 class="v-h" id="st-h">관심 과목</h2>
|
||||
<p class="view-sub">강의계획에서 관심을 남기면 여기에 모입니다. 여석과 시간은 실시간입니다.</p>
|
||||
<p class="view-sub">강의계획에서 관심을 남기면 여기에 모입니다. 여석과 시간은 과목 정보에서 확인하세요.</p>
|
||||
<div class="statline" id="st-stats"></div>
|
||||
<p class="st-warn" id="st-warn" role="status" hidden></p>
|
||||
<div class="toolbar">
|
||||
|
|
@ -324,7 +325,7 @@
|
|||
/*
|
||||
수강신청 상태 기계 — 겹침 검사, 학점 한도, 여석 감소까지 전부 진짜.
|
||||
여섯 화면이 같은 상태(enrolled, starred)를 공유한다.
|
||||
새로고침하면 초기화됩니다(데모 원칙: 저장을 흉내내지 않는다).
|
||||
새로고침하면 초기화하며, 선택 내용은 저장하지 않는다.
|
||||
*/
|
||||
const fmtDay = (d) => ({ 1: "월", 2: "화", 3: "수", 4: "목", 5: "금" })[d];
|
||||
|
||||
|
|
@ -344,14 +345,14 @@
|
|||
const NOTICES = [
|
||||
{ date: "03-08", kind: "변경", body: "게임이론(ECON315) 강의실이 경상관 308호에서 309호로 바뀝니다. 이 목록에는 이미 반영했습니다." },
|
||||
{ date: "03-07", kind: "공지", body: "수강신청 기간은 3월 2일부터 3월 13일 자정까지입니다. 기간 끝에는 시간표가 확정됩니다." },
|
||||
{ date: "03-06", kind: "안내", body: "기준 학점(18)을 넘는 신청은 학부 사무실의 승인을 받아야 합니다. 이 데모에서는 넘는 신청을 막아둡니다." },
|
||||
{ date: "03-06", kind: "안내", body: "기준 학점(18)을 넘는 신청은 학부 사무실의 승인을 받아야 합니다. 이 화면에서는 기준을 넘는 신청을 막습니다." },
|
||||
{ date: "03-05", kind: "안내", body: "드로잉 입문(ART130)은 재료비 3만 원이 듭니다. 첫 수업에 안내합니다." },
|
||||
{ date: "03-03", kind: "공지", body: "정원이 마감된 과목은 대기자 신청을 받지 않습니다. 분반 개설은 학부 상황에 따라 결정됩니다." },
|
||||
{ date: "03-02", kind: "공지", body: "2026학년도 1학기 수강신청이 열렸습니다. 시간이 겹치는 과목은 함께 담을 수 없습니다." },
|
||||
];
|
||||
|
||||
const MAX_CREDITS = 18;
|
||||
// 네 학기를 마친 시점의 이수 기록 — 이 데모의 가상 학생
|
||||
// 네 학기를 마친 시점의 이수 기록 — 화면에 표시하는 예시 학생
|
||||
const RECORD = [
|
||||
{ label: "전공", need: 66, done: 45 },
|
||||
{ label: "교양", need: 24, done: 21 },
|
||||
|
|
@ -868,7 +869,7 @@
|
|||
if (!current || $("cd-apply").disabled) return;
|
||||
current.seats -= 1;
|
||||
enrolled.add(current.id);
|
||||
setStatus(`${current.name} 신청 완료 · ${timeLabel(current)} · ${credits()}학점`, false);
|
||||
setStatus(`${current.name} 시간표에 추가됨 · ${timeLabel(current)} · ${credits()}학점`, false);
|
||||
dialog.close();
|
||||
renderAll();
|
||||
});
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 75 KiB |
|
|
@ -141,8 +141,9 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
max-width: 1180px;
|
||||
margin-inline: auto;
|
||||
padding: var(--space-5) var(--space-5) 96px;
|
||||
min-width: 0;
|
||||
}
|
||||
.view { display: none; }
|
||||
.view { display: none; min-width: 0; }
|
||||
.view.is-on { display: block; }
|
||||
.view:focus { outline: none; }
|
||||
.view > h1 { margin-bottom: 4px; }
|
||||
|
|
@ -152,11 +153,15 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
.view.is-on { animation: view-in 70ms var(--ease-out) both; }
|
||||
@keyframes view-in { from { opacity: 0; transform: translateY(4px); } }
|
||||
}
|
||||
.view-sub { color: var(--muted-foreground); font-size: var(--step-0); margin-bottom: var(--space-5); max-width: 46em; }
|
||||
.view-sub { color: var(--muted-foreground); font-size: var(--workspace-meta-size); margin-bottom: var(--space-5); max-width: 46em; }
|
||||
#view-catalog.is-on {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
|
||||
gap: var(--space-4);
|
||||
gap: 0;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--workspace-rule);
|
||||
border-radius: var(--workspace-radius);
|
||||
box-shadow: none;
|
||||
align-items: start;
|
||||
}
|
||||
.view > *:not(:first-child):not(.view-sub) { margin-top: 0; }
|
||||
|
|
@ -226,16 +231,17 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
appearance: none; background: none; cursor: pointer; font: inherit;
|
||||
width: 100%; text-align: left;
|
||||
display: flex; justify-content: space-between; align-items: center; gap: var(--space-4);
|
||||
padding: var(--space-3) var(--space-2);
|
||||
min-width: 0;
|
||||
padding: 0.875rem var(--space-2);
|
||||
border: 0; border-bottom: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
border-radius: 0;
|
||||
transition: background-color 80ms ease;
|
||||
}
|
||||
.course-row:first-child { border-top: 1px solid var(--border); }
|
||||
.course-row:hover { background: var(--muted); }
|
||||
.course-row:active { scale: 0.995; }
|
||||
.c-main { display: grid; gap: 2px; min-width: 0; }
|
||||
.c-name { font-weight: 600; display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
|
||||
.c-name { font-weight: 600; display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; font-size: var(--workspace-body-size); }
|
||||
.c-dept {
|
||||
font-size: var(--step--1); font-weight: 500;
|
||||
color: var(--muted-foreground);
|
||||
|
|
@ -243,10 +249,10 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
border-radius: var(--radius-xs);
|
||||
padding: 1px 6px;
|
||||
}
|
||||
.c-sub { color: var(--muted-foreground); font-size: var(--step--1); }
|
||||
.c-side { display: grid; gap: 2px; text-align: right; flex: none; font-size: var(--step--1); }
|
||||
.c-credit { font-weight: 500; color: var(--foreground); }
|
||||
.c-seats { color: var(--muted-foreground); }
|
||||
.c-sub { color: var(--muted-foreground); font-size: var(--workspace-meta-size); }
|
||||
.c-side { display: grid; min-width: 4.75rem; gap: 0.25rem; text-align: right; flex: none; font-size: var(--workspace-meta-size); }
|
||||
.c-credit { font-weight: 500; color: var(--foreground); font-variant-numeric: tabular-nums; }
|
||||
.c-seats { color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
|
||||
.c-seats.is-low { color: var(--danger); font-weight: 600; }
|
||||
.c-star { color: var(--accent); font-weight: 600; }
|
||||
.c-enrolled {
|
||||
|
|
@ -262,8 +268,11 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
.empty { padding: var(--space-4) 0; color: var(--muted-foreground); }
|
||||
|
||||
/* ── 표 ── */
|
||||
.table-wrap { overflow-x: auto; overflow-y: hidden; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
.table-wrap { max-width: 100%; overflow-x: auto; overflow-y: hidden; overscroll-behavior-inline: contain; scrollbar-gutter: stable both-edges; }
|
||||
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
|
||||
.table-wrap > table { min-width: 600px; }
|
||||
#en-table, #ar-table { min-width: 680px; }
|
||||
table th, table td { overflow-wrap: normal; word-break: keep-all; }
|
||||
th { font-weight: 500; } /* UA 기본 th 볼드 재정의 */
|
||||
thead th {
|
||||
text-align: left; font-weight: 500; font-size: var(--step--1);
|
||||
|
|
@ -289,7 +298,7 @@ tbody tr:hover { background: var(--muted); }
|
|||
background: none; color: var(--muted-foreground);
|
||||
border: 1px solid var(--border-strong); border-radius: var(--radius);
|
||||
height: 28px; padding: 0 10px;
|
||||
transition: all 100ms ease;
|
||||
transition: background-color 100ms ease, border-color 100ms ease, color 100ms ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.give-btn:hover { border-color: var(--danger); color: var(--danger); }
|
||||
|
|
@ -403,6 +412,8 @@ tbody tr:hover { background: var(--muted); }
|
|||
|
||||
/* ── 모달: 스플릿. 정보 패널과 핵심 동작을 분리한다. ── */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
width: min(94vw, 860px);
|
||||
max-height: min(86dvh, 760px);
|
||||
|
|
@ -412,8 +423,11 @@ tbody tr:hover { background: var(--muted); }
|
|||
background: var(--card);
|
||||
color: var(--foreground);
|
||||
overflow: hidden;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
html:has(dialog.modal[open]) { overflow: hidden; }
|
||||
.modal::backdrop { background: var(--backdrop-dim); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.modal[open] { animation: modal-in 200ms cubic-bezier(0.16, 1, 0.3, 1) both; }
|
||||
|
|
@ -514,7 +528,8 @@ tbody tr:hover { background: var(--muted); }
|
|||
|
||||
/* ── 반응형 ── */
|
||||
@media (max-width: 900px) {
|
||||
#view-catalog.is-on { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
|
||||
#view-catalog.is-on { grid-template-columns: minmax(0, 1fr); gap: 0; }
|
||||
#view-catalog .catalog { border-right: 0; border-bottom: 1px solid var(--workspace-rule); }
|
||||
.timetable-panel { position: static; }
|
||||
.notice-list li { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
|
||||
.no-kind, .no-date { justify-self: start; }
|
||||
|
|
@ -526,6 +541,10 @@ tbody tr:hover { background: var(--muted); }
|
|||
.layout {
|
||||
padding: var(--space-4) max(var(--space-5), var(--safe-right)) calc(72px + var(--safe-bottom)) max(var(--space-5), var(--safe-left));
|
||||
}
|
||||
.toolbar { align-items: stretch; min-width: 0; }
|
||||
#q { width: 100%; min-width: 0; flex: 1 1 100%; }
|
||||
.chips { width: 100%; min-width: 0; flex-wrap: wrap; }
|
||||
.chip { min-width: 0; flex: 1 1 0; justify-content: center; }
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.mspread { grid-template-columns: minmax(0, 1fr); }
|
||||
|
|
@ -675,15 +694,15 @@ kbd {
|
|||
border-top: 1px solid var(--border);
|
||||
padding: 6px max(var(--space-2), var(--safe-left)) calc(6px + var(--safe-bottom)) max(var(--space-2), var(--safe-right));
|
||||
}
|
||||
@media (max-width: 900px) { .bottom-nav { display: grid; grid-auto-flow: column; } }
|
||||
@media (max-width: 900px) { .bottom-nav { display: grid; min-width: 0; grid-template-columns: repeat(5, minmax(0, 1fr)); grid-auto-flow: row; } }
|
||||
.bnav-item {
|
||||
appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
|
||||
display: grid; justify-items: center; align-content: center; gap: 3px;
|
||||
min-height: 52px; min-width: 48px; padding: 4px 2px;
|
||||
min-height: 52px; min-width: 0; padding: 4px 2px;
|
||||
border-radius: var(--radius);
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
.bnav-item span { font-size: var(--step--2); font-weight: 500; }
|
||||
.bnav-item span { max-width: 100%; font-size: var(--step--2); font-weight: 500; overflow-wrap: anywhere; }
|
||||
.bnav-item.is-current { color: var(--accent-ink); font-weight: 600; }
|
||||
.bnav-item.is-current svg { color: var(--accent); }
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,11 @@
|
|||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
|
||||
--workspace-radius: 8px;
|
||||
--workspace-rule: var(--border);
|
||||
--workspace-body-size: 0.9375rem;
|
||||
--workspace-meta-size: 0.8125rem;
|
||||
|
||||
/* 안전 영역 — viewport-fit=cover 선언 후에만 0이 아닌 값이 온다(WebKit 공식·MDN env()) */
|
||||
--safe-top: env(safe-area-inset-top, 0px);
|
||||
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
||||
|
|
|
|||
169
apps/site/public/work/font-foundry/assets/fonts/LICENSE.html
Normal file
13
apps/site/public/work/font-foundry/assets/fonts/SOURCES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## 브리프
|
||||
|
||||
- **무엇을**: 가상 서체 기획을 정직하게 밝히면서, 실제 마루 부리와 Pretendard로 문장 조판의 차이를 즉시 체험하는 한국어 타입 스튜디오.
|
||||
- **무엇을**: 가상 서체 기획을 정직하게 밝히면서, 실제 마루 부리와 Pretendard로 문장 조판의 형태 차이를 즉시 체험하는 한국어 타입 스튜디오.
|
||||
- **누구에게**: 활자를 고르거나 본문 조판 값을 결정해야 하는 편집자·디자이너·개발자.
|
||||
- **제약**: ko-KR 한국어, 밝은 아이보리·검정 본문·버밀리언 정체성 유지. 가상 서체를 판매하거나 가짜 스펙을 제시하지 않으며, 본문은 최대 42rem. 320px부터 가로 넘침이 없어야 한다.
|
||||
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
| `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`으로 둔다. |
|
||||
| `WCAG-READ` · 접근성 | 200% 텍스트 확대와 320 CSS px reflow를 핵심 화면에서 별도로 시험한다. | 큰 활자 장면은 접근성 제목을 대신하지 않는다. | 이전 판에서 320px·200% 확대와 `aria-hidden` 표본을 관찰했다. Round 4 현재 결과는 최종 proof를 참조한다. |
|
||||
|
||||
## 방향
|
||||
|
||||
|
|
@ -25,11 +25,11 @@
|
|||
|
||||
## 토큰과 구현
|
||||
|
||||
- **폰트**: 마루 부리는 제목·조판 시험대·표본의 제목 상태, Pretendard는 본문·기본 표본 상태. 두 폰트 모두 CDN 웹폰트이며 `font-synthesis-weight: none`으로 합성 굵기를 막는다.
|
||||
- **폰트**: 마루 부리는 공식 로컬 WOFF2 Regular 400·SemiBold 600으로 제목·조판 시험대·표본의 제목 상태를 맡고, 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와 리셋은 유지한다.
|
||||
- **크기**: h1 `clamp(40px, 1.6rem + 3vw, 60px)`, 표본 `clamp(96px, 11vw, 180px)`, 본문 폭 최대 `42rem`.
|
||||
- **레이아웃**: 데스크톱 첫 화면은 5:7 두 칼럼으로, 종이색 소개면과 적갈색 표본면을 맞물린다. 900px 미만에서는 소개면 다음에 표본면을 한 칼럼으로 둔다.
|
||||
- **상호작용**: native 버튼 두 개의 `aria-pressed`가 표본 폰트와 상태 문구를 바꾼다. 조판 시험대는 실제 마루 부리·Pretendard 선택과 크기·자간·행간 range를 제공하고, 리셋은 네 값을 함께 복원한다.
|
||||
- **성능**: 새 JavaScript 라이브러리·이미지·애니메이션 없음. 초기 체험은 인라인 DOM 코드만 사용한다. 활자 케이스의 두 장면은 생성 이미지임을 캡션으로 밝힌다.
|
||||
|
||||
## 의도적으로 하지 않은 것
|
||||
|
|
@ -38,10 +38,40 @@
|
|||
- 인장·붓글씨·세로쓰기·종이결처럼 한국 활자를 이국화하는 장식을 사용하지 않는다. 활자의 형태와 조판만으로 분위기를 만든다.
|
||||
- 움직임으로 활자의 차이를 설명하지 않는다. reduced motion에서도 같은 표본과 모든 조작이 남는다.
|
||||
|
||||
## 검증 기록 · 2026-09-12
|
||||
## 이전 판 검증 기록 · 2026-09-12
|
||||
|
||||
아래는 이전 판에서 남긴 역사적 관찰이며 Round 4의 현재 통과 결과가 아니다. Round 4 결과는 최종 proof를 참조한다.
|
||||
|
||||
- `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 폴더에 있다.
|
||||
|
||||
## Round 4 판단표 · 2026-09-12
|
||||
|
||||
| 축 | 참고 범위와 현재 질문 | 채택과 기각 조건 | 구현 |
|
||||
| --- | --- | --- | --- |
|
||||
| 구도 | Arnheim·Hofmann의 균형·대비·구도 관점을 참고해 소개와 표본의 관계, 5:7 대안, 작은 폭에서의 순서 유지를 살핀다. | 소개와 표본의 관계가 읽히고 작은 폭에서 순서가 유지되면 채택한다. CTA가 밀리면 기각한다. | `--container-wide`, `--pad-wide` |
|
||||
| 타입 | Ruder의 구조·리듬·비례와 Wallace의 개인·성능·선호 분리 범위를 참고해 같은 문장·22px·1.8·동일 폭 두 칼럼과 가족 선택을 살핀다. | 조절과 reset이 깨지지 않을 때만 채택한다. 읽기 성능의 보편 효과는 주장하지 않는다. | `--font-display`, `--font-body` |
|
||||
| 색 | Albers의 색 맥락 관점을 참고해 작은 accent와 큰 stage의 관계를 비교한다. | 역할 경계와 대비가 유지되고 상태가 묻히지 않을 때 채택한다. | `--stage-ink`, `--accent` |
|
||||
|
||||
## Round 4 실증 기록 · 2026-09-12
|
||||
|
||||
- **방향과 원리**: 한성활자연구소의 종이색·적갈색·금속 활자 이미지는 유지하고, 첫 화면을 5:7 종이 소개면과 적갈색 표본면으로 재배치했다. 표본면의 큰 흰 글자, 기준선, 선택 상태는 같은 시선 안에서 글꼴 전환을 읽게 한다.
|
||||
- **비교 조건**: Ruder의 구조·리듬·비례를 현재 화면의 검증 질문으로만 번역했다. 비교 두 칼럼은 같은 원문, 동일 폭, 기본 22px(`1.375rem`)와 `1.8` 행간을 공유하며 글꼴 가족만 다르다. 서체마다 글자가 보이는 크기는 다를 수 있다는 안내를 함께 둔다.
|
||||
- **채택과 측정 한계**: 조판 시험대는 실제 웹폰트 선택을 문장에 적용하고 네 제어값을 리셋한다. 화면 비교는 형태·폭·줄바꿈의 관찰을 돕지만, 읽기 속도·이해도 향상은 사용자 과업 측정 전까지 주장하지 않는다. 역사처럼 보일 수 있는 견본 문단은 견본글임을 명시했다.
|
||||
- **동일 브리프 보존**: 가상 기획·예시 가격·라이선스 안내, 네 개의 내비와 앵커, 기존 CTA, 금속 이미지와 생성 이미지 표기, 버튼의 `aria-pressed`·상태 문구, 기존 조판 range와 reset 기능을 보존한다.
|
||||
|
||||
### Round 4 최종 판정
|
||||
|
||||
2026-09-12 원본 담당자 판정: 수용. 소개와 표본의 면 구분, 표본과 제어의 군집, 같은 조건의 문장 비교를 실제 데스크톱·모바일 캡처에서 확인했다. 작은 폭의 확대에서 발견한 내비·가격·제목의 넘침과 앵커 가림을 수정했다.
|
||||
|
||||
`outputs/design-expertise-20260912/round4/browser-proof/root-final-v3/feature-check-results.json`: 수정본 320·390·1440·1920px 4개 행 통과, 실패 0. 글꼴 실제 로딩, 키보드 선택·range·초기화, 앵커, 감소 모션, 이미지 decode, 문서 가로 넘침을 검사했다. 확대 검사는 루트 글자 크기 200% 및 자간 0.12em·행간 2의 조합이며, 브라우저 확대나 WCAG 전체 적합성 인증을 대신하지 않는다. 표본면의 설명·상태·선택/비선택 버튼 텍스트 대비는 6.07:1이었다. 사용자 읽기 속도·이해도·전환은 미측정이다.
|
||||
|
||||
## 2026-09-12 전체 쇼케이스 점검
|
||||
|
||||
- 기존 글꼴 선택·range·reset, 22px·1.8·동일 폭 비교 계약과 캡션·앵커 구도를 유지했다. 로컬 MaruBuri 경로는 `fonts.css?v=20260912all`로 명시했다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
<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="./styles/fonts.css?v=20260912all" />
|
||||
<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="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 width='32' height='32' fill='%23F5F1E8'/%3E%3Crect x='8' y='8' width='16' height='16' fill='%23A63D2F'/%3E%3C/svg%3E" />
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
<h1 id="hero-h">읽는 리듬을<br />만드는 활자.</h1>
|
||||
</div>
|
||||
<div class="hero-note">
|
||||
<p>글자는 모양만으로 읽히지 않습니다. 문장 안에서 어떤 폭으로 서고, 얼마나 가까이 호흡하는지가 읽는 시간을 바꿉니다.</p>
|
||||
<p>글자는 모양만으로 읽히지 않습니다. 문장 안에서 어떤 폭으로 서고, 얼마나 가까이 호흡하는지 직접 관찰해 보세요.</p>
|
||||
<a class="text-link" href="#bench">조판해 보기 <span aria-hidden="true">↓</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -59,20 +59,21 @@
|
|||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<p class="eyebrow">두 가지 읽기 방식</p>
|
||||
<h2 id="typefaces-h">같은 문장이<br />다른 속도로 읽힌다.</h2>
|
||||
<h2 id="typefaces-h">같은 문장, 서로 다른 결.</h2>
|
||||
</div>
|
||||
<p class="comparison-note">크기·행간·폭을 맞췄습니다. 서체마다 글자가 보이는 크기는 다를 수 있습니다.</p>
|
||||
<div class="comparison">
|
||||
<article class="typeface">
|
||||
<header class="typeface-head">
|
||||
<h3>본문</h3>
|
||||
<p class="muted">Pretendard · 작고 긴 문장에 필요한 명료한 골격</p>
|
||||
<h3>Pretendard</h3>
|
||||
<p class="muted">본문용 산세리프</p>
|
||||
</header>
|
||||
<p class="specimen specimen-body">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.</p>
|
||||
</article>
|
||||
<article class="typeface typeface-title">
|
||||
<header class="typeface-head">
|
||||
<h3>제목</h3>
|
||||
<p class="muted">마루 부리 · 표제에서 멈춤과 여백을 만드는 결</p>
|
||||
<h3>MaruBuri</h3>
|
||||
<p class="muted">명조 계열 세리프</p>
|
||||
</header>
|
||||
<p class="specimen specimen-display">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.</p>
|
||||
</article>
|
||||
|
|
@ -86,10 +87,11 @@
|
|||
<p class="eyebrow">조판 시험대</p>
|
||||
<h2 id="bench-h">읽는 호흡을<br />직접 맞춰 보세요.</h2>
|
||||
</div>
|
||||
<p class="muted bench-lead">크기는 문장의 밀도를, 자간은 낱글자 사이의 공기를, 행간은 다음 줄로 넘어가는 속도를 바꿉니다. 하나씩 움직여 같은 문장이 어떻게 달라지는지 비교해 보세요.</p>
|
||||
<p class="muted bench-lead">크기·자간·행간을 바꾸며 문장 폭, 줄바꿈, 글자 형태의 차이가 어떻게 보이는지 비교해 보세요.</p>
|
||||
</div>
|
||||
<div class="bench">
|
||||
<form class="bench-controls" aria-label="조판 설정">
|
||||
<div class="control"><label for="ctl-font">글꼴</label><select id="ctl-font" name="font"><option value="display">마루부리</option><option value="body">Pretendard</option></select></div>
|
||||
<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>
|
||||
|
|
@ -97,6 +99,7 @@
|
|||
</form>
|
||||
<div class="bench-reading">
|
||||
<p id="bench-caption" class="bench-caption">마루 부리 · 17px · 자간 0em · 행간 1.8</p>
|
||||
<p class="bench-disclaimer">아래 문단은 역사 사실을 설명하는 글이 아닌 조판 견본글입니다.</p>
|
||||
<div class="bench-sample" id="bench-sample" aria-live="polite">활자의 역사는 글자를 찍는 기계의 역사가 아니라, 글자를 읽는 눈의 역사였다. 활자소의 식자공들은 하루에 만 글자를 짜면서 어느 글꼴이 눈을 덜 아프게 하는지 알고 있었다. 그 지식은 문서로 남지 않았고 손끝으로만 전해졌다. 이 미리보기는 그 판단을 눈으로 비교할 수 있게 옮긴 것입니다. 자간을 0에서 5로 바꾸면 문단의 호흡과 줄 길이가 어떻게 달라지는지 확인할 수 있습니다.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -147,20 +150,22 @@
|
|||
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 ctl = { font: document.getElementById("ctl-font"), 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 };
|
||||
const defaults = { font: "display", size: 17, tracking: 0, leading: 1.8 };
|
||||
const fontNames = { display: "마루 부리", body: "Pretendard" };
|
||||
function apply() {
|
||||
sample.style.fontFamily = ctl.font.value === "display" ? "var(--font-display)" : "var(--font-body)";
|
||||
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)}`;
|
||||
document.getElementById("bench-caption").textContent = `${fontNames[ctl.font.value]} · ${ctl.size.value}px · 자간 ${ctl.tracking.value / 100}em · 행간 ${Number(ctl.leading.value).toFixed(1)}`;
|
||||
}
|
||||
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(); });
|
||||
Object.values(ctl).forEach((input) => { input.addEventListener("input", apply); input.addEventListener("change", apply); });
|
||||
document.getElementById("ctl-reset").addEventListener("click", () => { ctl.font.value = defaults.font; ctl.size.value = defaults.size; ctl.tracking.value = defaults.tracking; ctl.leading.value = defaults.leading; apply(); });
|
||||
apply();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
15
apps/site/public/work/font-foundry/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import url("./tokens.css?v=3");
|
||||
@import url("./tokens.css?v=20260912all");
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
* { margin: 0; }
|
||||
|
|
@ -19,36 +19,41 @@ a { color: inherit; }
|
|||
.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 { display: flex; flex-wrap: wrap; 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); }
|
||||
|
||||
.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); }
|
||||
.hero { padding-block: clamp(var(--space-5), 5vw, var(--space-7)) var(--space-7); }
|
||||
.hero-rail { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: clamp(var(--space-5), 5vw, var(--space-8)); width: min(100% - 2 * var(--pad-wide), var(--container-wide)); margin-inline: auto; align-items: stretch; }
|
||||
.hero-rail > *, .hero-intro > * { min-inline-size: 0; }
|
||||
.hero-intro { display: flex; flex-direction: column; justify-content: space-between; gap: var(--space-6); min-block-size: clamp(32rem, 52vw, 44rem); padding: clamp(var(--space-5), 4vw, var(--space-7)); border: 1px solid var(--line); background: var(--surface-raised); }
|
||||
.hero h1 { margin-top: var(--space-3); font-size: clamp(2.5rem, 1.6rem + 3vw, 3.75rem); max-inline-size: 10ch; }
|
||||
.hero-note { max-inline-size: 29rem; }
|
||||
.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 { display: flex; flex-direction: column; justify-content: space-between; min-inline-size: 0; min-block-size: clamp(32rem, 52vw, 44rem); padding: clamp(var(--space-5), 4vw, var(--space-7)); background: var(--accent); color: var(--stage-ink); }
|
||||
.stage-label { color: var(--stage-ink); }
|
||||
.hero-specimen { min-inline-size: 0; margin-block: var(--space-5); font-family: var(--font-body); font-size: clamp(6rem, 11vw, 11.25rem); font-weight: 650; letter-spacing: -0.09em; line-height: 0.82; overflow-wrap: anywhere; white-space: normal; }
|
||||
.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); }
|
||||
.baseline { block-size: 1px; margin-top: var(--space-3); background: color-mix(in srgb, var(--stage-ink) 75%, transparent); }
|
||||
.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-switch { display: flex; flex-wrap: wrap; 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; }
|
||||
.type-choice { border-color: color-mix(in srgb, var(--stage-ink) 72%, transparent); color: var(--stage-ink); }
|
||||
.type-choice[aria-pressed="true"] { border-color: var(--stage-ink); background: var(--stage-ink); color: var(--accent); }
|
||||
.type-choice:focus-visible { outline-color: var(--stage-ink); }
|
||||
.type-status { color: var(--stage-ink); font-size: var(--step--1); text-align: end; }
|
||||
|
||||
.section { padding-block: var(--space-8); }
|
||||
.section[id] { scroll-margin-block-start: 5rem; }
|
||||
.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); }
|
||||
.comparison-note { max-inline-size: 42rem; margin-top: var(--space-5); color: var(--ink-muted); }
|
||||
.comparison { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-6); margin-top: var(--space-6); border-top: 1px solid var(--line); }
|
||||
.typeface { min-inline-size: 0; 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; }
|
||||
.specimen { margin-top: var(--space-5); inline-size: 100%; font-size: 1.375rem; line-height: 1.8; }
|
||||
.specimen-body { font-family: var(--font-body); }
|
||||
.specimen-display { font-family: var(--font-display); }
|
||||
|
||||
.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; }
|
||||
|
|
@ -58,10 +63,13 @@ a { color: inherit; }
|
|||
.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); }
|
||||
input[type="range"], select { inline-size: 100%; accent-color: var(--accent); }
|
||||
select { min-block-size: 2.75rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); color: var(--ink); font: inherit; padding-inline: var(--space-3); }
|
||||
.btn-reset { justify-self: start; margin-top: var(--space-2); }
|
||||
.btn-reset:hover, .type-choice:hover { border-color: var(--accent); }
|
||||
.btn-reset:hover { border-color: var(--accent); }
|
||||
.type-choice:hover { border-color: var(--stage-ink); }
|
||||
.bench-caption { margin-bottom: var(--space-3); }
|
||||
.bench-disclaimer { margin-bottom: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); }
|
||||
.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; }
|
||||
|
||||
.case-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); align-items: start; }
|
||||
|
|
@ -85,23 +93,31 @@ input[type="range"] { inline-size: 100%; accent-color: var(--accent); }
|
|||
.foot-inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3) var(--space-5); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.hero-intro, .section-head, .bench-heading, .bench, .case-grid, .order-grid { grid-template-columns: 1fr; }
|
||||
.hero-rail, .section-head, .comparison, .bench-heading, .bench, .case-grid, .order-grid { grid-template-columns: 1fr; }
|
||||
.hero-intro, .specimen-stage { min-block-size: 0; }
|
||||
.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 { 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 { justify-content: space-between; gap: var(--space-2); max-inline-size: 100%; margin-top: var(--space-1); }
|
||||
.nav a { flex: 0 0 auto; padding-block: var(--space-1); }
|
||||
.hero { padding-block: var(--space-6); }
|
||||
.hero { padding-block: var(--space-5); }
|
||||
.hero-rail { gap: var(--space-4); }
|
||||
.hero-intro, .specimen-stage { padding: var(--space-4); }
|
||||
.hero-intro :where(p, h1), .stage-label, .type-status { overflow-wrap: anywhere; }
|
||||
.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; }
|
||||
.bench-heading > * { min-inline-size: 0; }
|
||||
.bench-heading h2 { overflow-wrap: anywhere; }
|
||||
.order-row > * { min-inline-size: 0; max-inline-size: 100%; }
|
||||
.order-price { white-space: normal; overflow-wrap: anywhere; }
|
||||
.stage-footer, .order-row { align-items: flex-start; flex-direction: column; }
|
||||
.type-status { text-align: start; }
|
||||
.section { padding-block: var(--space-7); }
|
||||
.section[id] { scroll-margin-block-start: calc(7rem + 1rem); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
--line: #d9d1bf;
|
||||
--accent: #a63d2f;
|
||||
--accent-ink: #f5f1e8;
|
||||
--stage-ink: #fffaf1;
|
||||
--accent-hover: #8a3225;
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
|
|
@ -41,3 +41,10 @@
|
|||
- `prefers-reduced-motion`과 효과 제거 상태에서도 콘텐츠가 같은 순서로 읽히는지 점검한다.
|
||||
- 200% 텍스트 확대에서 본문·CTA가 사라지지 않는지 점검한다.
|
||||
- 실행 결과와 화면 캡처는 `outputs/design-expertise-20260912/form/`에 남긴다.
|
||||
|
||||
## 2026-09-12 전체 쇼케이스 점검
|
||||
|
||||
- 기존 흑백 사진·도면 구도, 앵커와 긴 모바일 라벨의 줄바꿈 규칙을 보존했다. 로컬 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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
<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=3" />
|
||||
<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" />
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
15
apps/site/public/work/form-architecture/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import url("./tokens.css?v=3");
|
||||
@import url("./tokens.css?v=20260912all");
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
* { margin: 0; }
|
||||
|
|
|
|||
24
apps/site/public/work/gaon-lms/design.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# 가온 학적부 디자인 기록
|
||||
|
||||
## 브리프
|
||||
|
||||
- 무엇을: 학원 강사가 수업·출석·과제·상담을 기록으로 확인하는 학적부형 관리 화면.
|
||||
- 누구에게: 행과 상태를 빠르게 대조하고 다음 업무를 정해야 하는 강사.
|
||||
- 제약: 예시 데이터와 기존 계산·상태·모바일 내비·대화상자·인쇄 흐름을 보존한다.
|
||||
|
||||
## 판단 기준
|
||||
|
||||
| 축 | 질문 | 적용 |
|
||||
| --- | --- | --- |
|
||||
| Warde · 정보 전달 | 다음 확인 대상이 수치보다 먼저 읽히는가 | 공통 정렬선과 가는 구분선의 요약 기록 밴드, 15px 표 본문 |
|
||||
| Ruder · 비교 변수 | 같은 종류의 값이 같은 열과 숫자 폭에서 비교되는가 | 표 숫자에 tabular figures, 수치 열 오른쪽 정렬 |
|
||||
| Albers · 상태 맥락 | 상태색이 주변의 중립 기록면에서 구분되는가 | 상태는 텍스트와 함께 유지하고 배경색은 보조로 사용 |
|
||||
|
||||
Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하는 참고 질문이며 인과나 성능 법칙이 아니다.
|
||||
|
||||
## 역할 토큰과 검증
|
||||
|
||||
- `--record-radius`, `--record-rule`, `--record-body-size`, `--record-meta-size`가 기록면·구분선·본문·보조정보 역할을 고정한다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -9,15 +9,15 @@
|
|||
<link rel="manifest" href="./manifest.webmanifest" />
|
||||
<link rel="apple-touch-icon" href="./icon-192.png" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="가온 학적부 — 학습관리 콘솔" />
|
||||
<meta property="og:title" content="가온 학적부 — 학습 관리" />
|
||||
<meta property="og:url" content="https://designpaca.chanpaca.net/work/gaon-lms/" />
|
||||
<meta property="og:image" content="https://designpaca.chanpaca.net/work/gaon-lms/og.png" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="가온 학적부 — 학습관리 콘솔" />
|
||||
<meta name="twitter:title" content="가온 학적부 — 학습 관리" />
|
||||
<meta name="twitter:image" content="https://designpaca.chanpaca.net/work/gaon-lms/og.png" />
|
||||
<meta
|
||||
name="description"
|
||||
content="학원 강사를 위한 학습관리 대시보드. 출석부, 수강생 기록, 과제 채점, 학사 일정까지 여덟 화면. 데모 데이터로 실제로 작동합니다."
|
||||
content="학원 강사를 위한 학습 관리 화면입니다. 출석, 수강생 기록, 과제 채점, 학사 일정을 한곳에서 살펴볼 수 있습니다."
|
||||
/>
|
||||
<meta property="og:title" content="가온 학적부 · 학습관리 대시보드" />
|
||||
<meta property="og:description" content="출석부에서 출발한 학습관리 대시보드." />
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Noto+Serif+KR:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=24" />
|
||||
<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 width='32' height='32' rx='7' fill='%23fafafa'/%3E%3Crect x='6.5' y='6.5' width='19' height='19' rx='4' fill='%23059669'/%3E%3Crect x='10' y='12' width='12' height='1.8' rx='0.9' fill='%23ffffff'/%3E%3Crect x='10' y='15.5' width='8' height='1.8' rx='0.9' fill='%23ffffff' opacity='0.85'/%3E%3Crect x='10' y='19' width='10' height='1.8' rx='0.9' fill='%23ffffff' opacity='0.7'/%3E%3C/svg%3E"
|
||||
|
|
@ -141,6 +141,7 @@
|
|||
<div>
|
||||
<h2 class="v-h" id="dash-h">이번 주, 열두 명 중 열 명</h2>
|
||||
<p class="view-sub">3월 9일 월요일 오전 기준. 명단 12명 중 재학 10명, 주간 출석은 지난 주(3/2–3/6) 결산입니다.</p>
|
||||
<p class="panel-note muted" role="note">표시된 수강생·수업·상담 기록은 예시입니다. 이 화면에서 바꾼 내용은 새로고침하면 초기화됩니다.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -217,6 +218,7 @@
|
|||
<span class="key"><i class="k-abs"></i>결석</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
<div class="table-wrap" tabindex="0" aria-label="요일별 출석 결산 표 가로 스크롤">
|
||||
<table class="wk-sum">
|
||||
<caption class="sr-only">요일별 출석 결산</caption>
|
||||
<thead><tr><th scope="col">요일</th><th scope="col" class="num">출석</th><th scope="col" class="num">지각</th><th scope="col" class="num">결석</th><th scope="col" class="num">출석률</th></tr></thead>
|
||||
|
|
@ -229,6 +231,7 @@
|
|||
</tbody>
|
||||
<tfoot><tr><td>합계</td><td class="num">46</td><td class="num">2</td><td class="num">2</td><td class="num"><span class="hl">92%</span></td></tr></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 마감 임박 -->
|
||||
|
|
@ -550,7 +553,7 @@
|
|||
</thead>
|
||||
<tbody id="file-body"><!-- JS --></tbody>
|
||||
</table>
|
||||
<p class="panel-note muted">다운로드 링크를 만들지 않는다 — 이 데모에는 파일이 없다. 배부 상태만 기록한다.</p>
|
||||
<p class="panel-note muted">배부 여부와 열람 현황을 확인하는 목록입니다. 원본 파일은 이 화면에서 제공하지 않습니다.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -798,7 +801,7 @@
|
|||
<script src="calc.js?v=2"></script>
|
||||
<script>
|
||||
/*
|
||||
앱 상태는 전부 이 안에 있다. 서버도 localStorage 도 없다 — 데모 원칙.
|
||||
화면 상태는 새로고침하면 초기화한다. 서버와 localStorage에는 저장하지 않는다.
|
||||
뷰 전환 · 검색/필터 · 출석 순환 · 배부 상태 · 리포트 계산까지
|
||||
네이티브 기능 위에 얹은 얇은 JS.
|
||||
*/
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 115 KiB |
|
|
@ -76,6 +76,7 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
display: grid;
|
||||
grid-template-columns: 244px minmax(0, 1fr);
|
||||
min-height: 100dvh;
|
||||
min-width: 0;
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
.topbar .brand { display: none; } /* 브랜드는 사이드바에 있다 — 모바일에서만 탑바가 이어받는다 */
|
||||
.crumb { display: flex; align-items: center; gap: var(--space-2); font-size: var(--step-0); color: var(--muted-foreground); }
|
||||
.crumb strong { color: var(--foreground); font-weight: 600; }
|
||||
.topbar-right { display: flex; align-items: center; gap: var(--space-3); }
|
||||
.topbar-right { display: flex; min-width: 0; align-items: center; gap: var(--space-3); }
|
||||
.term-badge {
|
||||
display: inline-flex; align-items: center;
|
||||
font-size: var(--step-1); font-weight: 500;
|
||||
|
|
@ -170,11 +171,11 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
/* ── 본문 ── */
|
||||
.main { min-width: 0; display: flex; flex-direction: column; }
|
||||
|
||||
.view { display: none; }
|
||||
.view { display: none; min-width: 0; }
|
||||
.view.is-on { display: block; }
|
||||
.view:focus { outline: none; }
|
||||
|
||||
.view-sub { color: var(--muted-foreground); font-size: var(--step-0); margin-top: 4px; max-width: 46em; }
|
||||
.view-sub { color: var(--muted-foreground); font-size: var(--record-meta-size); margin-top: 4px; max-width: 46em; }
|
||||
.view-head {
|
||||
display: flex; justify-content: space-between; align-items: flex-start;
|
||||
gap: var(--space-4);
|
||||
|
|
@ -193,23 +194,23 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
.panel {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
border-radius: var(--record-radius);
|
||||
box-shadow: none;
|
||||
padding: var(--space-5);
|
||||
min-width: 0;
|
||||
}
|
||||
.panel h2 { margin-bottom: var(--space-4); font-size: var(--step-2); }
|
||||
.panel-note { font-size: var(--step-1); color: var(--faint); margin-top: var(--space-3); }
|
||||
.panel-note { font-size: var(--record-meta-size); color: var(--faint); margin-top: var(--space-3); }
|
||||
|
||||
/* 통계 카드 */
|
||||
.headline { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); }
|
||||
.headline { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; background: var(--card); border: 1px solid var(--record-rule); border-radius: var(--record-radius); box-shadow: none; font-variant-numeric: tabular-nums; }
|
||||
.headline > div {
|
||||
background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm); padding: var(--space-4) var(--space-5);
|
||||
min-width: 0; padding: var(--space-4) var(--space-5) var(--space-5); background: transparent; border: 0; border-right: 1px solid var(--record-rule); border-radius: 0; box-shadow: none;
|
||||
}
|
||||
.headline dt { font-size: var(--step-0); color: var(--muted-foreground); font-weight: 500; }
|
||||
.headline > div:last-child { border-right: 0; }
|
||||
.headline dt { font-size: var(--record-meta-size); color: var(--muted-foreground); font-weight: 500; }
|
||||
.headline dd { font-size: var(--step-3); font-weight: 600; letter-spacing: -0.03em; line-height: 1.2; font-variant-numeric: tabular-nums; margin-top: 4px; }
|
||||
.headline .stat-sub { font-size: var(--step--1); color: var(--faint); margin: 6px 0 0; margin-inline-start: 0; font-variant-numeric: tabular-nums; }
|
||||
.headline .stat-sub { font-size: var(--record-meta-size); color: var(--faint); margin: 6px 0 0; margin-inline-start: 0; font-variant-numeric: tabular-nums; }
|
||||
|
||||
.dash-grid {
|
||||
display: grid;
|
||||
|
|
@ -221,15 +222,19 @@ h3 { font-size: var(--step-0); font-weight: 500; }
|
|||
.week-panel { grid-row: span 2; }
|
||||
|
||||
/* ── 표 ── */
|
||||
.table-wrap { overflow-x: auto; overflow-y: hidden; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
.table-wrap { max-width: 100%; overflow-x: auto; overflow-y: hidden; overscroll-behavior-inline: contain; scrollbar-gutter: stable both-edges; }
|
||||
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
|
||||
.table-wrap > table { min-width: 600px; }
|
||||
#stu-table, #att-table, #as-table { min-width: 760px; }
|
||||
#report-table { min-width: 680px; }
|
||||
table th, table td { overflow-wrap: normal; word-break: keep-all; }
|
||||
thead th {
|
||||
text-align: left; font-weight: 500; font-size: var(--step-1);
|
||||
text-align: left; font-weight: 600; font-size: var(--record-meta-size);
|
||||
color: var(--muted-foreground); padding: 0 var(--space-3) var(--space-3);
|
||||
border-bottom: 1px solid var(--border);
|
||||
height: 36px; white-space: nowrap;
|
||||
}
|
||||
tbody td, tbody th { padding: var(--space-3); border-bottom: 1px solid var(--border); vertical-align: middle; text-align: left; }
|
||||
tbody td, tbody th { padding: var(--space-3); border-bottom: 1px solid var(--border); vertical-align: middle; text-align: left; font-size: var(--record-body-size); line-height: 1.55; }
|
||||
tbody th { font-weight: 500; }
|
||||
tbody tr:last-child td, tbody tr:last-child th { border-bottom: 0; }
|
||||
tbody tr { transition: background-color 80ms ease; }
|
||||
|
|
@ -239,7 +244,7 @@ tbody tr:hover { background: var(--muted); }
|
|||
.row-open {
|
||||
appearance: none; border: 0; background: none; padding: 0;
|
||||
font: inherit; font-weight: 500; color: var(--foreground);
|
||||
cursor: pointer; text-align: left;
|
||||
min-height: 24px; cursor: pointer; text-align: left;
|
||||
transition: color 80ms ease;
|
||||
}
|
||||
.row-open:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
|
||||
|
|
@ -285,8 +290,8 @@ tbody tr:hover { background: var(--muted); }
|
|||
.wk-sum thead th { padding: 8px var(--space-2); }
|
||||
.wk-sum tbody td { padding: 7px var(--space-2); }
|
||||
.wk-sum tfoot td { padding: 8px var(--space-2); border-top: 1px solid var(--border-strong); font-weight: 600; }
|
||||
.wk-sum td:first-child, .wk-sum th:first-child { padding-left: 0; white-space: nowrap; }
|
||||
.wk-sum td:last-child, .wk-sum th:last-child { padding-right: 0; }
|
||||
.wk-sum td:first-child, .wk-sum th:first-child { padding-left: var(--space-2); white-space: nowrap; }
|
||||
.wk-sum td:last-child, .wk-sum th:last-child { padding-right: var(--space-2); }
|
||||
.key { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.key i { width: 8px; height: 8px; display: inline-block; border-radius: var(--radius-xs); }
|
||||
.k-att { background: var(--border-strong); }
|
||||
|
|
@ -373,7 +378,7 @@ tbody tr:hover { background: var(--muted); }
|
|||
background: none; color: var(--muted-foreground);
|
||||
border: 1px solid var(--border-strong); border-radius: var(--radius);
|
||||
height: 28px; padding: 0 10px;
|
||||
transition: all 100ms ease;
|
||||
transition: background-color 100ms ease, border-color 100ms ease, color 100ms ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.give-btn:hover { border-color: var(--foreground); color: var(--foreground); }
|
||||
|
|
@ -387,7 +392,7 @@ tbody tr:hover { background: var(--muted); }
|
|||
align-items: start;
|
||||
}
|
||||
.cal { table-layout: fixed; }
|
||||
.cal th { text-align: center; font-size: var(--step-1); height: 32px; padding: 0; }
|
||||
.cal th { text-align: center; font-size: var(--step-1); height: 32px; padding-inline: var(--space-2); }
|
||||
.cal-day {
|
||||
height: 76px; padding: var(--space-1);
|
||||
border: 1px solid var(--border);
|
||||
|
|
@ -454,6 +459,8 @@ tbody tr:hover { background: var(--muted); }
|
|||
|
||||
/* ── 모달 ── */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
width: min(94vw, 860px);
|
||||
max-height: min(86dvh, 760px);
|
||||
|
|
@ -463,8 +470,11 @@ tbody tr:hover { background: var(--muted); }
|
|||
background: var(--card);
|
||||
color: var(--foreground);
|
||||
overflow: hidden;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
html:has(dialog.modal[open]) { overflow: hidden; }
|
||||
.modal::backdrop { background: var(--backdrop-dim); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.modal[open] { animation: modal-in 200ms cubic-bezier(0.16, 1, 0.3, 1) both; }
|
||||
|
|
@ -613,6 +623,8 @@ tbody tr:hover { background: var(--muted); }
|
|||
padding-top: max(0px, var(--safe-top));
|
||||
}
|
||||
.topbar .brand { display: flex; flex: none; padding: 0; }
|
||||
.topbar-right { flex-wrap: wrap; justify-content: flex-end; gap: var(--space-1); }
|
||||
.topbar-right > * { min-width: 0; }
|
||||
.crumb { display: none; }
|
||||
.main { padding-bottom: calc(72px + var(--safe-bottom)); } /* 바텀 내비 + 홈 인디케이터 확보 */
|
||||
.dash-grid, .cal-layout, .counsel-grid { grid-template-columns: minmax(0, 1fr); }
|
||||
|
|
@ -620,7 +632,7 @@ tbody tr:hover { background: var(--muted); }
|
|||
.dist-body { grid-template-columns: minmax(0, 1fr); }
|
||||
.view-head { flex-direction: column; align-items: flex-start; gap: var(--space-2); padding: var(--space-5) var(--space-4) var(--space-4); }
|
||||
.view > *:not(.view-head) { margin-inline: var(--space-4); }
|
||||
.headline { gap: var(--space-2); }
|
||||
.headline { gap: 0; }
|
||||
.term-badge { display: none; }
|
||||
.cal-day { height: 56px; }
|
||||
}
|
||||
|
|
@ -634,24 +646,26 @@ tbody tr:hover { background: var(--muted); }
|
|||
.mcover-mark { font-size: 5rem; }
|
||||
.msheet { padding: var(--space-4); min-height: 0; }
|
||||
.headline { grid-template-columns: minmax(0, 1fr); }
|
||||
.headline > div { border-right: 0; border-bottom: 1px solid var(--record-rule); }
|
||||
.headline > div:last-child { border-bottom: 0; }
|
||||
}
|
||||
|
||||
/* ── 알림 센터 ── */
|
||||
.bell-wrap { position: relative; }
|
||||
.bell {
|
||||
position: relative; width: 32px; height: 32px;
|
||||
position: relative; width: 44px; height: 44px;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
border-radius: var(--radius); border: none; background: none; cursor: pointer;
|
||||
color: var(--muted-foreground); transition: background-color 80ms ease, color 80ms ease;
|
||||
}
|
||||
.bell:hover, .bell.is-open { background: var(--muted-2); color: var(--foreground); }
|
||||
.bell-dot {
|
||||
position: absolute; top: 4px; right: 4px;
|
||||
position: absolute; top: 8px; right: 8px;
|
||||
width: 8px; height: 8px; border-radius: var(--radius-pill);
|
||||
background: var(--danger); border: 2px solid var(--background);
|
||||
}
|
||||
.bell-menu {
|
||||
display: none; position: absolute; top: 40px; right: 0; z-index: 40;
|
||||
display: none; position: absolute; top: 52px; right: 0; z-index: 40;
|
||||
width: min(340px, calc(100vw - 32px));
|
||||
background: var(--card); border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
|
||||
|
|
@ -679,6 +693,21 @@ tbody tr:hover { background: var(--muted); }
|
|||
.ntc.is-read p { color: var(--muted-foreground); }
|
||||
.ntc time { display: block; font-size: var(--step--1); color: var(--faint); margin-top: 2px; }
|
||||
|
||||
/* 좁거나 낮은 화면에서는 종 모서리가 아니라 뷰포트가 알림판의 기준이다.
|
||||
상대 위치를 유지하면 340px 패널이 좌측으로 밀리고 긴 목록이 하단 내비 아래로 사라진다. */
|
||||
@media (max-width: 680px) {
|
||||
.bell-menu {
|
||||
position: fixed;
|
||||
top: calc(56px + var(--safe-top) + var(--space-2));
|
||||
right: max(var(--space-3), var(--safe-right));
|
||||
bottom: calc(72px + var(--safe-bottom) + var(--space-2));
|
||||
left: max(var(--space-3), var(--safe-left));
|
||||
width: auto;
|
||||
max-height: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 태그 칩 (유형·사유) ── */
|
||||
.tag {
|
||||
display: inline-flex; align-items: center; white-space: nowrap;
|
||||
|
|
@ -920,15 +949,15 @@ kbd {
|
|||
border-top: 1px solid var(--border);
|
||||
padding: 6px max(var(--space-2), var(--safe-left)) calc(6px + var(--safe-bottom)) max(var(--space-2), var(--safe-right));
|
||||
}
|
||||
@media (max-width: 900px) { .bottom-nav { display: grid; grid-auto-flow: column; } }
|
||||
@media (max-width: 900px) { .bottom-nav { display: grid; min-width: 0; grid-template-columns: repeat(5, minmax(0, 1fr)); grid-auto-flow: row; } }
|
||||
.bnav-item {
|
||||
appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
|
||||
display: grid; justify-items: center; align-content: center; gap: 3px;
|
||||
min-height: 52px; min-width: 48px; padding: 4px 2px;
|
||||
min-height: 52px; min-width: 0; padding: 4px 2px;
|
||||
border-radius: var(--radius);
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
.bnav-item span { font-size: var(--step--2); font-weight: 500; }
|
||||
.bnav-item span { max-width: 100%; font-size: var(--step--2); font-weight: 500; overflow-wrap: anywhere; }
|
||||
.bnav-item.is-current { color: var(--accent-ink); font-weight: 600; }
|
||||
.bnav-item.is-current svg { color: var(--accent); }
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,11 @@
|
|||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
|
||||
--record-radius: 8px;
|
||||
--record-rule: var(--border);
|
||||
--record-body-size: 0.9375rem;
|
||||
--record-meta-size: 0.8125rem;
|
||||
|
||||
/* 안전 영역 — viewport-fit=cover 선언 후에만 0이 아닌 값이 온다(WebKit 공식·MDN env()) */
|
||||
--safe-top: env(safe-area-inset-top, 0px);
|
||||
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 64 KiB |
31
apps/site/public/work/griddle-club/design.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# 철판클럽 — 2026-09-12 화면 보정 기록
|
||||
|
||||
## 구도·타입·색 관찰
|
||||
|
||||
- 코발트, 큰 고딕 제목, 버거 사진은 브랜드의 속도와 조리 장면을 만드는 기존 자산이다.
|
||||
- 기준 첫 화면의 파란 상품 카드는 절대 배치로 사진 프레임 밖에 걸쳐 캡션과 조합 시작 행동의 읽기 순서를 흐린다.
|
||||
- 메뉴 이미지는 이미 4:3 표면이지만 크롭 기준이 명시되지 않았고, 조합·가맹의 조건과 비용은 값보다 장식 카드처럼 보일 위험이 있다.
|
||||
|
||||
## 근거 확인 범위
|
||||
|
||||
- `outputs/full-showcase-upgrade-20260912/screenshots/griddle-club/1440x1000-first.png`
|
||||
- 소비자 홈, `franchise/`, 기존 메뉴·조합·가상 매장·가맹 조건의 ID와 스크립트
|
||||
- 가상·생성 이미지·미전송·비용 미정·실제 주문 불가 표기는 보존한다.
|
||||
|
||||
## 결정 / 기각 조건
|
||||
|
||||
- 상품·예시 가격·조합 시작 버튼은 사진 아래 정상 흐름의 코발트 shelf로 둬 캡션과 겹치지 않게 한다.
|
||||
- 메뉴 이미지는 중앙 `cover` 크롭을 통일하고, 조합기는 라벨·선택·가격 보조 정보를 같은 위계로 둔다.
|
||||
- 가맹 절차와 비용은 단계·상태·다음 확인의 원장으로 정렬한다. 수익, 기간, 비용, 운영 성과를 새로 만들거나 추정하는 변경은 기각한다.
|
||||
|
||||
## 토큰·규칙
|
||||
|
||||
- 기존 `--blue`, `--paper-*`, `--ink`, `--line`, `--font-mono`만 사용한다.
|
||||
- 상품 shelf와 제어 요소는 44px 이상의 동작 영역과 기존 focus 계약을 유지한다.
|
||||
- 320px과 200% 확대에서 `min-width: 0`, 가로 스크롤 표, normal flow로 정보를 보존한다.
|
||||
|
||||
## 2026-09-12 현재 검증 기록
|
||||
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
<title>철판클럽 가맹 안내 — 운영 조건부터 확인하기</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="가상 버거 브랜드 철판클럽의 상권, 점포, 면적, 개설 절차와 비용 미정 항목을 먼저 살펴보는 가맹 문의 쇼케이스입니다."
|
||||
content="가상 버거 브랜드 철판클럽의 상권, 점포, 면적, 개설 절차와 비용 미정 항목을 살펴보고 가맹 검토에 필요한 조건을 정리해 보세요. 실제 상담과 계약은 제공하지 않습니다."
|
||||
/>
|
||||
<meta property="og:title" content="철판클럽 가맹 안내 — 운영 조건부터" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="수익 약속 없이 상권, 점포, 주방 동선과 비용 미정 항목을 먼저 보여 주는 국내형 가맹 안내 화면."
|
||||
content="수익 약속 없이 상권, 점포, 주방 동선과 비용 미정 항목을 안내합니다."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="../og.png" />
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
<meta name="twitter:title" content="철판클럽 가맹 안내 — 운영 조건부터" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="상권, 점포, 주방 동선과 비용 미정 항목을 먼저 살펴보는 가맹 안내 쇼케이스."
|
||||
content="상권, 점포, 주방 동선과 비용 미정 항목을 살펴보는 가맹 안내."
|
||||
/>
|
||||
<meta name="twitter:image" content="../og.png" />
|
||||
<meta name="theme-color" content="#fffdf8" />
|
||||
|
|
@ -35,9 +35,9 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="../styles/tokens.css?v=1" />
|
||||
<link rel="stylesheet" href="../styles/site.css?v=1" />
|
||||
<link rel="stylesheet" href="../styles/franchise.css?v=1" />
|
||||
<link rel="stylesheet" href="../styles/tokens.css?v=20260912all" />
|
||||
<link rel="stylesheet" href="../styles/site.css?v=20260912all" />
|
||||
<link rel="stylesheet" href="../styles/franchise.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 width='32' height='32' fill='%232147e7'/%3E%3Cpath d='M6 9h20M8 16h16M10 23h12' stroke='%23fffdf8' stroke-width='3' stroke-linecap='square'/%3E%3C/svg%3E"
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
<a class="skip-link" href="#main">본문으로 건너뛰기</a>
|
||||
|
||||
<div class="truth-bar" role="note">
|
||||
<p>쇼케이스용 가상 브랜드 · 실제 가맹 모집, 상담, 계약을 제공하지 않음</p>
|
||||
<p>가상 브랜드 안내 · 실제 가맹 모집, 상담, 계약을 제공하지 않습니다.</p>
|
||||
</div>
|
||||
|
||||
<header class="site-head">
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
<a href="#process">개설 절차</a>
|
||||
<a href="#cost">비용 항목</a>
|
||||
</nav>
|
||||
<a class="head-action" href="#inquiry">문의 준비하기</a>
|
||||
<a class="head-action" href="#inquiry">조건 정리하기</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
</div>
|
||||
<div class="franchise-truth">
|
||||
<strong>먼저 확인해 주세요</strong>
|
||||
<p>실제 가맹 모집이 아닌 디자인 쇼케이스입니다. 금액, 수익, 매장 수와 개점 기간을 꾸며내지 않습니다.</p>
|
||||
<p>금액, 수익, 매장 수와 개점 기간은 확인된 정보가 없어 표시하지 않습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
<figure class="franchise-hero-media image-shell">
|
||||
|
|
@ -166,7 +166,7 @@
|
|||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div class="process-heading-copy"><p class="section-kicker">개설 절차</p><h2 id="process-heading-franchise">기간을 꾸미지 않고<br />순서만 보여 줍니다.</h2></div>
|
||||
<p class="section-note">점포, 자료, 협의와 공사 조건에 따라 기간이 달라집니다. 아래 흐름은 화면 설명용 예시이며 계약이나 개점을 보장하지 않습니다.</p>
|
||||
<p class="section-note">점포, 자료, 협의와 공사 조건에 따라 기간이 달라집니다. 아래 순서는 검토에 필요한 항목을 정리한 것이며 계약이나 개점을 보장하지 않습니다.</p>
|
||||
</div>
|
||||
<ol class="franchise-timeline">
|
||||
<li><span>01</span><div><h3>문의 준비</h3><p>희망 지역과 시작 시기를 정리합니다.</p></div><small>준비 전</small></li>
|
||||
|
|
@ -219,17 +219,17 @@
|
|||
<section class="section inquiry-section" id="inquiry" aria-labelledby="inquiry-heading">
|
||||
<div class="wrap inquiry-layout">
|
||||
<div class="inquiry-intro">
|
||||
<p class="section-kicker">문의 준비 체험</p>
|
||||
<p class="section-kicker">조건 정리</p>
|
||||
<h2 id="inquiry-heading">연락처 없이도<br />조건부터 정리하기.</h2>
|
||||
<p>실제 이름과 연락처를 받지 않습니다. 화면 안에서 상권과 점포 조건을 정리하고 전송 전 검토 상태를 체험합니다.</p>
|
||||
<p>실제 이름과 연락처를 받지 않습니다. 가상의 운영 조건을 입력하면 어떤 항목을 확인해야 하는지 순서대로 살펴볼 수 있습니다.</p>
|
||||
<div class="privacy-note" role="note">
|
||||
<strong>실제 개인정보를 입력하지 마세요.</strong>
|
||||
<p>입력 내용은 서버로 보내거나 저장하지 않습니다. 성공 화면도 실제 접수가 아닙니다.</p>
|
||||
<p>입력한 내용은 전송하거나 보관하지 않습니다. 조건을 확인해도 상담 신청은 이루어지지 않습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inquiry-panel">
|
||||
<ol class="step-indicator" aria-label="문의 준비 단계">
|
||||
<ol class="step-indicator" aria-label="조건 정리 단계">
|
||||
<li class="is-current" data-step-indicator="1" aria-current="step"><span>1</span><small>상권</small></li>
|
||||
<li data-step-indicator="2"><span>2</span><small>점포</small></li>
|
||||
<li data-step-indicator="3"><span>3</span><small>검토</small></li>
|
||||
|
|
@ -240,8 +240,8 @@
|
|||
<p class="step-count">1 / 3</p>
|
||||
<h3 id="step-one-heading">어느 지역에서 시작할까요?</h3>
|
||||
<div class="form-field">
|
||||
<label for="demo-name">예시 호칭</label>
|
||||
<input id="demo-name" name="demoName" type="text" minlength="2" autocomplete="off" placeholder="실명 대신 예시 호칭" required />
|
||||
<label for="demo-name">확인용 호칭</label>
|
||||
<input id="demo-name" name="demoName" type="text" minlength="2" autocomplete="off" placeholder="실명 대신 두 글자 이상 입력" required />
|
||||
<small>실제 이름을 입력하지 마세요.</small>
|
||||
<p class="field-error" id="demo-name-error"></p>
|
||||
</div>
|
||||
|
|
@ -306,14 +306,14 @@
|
|||
<p class="field-error" id="experience-error"></p>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>이전</button><button class="button button-primary" type="button" data-next>입력 내용 검토</button></div>
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>이전</button><button class="button button-primary" type="button" data-next>입력한 조건 검토</button></div>
|
||||
</section>
|
||||
|
||||
<section class="form-step" data-step="3" aria-labelledby="step-three-heading">
|
||||
<p class="step-count">3 / 3</p>
|
||||
<h3 id="step-three-heading">보내기 전에 확인하세요.</h3>
|
||||
<h3 id="step-three-heading">입력한 조건을 확인하세요.</h3>
|
||||
<dl class="review-list" id="review-list">
|
||||
<div><dt>예시 호칭</dt><dd data-review="demoName">—</dd></div>
|
||||
<div><dt>확인용 호칭</dt><dd data-review="demoName">—</dd></div>
|
||||
<div><dt>희망 지역</dt><dd data-review="region">—</dd></div>
|
||||
<div><dt>희망 상권</dt><dd data-review="areaKind">—</dd></div>
|
||||
<div><dt>점포 상태</dt><dd data-review="storePossession">—</dd></div>
|
||||
|
|
@ -321,18 +321,18 @@
|
|||
<div><dt>희망 시기</dt><dd data-review="openingWindow">—</dd></div>
|
||||
<div><dt>운영 경험</dt><dd data-review="experience">—</dd></div>
|
||||
</dl>
|
||||
<label class="consent-check"><input type="checkbox" id="demo-consent" required /><span>실제 문의가 아니며 개인정보를 입력하지 않았음을 확인했습니다.</span></label>
|
||||
<label class="consent-check"><input type="checkbox" id="demo-consent" required /><span>이 페이지에서는 상담을 신청할 수 없으며 개인정보를 입력하지 않았습니다.</span></label>
|
||||
<p class="field-error" id="demo-consent-error"></p>
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>수정하기</button><button class="button button-primary" type="submit" id="submit-inquiry">문의 준비 마치기</button></div>
|
||||
<div class="form-actions split-actions"><button class="button button-ghost" type="button" data-back>수정하기</button><button class="button button-primary" type="submit" id="submit-inquiry">입력한 조건 확인하기</button></div>
|
||||
<p class="submit-state" id="submit-state" role="alert"></p>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<section class="inquiry-success" id="inquiry-success" hidden aria-labelledby="success-heading" tabindex="-1">
|
||||
<span class="success-mark" aria-hidden="true">✓</span>
|
||||
<p class="section-kicker">화면 체험 완료</p>
|
||||
<h3 id="success-heading">문의 준비가 끝났어요.</h3>
|
||||
<p>제출 절차를 보여 주는 예시이며 실제 담당자에게 전송되지 않았습니다.</p>
|
||||
<p class="section-kicker">조건 확인 완료</p>
|
||||
<h3 id="success-heading">입력한 조건을 확인했습니다.</h3>
|
||||
<p>입력한 내용은 전송되거나 보관되지 않으며, 상담 신청은 이루어지지 않습니다.</p>
|
||||
<div class="success-actions"><button class="button button-ghost" type="button" id="review-again">입력 내용 다시 보기</button><button class="button button-primary" type="button" id="restart-form">처음부터 보기</button></div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -351,7 +351,7 @@
|
|||
<div class="wrap foot-grid franchise-foot-grid">
|
||||
<div><a class="brand brand-foot" href="../"><span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span><span>철판클럽</span></a><p>눌러 굽고, 바로 건넨다.</p></div>
|
||||
<div><strong>가맹 안내</strong><a href="#model">운영 방식</a><a href="#site-fit">상권과 점포</a><a href="#process">개설 절차</a><a href="#cost">비용 항목</a></div>
|
||||
<div class="foot-truth"><strong>쇼케이스 고지</strong><p>가상 브랜드이며 실제 가맹 모집, 수익 안내, 계약과 문의 전송을 제공하지 않습니다.</p><p class="mono">DESIGN SHOWCASE · 2026</p></div>
|
||||
<div class="foot-truth"><strong>가상 브랜드 안내</strong><p>실제 가맹 모집, 수익 안내, 계약과 상담 신청을 제공하지 않습니다.</p><p class="mono">GRIDDLE CLUB · 2026</p></div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
<title>철판클럽 — 눌러 굽고, 바로 건넨다</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="서울의 빠른 한 끼를 위한 가상 눌림 버거 브랜드 철판클럽의 메뉴, 조합기, 예시 매장과 운영 방식을 살펴보세요."
|
||||
content="가상 버거 브랜드 철판클럽에서 메뉴를 고르고 버거를 조합하며, 가상 매장의 이용 방식과 운영 콘셉트를 살펴보세요. 실제 주문과 결제는 제공하지 않습니다."
|
||||
/>
|
||||
<meta property="og:title" content="철판클럽 — 눌러 굽고, 바로 건넨다" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="메뉴를 고르고, 재료를 조합하고, 예시 매장 운영 상태까지 확인하는 국내형 버거 쇼케이스."
|
||||
content="메뉴를 고르고 재료를 조합하며, 가상 매장의 운영 방식을 살펴보는 버거 브랜드 콘셉트."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="./og.png" />
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
<meta name="twitter:title" content="철판클럽 — 눌러 굽고, 바로 건넨다" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="메뉴를 고르고 재료를 조합해 보는 국내형 버거 브랜드 쇼케이스."
|
||||
content="메뉴를 고르고 재료를 조합해 보는 가상 버거 브랜드 철판클럽."
|
||||
/>
|
||||
<meta name="twitter:image" content="./og.png" />
|
||||
<meta name="theme-color" content="#f4f1e9" />
|
||||
|
|
@ -35,20 +35,20 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500;600&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/tokens.css?v=1" />
|
||||
<link rel="stylesheet" href="./styles/site.css?v=1" />
|
||||
<link rel="stylesheet" href="./styles/tokens.css?v=20260912all" />
|
||||
<link rel="stylesheet" href="./styles/site.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 width='32' height='32' fill='%232147e7'/%3E%3Cpath d='M6 9h20M8 16h16M10 23h12' stroke='%23fffdf8' stroke-width='3' stroke-linecap='square'/%3E%3C/svg%3E"
|
||||
/>
|
||||
<script>document.documentElement.classList.add("is-js");</script>
|
||||
<script src="./scripts/site.js?v=1" defer></script>
|
||||
<script src="./scripts/site.js?v=2" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">본문으로 건너뛰기</a>
|
||||
|
||||
<div class="truth-bar" role="note">
|
||||
<p>쇼케이스용 가상 브랜드 · 가격과 매장 정보는 화면 동작 확인용 예시</p>
|
||||
<p>가상 브랜드 안내 · 가격과 매장 정보는 실제 판매 정보가 아닙니다.</p>
|
||||
</div>
|
||||
|
||||
<header class="site-head" id="top">
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
<section class="hero" aria-labelledby="hero-heading">
|
||||
<div class="hero-copy">
|
||||
<p class="section-kicker">서울 도심 눌림 버거</p>
|
||||
<h1 id="hero-heading">눌러 굽고,<br />바로 건넨다.</h1>
|
||||
<h1 id="hero-heading">눌러 굽고,<span class="hero-wide-space" aria-hidden="true"> </span><br />바로 건넨다.</h1>
|
||||
<p class="hero-lead">
|
||||
얇게 눌러 가장자리는 바삭하게. 주문 뒤 조립하고 포장해, 퇴근길 한 상자로 건넵니다.
|
||||
</p>
|
||||
|
|
@ -124,10 +124,10 @@
|
|||
<div class="section-head split-head">
|
||||
<div>
|
||||
<p class="section-kicker">오늘 고를 메뉴</p>
|
||||
<h2 id="menu-heading">버거가 먼저 보이게.</h2>
|
||||
<h2 id="menu-heading">메뉴를 고르고<br />내 버거를 조합해 보세요.</h2>
|
||||
</div>
|
||||
<p class="section-note">
|
||||
모든 가격은 조합기 동작을 보여 주기 위한 예시입니다. 실제 판매와 주문은 이루어지지 않습니다.
|
||||
표시된 메뉴와 가격은 브랜드 콘셉트를 설명하기 위한 값입니다. 이 페이지에서는 실제 판매와 주문을 제공하지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -162,9 +162,17 @@
|
|||
</article>
|
||||
|
||||
<article class="menu-item menu-item-graphic" data-menu-category="burger">
|
||||
<div class="ingredient-stack" aria-hidden="true">
|
||||
<span class="bun"></span><span class="pickle"></span><span class="cheese"></span><span class="patty"></span><span class="bun bottom"></span>
|
||||
<b>02</b>
|
||||
<div class="menu-photo image-shell">
|
||||
<img
|
||||
src="./assets/single-smash-generated.webp"
|
||||
width="960"
|
||||
height="720"
|
||||
alt="코발트색 트레이 위에 치즈와 피클을 넣은 싱글 스매시 버거"
|
||||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">철판 싱글 사진</span>
|
||||
<span class="menu-number">02</span>
|
||||
<span class="menu-image-note">생성 이미지</span>
|
||||
</div>
|
||||
<div class="menu-body">
|
||||
<div><p class="menu-state">가볍게</p><h3>철판 싱글</h3></div>
|
||||
|
|
@ -224,9 +232,9 @@
|
|||
<div class="section-head split-head">
|
||||
<div>
|
||||
<p class="section-kicker">버거 조합기</p>
|
||||
<h2 id="builder-heading">상자에 담길 내용을<br />먼저 정하세요.</h2>
|
||||
<h2 id="builder-heading">내 버거 조합을<br />바꿔 보세요.</h2>
|
||||
</div>
|
||||
<p class="section-note">재료 선택과 예시 총액은 실제로 바뀝니다. 주문 전송 기능은 없습니다.</p>
|
||||
<p class="section-note">아래 구성은 메뉴를 살펴보기 위한 예시입니다. 재료를 바꾸면 오른쪽 안내 카드와 예시 가격이 함께 바뀝니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="builder-layout">
|
||||
|
|
@ -288,14 +296,14 @@
|
|||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend><span>05</span> 받을 예시 매장</legend>
|
||||
<legend><span>05</span> 확인할 가상 매장</legend>
|
||||
<label class="field-label" for="builder-store">매장</label>
|
||||
<select id="builder-store" name="store">
|
||||
<option value="seongsu">성수 예시점 · 영업 중 예시</option>
|
||||
<option value="yeonnam">연남 예시점 · 영업 종료 예시</option>
|
||||
<option value="mullae">문래 예시점 · 오늘은 쉬어요</option>
|
||||
<option value="seongsu">성수점 · 영업 중</option>
|
||||
<option value="yeonnam">연남점 · 영업 종료</option>
|
||||
<option value="mullae">문래점 · 오늘 휴무</option>
|
||||
</select>
|
||||
<p class="field-help">매장과 영업 상태도 화면 동작용 예시입니다.</p>
|
||||
<p class="field-help">아래 매장과 운영 상태는 실제 방문·주문 정보가 아닙니다.</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
|
|
@ -309,27 +317,25 @@
|
|||
loading="lazy"
|
||||
/>
|
||||
<span class="image-fallback" aria-hidden="true">조합 버거 사진</span>
|
||||
<span class="summary-stamp">화면 예시</span>
|
||||
<span class="summary-stamp">가상 메뉴</span>
|
||||
</div>
|
||||
<div class="summary-body">
|
||||
<p class="section-kicker">고른 내용</p>
|
||||
<p class="section-kicker">선택 결과</p>
|
||||
<h3 id="summary-heading">철판 더블</h3>
|
||||
<p id="summary-lines">패티 두 장 / 치즈 한 장 / 양파 기본 / 피클 기본 / 소스 기본</p>
|
||||
<dl class="summary-list">
|
||||
<div><dt>받기</dt><dd id="summary-serving">단품</dd></div>
|
||||
<div><dt>예시 매장</dt><dd id="summary-store">성수 예시점</dd></div>
|
||||
<div><dt>상태</dt><dd id="summary-store-state">영업 중 예시</dd></div>
|
||||
<div><dt>가상 매장</dt><dd id="summary-store">성수점</dd></div>
|
||||
<div><dt>상태</dt><dd id="summary-store-state">영업 중</dd></div>
|
||||
</dl>
|
||||
<div class="allergen-note" id="allergen-note">
|
||||
<strong>알레르기 예시</strong>
|
||||
<p id="summary-allergens">알, 우유, 대두, 밀, 쇠고기</p>
|
||||
<small>같은 철판과 조리 도구를 함께 씁니다. 재료를 빼도 알레르기 안전을 보장하지 않습니다.</small>
|
||||
</div>
|
||||
<div class="summary-total"><span>화면 계산용 예시 총액</span><strong id="summary-total">8,900원</strong></div>
|
||||
<p class="truth-inline">실제 주문이나 결제로 전송되지 않습니다.</p>
|
||||
<button class="button button-primary button-full" type="button" id="save-builder">선택 내용 저장</button>
|
||||
<a class="button button-ghost button-full" href="#stores" id="other-store-link">다른 매장 찾기</a>
|
||||
<p class="save-status" id="save-status" role="status" aria-live="polite"></p>
|
||||
<div class="summary-total"><span>예시 총액</span><strong id="summary-total">8,900원</strong></div>
|
||||
<p class="truth-inline">이 페이지는 메뉴 조합을 살펴보는 안내 페이지이며, 주문과 결제는 진행되지 않습니다.</p>
|
||||
<a class="button button-primary button-full" href="#stores" id="check-store-link">가상 매장 안내 보기</a>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
|
@ -364,13 +370,13 @@
|
|||
<section class="section stores-section" id="stores" aria-labelledby="stores-heading">
|
||||
<div class="wrap">
|
||||
<div class="section-head split-head">
|
||||
<div><p class="section-kicker">매장 찾기</p><h2 id="stores-heading">지금 받을 수 있는지<br />먼저 확인하세요.</h2></div>
|
||||
<p class="section-note">주소, 전화, 거리와 운영 상태를 꾸며내지 않습니다. 아래 세 곳은 상태 화면을 위한 예시 매장입니다.</p>
|
||||
<div><p class="section-kicker">가상 매장 안내</p><h2 id="stores-heading">매장별 이용 방식은<br />이렇게 안내합니다.</h2></div>
|
||||
<p class="section-note">주소, 전화, 거리와 운영 상태를 임의로 만들지 않습니다. 아래 세 곳은 브랜드 콘셉트를 설명하는 가상 매장입니다.</p>
|
||||
</div>
|
||||
|
||||
<div class="store-tools">
|
||||
<div class="search-box">
|
||||
<input id="store-search" type="search" placeholder="동네 또는 매장명 검색" autocomplete="off" aria-label="예시 매장 검색" />
|
||||
<input id="store-search" type="search" placeholder="동네 또는 매장명 검색" autocomplete="off" aria-label="가상 매장 검색" />
|
||||
<button type="button" id="clear-store-search" hidden>지우기</button>
|
||||
</div>
|
||||
<div class="region-filters" role="group" aria-label="지역 고르기">
|
||||
|
|
@ -383,46 +389,46 @@
|
|||
|
||||
<div class="store-layout">
|
||||
<div class="store-list" id="store-list">
|
||||
<article class="store-card is-selected" data-store="seongsu" data-region="성동" data-search="성수 예시점 성동 성수동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-open">영업 중 예시</span><span>예시 매장</span></div>
|
||||
<h3>성수 예시점</h3>
|
||||
<article class="store-card is-selected" data-store="seongsu" data-region="성동" data-search="성수점 성동 성수동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-open">영업 중</span><span>가상 매장</span></div>
|
||||
<h3>성수점</h3>
|
||||
<p>서울 성동구 성수동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>예시 11:00–21:30</dd></div><div><dt>마지막 주문</dt><dd>예시 21:00</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 가능 예시</li><li>배달 가능 예시</li></ul>
|
||||
<button type="button" data-select-store="seongsu">이 매장으로 조합 보기</button>
|
||||
<dl><div><dt>운영</dt><dd>11:00–21:30</dd></div><div><dt>마지막 주문</dt><dd>21:00</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 가능</li><li>배달 가능</li></ul>
|
||||
<button type="button" data-select-store="seongsu">성수점으로 조합 보기</button>
|
||||
</article>
|
||||
|
||||
<article class="store-card" data-store="yeonnam" data-region="마포" data-search="연남 예시점 마포 연남동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-closed">영업 종료 예시</span><span>예시 매장</span></div>
|
||||
<h3>연남 예시점</h3>
|
||||
<article class="store-card" data-store="yeonnam" data-region="마포" data-search="연남점 마포 연남동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-closed">영업 종료</span><span>가상 매장</span></div>
|
||||
<h3>연남점</h3>
|
||||
<p>서울 마포구 연남동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>예시 11:30–20:30</dd></div><div><dt>마지막 주문</dt><dd>오늘 마감</dd></div></dl>
|
||||
<dl><div><dt>운영</dt><dd>11:30–20:30</dd></div><div><dt>마지막 주문</dt><dd>오늘 마감</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 마감</li><li>배달 마감</li></ul>
|
||||
<button type="button" data-select-store="yeonnam">종료 상태로 조합 보기</button>
|
||||
<button type="button" data-select-store="yeonnam">연남점으로 조합 보기</button>
|
||||
</article>
|
||||
|
||||
<article class="store-card" data-store="mullae" data-region="영등포" data-search="문래 예시점 영등포 문래동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-pause">오늘은 쉬어요</span><span>예시 매장</span></div>
|
||||
<h3>문래 예시점</h3>
|
||||
<article class="store-card" data-store="mullae" data-region="영등포" data-search="문래점 영등포 문래동" tabindex="-1">
|
||||
<div class="store-card-top"><span class="store-state state-pause">오늘 휴무</span><span>가상 매장</span></div>
|
||||
<h3>문래점</h3>
|
||||
<p>서울 영등포구 문래동 일대 · 상세 주소 없음</p>
|
||||
<dl><div><dt>운영</dt><dd>임시 휴무 예시</dd></div><div><dt>다음 확인</dt><dd>매장별 안내 필요</dd></div></dl>
|
||||
<dl><div><dt>운영</dt><dd>임시 휴무</dd></div><div><dt>다음 확인</dt><dd>매장별 안내 필요</dd></div></dl>
|
||||
<ul aria-label="이용 방식"><li>포장 불가</li><li>배달 불가</li></ul>
|
||||
<button type="button" data-select-store="mullae">휴무 상태로 조합 보기</button>
|
||||
<button type="button" data-select-store="mullae">문래점으로 조합 보기</button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<aside class="store-map" aria-label="예시 매장 분포 그림">
|
||||
<aside class="store-map" aria-label="가상 매장 분포 그림">
|
||||
<div class="map-grid" aria-hidden="true"></div>
|
||||
<span class="map-river" aria-hidden="true"></span>
|
||||
<button type="button" class="map-pin pin-seongsu is-active" data-map-store="seongsu" aria-label="성수 예시점 고르기"><span>성수</span></button>
|
||||
<button type="button" class="map-pin pin-yeonnam" data-map-store="yeonnam" aria-label="연남 예시점 고르기"><span>연남</span></button>
|
||||
<button type="button" class="map-pin pin-mullae" data-map-store="mullae" aria-label="문래 예시점 고르기"><span>문래</span></button>
|
||||
<p>실제 지도와 위치가 아닌 화면 설명용 도식</p>
|
||||
<button type="button" class="map-pin pin-seongsu is-active" data-map-store="seongsu" aria-label="성수점 고르기"><span>성수</span></button>
|
||||
<button type="button" class="map-pin pin-yeonnam" data-map-store="yeonnam" aria-label="연남점 고르기"><span>연남</span></button>
|
||||
<button type="button" class="map-pin pin-mullae" data-map-store="mullae" aria-label="문래점 고르기"><span>문래</span></button>
|
||||
<p>위치 안내가 아닌 지역별 이용 상태를 보여 주는 그림입니다.</p>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="empty-state store-empty" id="store-empty" hidden role="status">
|
||||
<strong>찾는 조건과 맞는 예시 매장이 없어요.</strong>
|
||||
<strong>찾는 조건과 맞는 가상 매장이 없어요.</strong>
|
||||
<p>검색어와 지역 조건을 지우고 다시 확인해 주세요.</p>
|
||||
<button type="button" id="reset-store-search">검색 조건 지우기</button>
|
||||
</div>
|
||||
|
|
@ -445,7 +451,7 @@
|
|||
<h2 id="franchise-heading">매출 약속보다<br />운영 조건부터.</h2>
|
||||
<p>상권, 점포, 면적, 조리 동선과 비용 미정 항목을 먼저 확인할 수 있게 분리했습니다.</p>
|
||||
<a class="button button-light" href="./franchise/">가맹 운영 조건 보기 <span aria-hidden="true">→</span></a>
|
||||
<small>실제 가맹 모집과 상담이 아닌 쇼케이스용 흐름입니다.</small>
|
||||
<small>실제 가맹 모집과 상담을 제공하지 않습니다.</small>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
|
@ -453,9 +459,9 @@
|
|||
<footer class="site-foot">
|
||||
<div class="wrap foot-grid">
|
||||
<div><a class="brand brand-foot" href="#top"><span class="brand-mark" aria-hidden="true"><i></i><i></i><i></i></span><span>철판클럽</span></a><p>눌러 굽고, 바로 건넨다.</p></div>
|
||||
<div><strong>살펴보기</strong><a href="#menu">메뉴</a><a href="#builder">조합기</a><a href="#stores">예시 매장</a><a href="./franchise/">가맹 안내</a></div>
|
||||
<div><strong>살펴보기</strong><a href="#menu">메뉴</a><a href="#builder">조합하기</a><a href="#stores">가상 매장 안내</a><a href="./franchise/">가맹 안내</a></div>
|
||||
<div><strong>정보 안내</strong><p>알레르기 정보도 예시입니다.</p><p>원산지와 영양 정보는 실제 제품 자료가 아닙니다.</p></div>
|
||||
<div class="foot-truth"><strong>쇼케이스 고지</strong><p>가상 브랜드이며 실제 주문, 결제, 매장 방문, 가맹 문의를 제공하지 않습니다.</p><p class="mono">DESIGN SHOWCASE · 2026</p></div>
|
||||
<div class="foot-truth"><strong>가상 브랜드 안내</strong><p>실제 주문, 결제, 매장 방문, 가맹 문의를 제공하지 않습니다.</p><p class="mono">GRIDDLE CLUB · 2026</p></div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 477 KiB After Width: | Height: | Size: 500 KiB |
|
|
@ -105,7 +105,7 @@
|
|||
clearRadioError("areaKind", "area-kind-error");
|
||||
|
||||
if (getInputValue("demoName").length < 2) {
|
||||
invalid.push(setFieldError("demo-name", "demo-name-error", "예시 호칭을 두 글자 이상 입력해 주세요."));
|
||||
invalid.push(setFieldError("demo-name", "demo-name-error", "확인용 호칭을 두 글자 이상 입력해 주세요."));
|
||||
}
|
||||
if (!getSelectValue("region")) {
|
||||
invalid.push(setFieldError("region", "region-error", "희망 지역을 골라 주세요."));
|
||||
|
|
@ -240,14 +240,14 @@
|
|||
if (!(consent instanceof HTMLInputElement) || !consent.checked) {
|
||||
consent?.setAttribute("aria-invalid", "true");
|
||||
consent?.setAttribute("aria-describedby", "demo-consent-error");
|
||||
if (consentError) consentError.textContent = "문의 체험 안내를 확인해 주세요.";
|
||||
if (consentError) consentError.textContent = "개인정보를 입력하지 않았는지 확인해 주세요.";
|
||||
consent?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (submitButton instanceof HTMLButtonElement) {
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = "화면 상태 확인 중";
|
||||
submitButton.textContent = "입력한 조건 확인 중";
|
||||
}
|
||||
if (submitState) submitState.textContent = "";
|
||||
|
||||
|
|
@ -258,7 +258,7 @@
|
|||
submitButton.textContent = "다시 시도하기";
|
||||
}
|
||||
if (submitState) {
|
||||
submitState.textContent = "지금은 문의 준비 상태를 바꾸지 못했어요. 입력 내용은 화면에 그대로 두었습니다.";
|
||||
submitState.textContent = "지금은 입력한 조건을 확인할 수 없어요. 입력한 값은 그대로 두었습니다.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@
|
|||
}
|
||||
if (submitButton instanceof HTMLButtonElement) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = "문의 준비 마치기";
|
||||
submitButton.textContent = "입력한 조건 확인하기";
|
||||
}
|
||||
}, 450);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -61,13 +61,10 @@
|
|||
const summaryStoreState = document.querySelector("#summary-store-state");
|
||||
const summaryAllergens = document.querySelector("#summary-allergens");
|
||||
const summaryTotal = document.querySelector("#summary-total");
|
||||
const saveButton = document.querySelector("#save-builder");
|
||||
const saveStatus = document.querySelector("#save-status");
|
||||
|
||||
const stores = {
|
||||
seongsu: { label: "성수 예시점", state: "영업 중 예시", available: true },
|
||||
yeonnam: { label: "연남 예시점", state: "오늘 영업 종료 예시", available: false },
|
||||
mullae: { label: "문래 예시점", state: "오늘은 쉬어요", available: false },
|
||||
seongsu: { label: "성수점", state: "영업 중" },
|
||||
yeonnam: { label: "연남점", state: "오늘 영업 종료" },
|
||||
mullae: { label: "문래점", state: "오늘 휴무" },
|
||||
};
|
||||
|
||||
const fieldValue = (name) => {
|
||||
|
|
@ -134,16 +131,6 @@
|
|||
if (summaryAllergens) summaryAllergens.textContent = allergens.join(", ");
|
||||
if (summaryTotal) summaryTotal.textContent = formatWon(builderPrice({ flavor, patty, cheese, serving }));
|
||||
|
||||
if (saveButton) {
|
||||
saveButton.disabled = soldOut || !store.available;
|
||||
if (soldOut) saveButton.textContent = "오늘 품절 예시";
|
||||
else if (!store.available) saveButton.textContent = "다른 매장을 골라 주세요";
|
||||
else saveButton.textContent = "선택 내용 저장";
|
||||
}
|
||||
if (saveStatus) {
|
||||
saveStatus.textContent = "";
|
||||
saveStatus.classList.remove("is-error");
|
||||
}
|
||||
};
|
||||
|
||||
builder?.addEventListener("change", updateBuilder);
|
||||
|
|
@ -169,20 +156,6 @@
|
|||
button.addEventListener("click", () => applyPreset(button.dataset.preset || "double"));
|
||||
});
|
||||
|
||||
saveButton?.addEventListener("click", () => {
|
||||
if (!builder || !saveStatus) return;
|
||||
const snapshot = Object.fromEntries(new FormData(builder).entries());
|
||||
try {
|
||||
if (fixture === "save-fail") throw new Error("fixture");
|
||||
sessionStorage.setItem("griddle-club-demo-selection", JSON.stringify(snapshot));
|
||||
saveStatus.classList.remove("is-error");
|
||||
saveStatus.textContent = "이 기기의 현재 탭에 선택 내용을 남겼어요. 실제 주문으로 전송되지 않습니다.";
|
||||
} catch {
|
||||
saveStatus.classList.add("is-error");
|
||||
saveStatus.textContent = "선택 내용을 남기지 못했어요. 고른 내용은 화면에 그대로 두었습니다.";
|
||||
}
|
||||
});
|
||||
|
||||
const storeCards = [...document.querySelectorAll("[data-store]")];
|
||||
const mapPins = [...document.querySelectorAll("[data-map-store]")];
|
||||
const storeSearch = document.querySelector("#store-search");
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
}
|
||||
|
||||
.franchise-hero-copy .section-kicker {
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
}
|
||||
|
||||
.franchise-hero h1 {
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
max-width: 560px;
|
||||
margin-top: 52px;
|
||||
padding: 18px 20px;
|
||||
border-left: 3px solid #8ea5ff;
|
||||
border-left: 3px solid var(--blue-soft);
|
||||
background: #2b2926;
|
||||
}
|
||||
|
||||
|
|
@ -299,9 +299,9 @@
|
|||
|
||||
.franchise-timeline li {
|
||||
display: grid;
|
||||
grid-template-columns: 56px 1fr auto;
|
||||
grid-template-columns: 4.5rem minmax(0, 1fr) minmax(9rem, auto);
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
gap: 1.5rem;
|
||||
min-height: 124px;
|
||||
padding: 22px 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
|
|
@ -325,10 +325,13 @@
|
|||
}
|
||||
|
||||
.franchise-timeline small {
|
||||
justify-self: end;
|
||||
min-width: 8rem;
|
||||
padding: 6px 9px;
|
||||
border: 1px solid var(--line);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cost-section {
|
||||
|
|
@ -337,7 +340,7 @@
|
|||
}
|
||||
|
||||
.cost-section .section-kicker {
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
}
|
||||
|
||||
.cost-head {
|
||||
|
|
@ -355,6 +358,7 @@
|
|||
.unknown-cost {
|
||||
padding: 22px;
|
||||
border: 1px solid #625f5a;
|
||||
border-left: 3px solid var(--blue-soft);
|
||||
background: #2a2825;
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +370,7 @@
|
|||
.unknown-cost strong {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
|
|
@ -390,8 +394,10 @@
|
|||
|
||||
.cost-table th,
|
||||
.cost-table td {
|
||||
padding: 17px 14px;
|
||||
padding: 1.125rem 1rem;
|
||||
border-bottom: 1px solid #4f4c47;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.55;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
|
@ -410,7 +416,7 @@
|
|||
}
|
||||
|
||||
.cost-table td:nth-child(3) {
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
|
@ -813,6 +819,12 @@
|
|||
grid-template-columns: 1.2fr 0.8fr 1.4fr;
|
||||
}
|
||||
|
||||
.cost-table th:nth-child(3),
|
||||
.cost-table td:nth-child(3) {
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.franchise-hero {
|
||||
grid-template-columns: 0.95fr 1.05fr;
|
||||
|
|
@ -977,3 +989,38 @@
|
|||
gap: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 공개 메인과 같은 초광폭 상한: 사진이 내용보다 먼저 커지지 않는다. */
|
||||
@media (min-width: 120rem) {
|
||||
.franchise-hero {
|
||||
width: min(100%, var(--hero-wide));
|
||||
min-height: clamp(43rem, 48vw, 54rem);
|
||||
grid-template-columns: minmax(0, 1fr) minmax(42rem, 1.14fr);
|
||||
margin-inline: auto;
|
||||
border-inline: 1px solid var(--ink-soft);
|
||||
}
|
||||
|
||||
.franchise-hero-copy {
|
||||
width: min(100%, var(--copy-wide));
|
||||
justify-self: end;
|
||||
padding: clamp(5.5rem, 6vw, 8rem) clamp(4rem, 5vw, 6rem);
|
||||
}
|
||||
|
||||
.franchise-hero h1 {
|
||||
max-width: 12ch;
|
||||
font-size: clamp(4.25rem, 3.2rem + 2.2vw, 5.5rem);
|
||||
}
|
||||
|
||||
.franchise-hero-media {
|
||||
width: min(100%, var(--media-wide));
|
||||
min-height: clamp(43rem, 48vw, 54rem);
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.fact-rail .wrap,
|
||||
.model-section .wrap,
|
||||
.site-fit-section .wrap,
|
||||
.franchise-process .wrap {
|
||||
max-width: var(--workbench-wide);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,17 @@ fieldset {
|
|||
line-height: 0.98;
|
||||
}
|
||||
|
||||
/* 초광폭에서 바깥 여백을 늘려도 카피 칼럼의 안쪽 폭이 사라지지 않게 한다. */
|
||||
@media (min-width: 120rem) {
|
||||
.hero {
|
||||
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-lead {
|
||||
max-width: 520px;
|
||||
margin-top: 30px;
|
||||
|
|
@ -373,8 +384,9 @@ fieldset {
|
|||
|
||||
.hero-order button {
|
||||
justify-self: start;
|
||||
margin-top: 12px;
|
||||
padding: 0 0 3px;
|
||||
min-height: 44px;
|
||||
margin: 8px -8px -4px;
|
||||
padding: 8px 8px 7px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid white;
|
||||
background: transparent;
|
||||
|
|
@ -501,7 +513,6 @@ fieldset {
|
|||
}
|
||||
|
||||
.menu-photo,
|
||||
.ingredient-stack,
|
||||
.menu-poster {
|
||||
aspect-ratio: 4 / 3;
|
||||
position: relative;
|
||||
|
|
@ -512,6 +523,7 @@ fieldset {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center center;
|
||||
}
|
||||
|
||||
.menu-number {
|
||||
|
|
@ -527,57 +539,17 @@ fieldset {
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ingredient-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
background: var(--steel);
|
||||
}
|
||||
|
||||
.ingredient-stack span {
|
||||
width: 62%;
|
||||
height: 9%;
|
||||
display: block;
|
||||
border-radius: 45% 45% 15% 15%;
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
.ingredient-stack .bun {
|
||||
height: 19%;
|
||||
background: #d89643;
|
||||
}
|
||||
|
||||
.ingredient-stack .bun.bottom {
|
||||
height: 11%;
|
||||
border-radius: 10% 10% 35% 35%;
|
||||
}
|
||||
|
||||
.ingredient-stack .pickle {
|
||||
height: 4%;
|
||||
background: #4e6726;
|
||||
}
|
||||
|
||||
.ingredient-stack .cheese {
|
||||
width: 67%;
|
||||
height: 5%;
|
||||
background: var(--cheese);
|
||||
transform: rotate(-2deg);
|
||||
}
|
||||
|
||||
.ingredient-stack .patty {
|
||||
height: 11%;
|
||||
border-radius: 30%;
|
||||
background: #4a2118;
|
||||
}
|
||||
|
||||
.ingredient-stack b {
|
||||
.menu-image-note {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 14px;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
padding: 5px 7px;
|
||||
background: rgb(255 253 248 / 94%);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.menu-poster {
|
||||
|
|
@ -697,7 +669,7 @@ fieldset {
|
|||
}
|
||||
|
||||
.builder-section > .wrap > .section-head .section-kicker {
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
}
|
||||
|
||||
.builder-summary .section-kicker {
|
||||
|
|
@ -724,6 +696,10 @@ fieldset {
|
|||
border-bottom: 1px solid #625f5a;
|
||||
}
|
||||
|
||||
.builder-form fieldset + fieldset {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.builder-form legend {
|
||||
width: 100%;
|
||||
margin-bottom: 18px;
|
||||
|
|
@ -733,7 +709,7 @@ fieldset {
|
|||
|
||||
.builder-form legend span {
|
||||
margin-right: 12px;
|
||||
color: #8ea5ff;
|
||||
color: var(--blue-soft);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
@ -776,7 +752,7 @@ fieldset {
|
|||
}
|
||||
|
||||
.choice-row input:checked + span {
|
||||
border-color: #8ea5ff;
|
||||
border-color: var(--blue-soft);
|
||||
background: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -786,7 +762,7 @@ fieldset {
|
|||
}
|
||||
|
||||
.choice-row input:focus-visible + span {
|
||||
outline: 3px solid #8ea5ff;
|
||||
outline: 3px solid var(--blue-soft);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
|
|
@ -992,18 +968,6 @@ fieldset {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
.save-status {
|
||||
min-height: 23px;
|
||||
margin-top: 10px;
|
||||
color: var(--success);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.save-status.is-error {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.process-section {
|
||||
padding-block: 0;
|
||||
background: var(--steel);
|
||||
|
|
@ -1097,7 +1061,7 @@ fieldset {
|
|||
.search-box input {
|
||||
width: 100%;
|
||||
min-height: 50px;
|
||||
padding: 8px 0;
|
||||
padding: 8px 12px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
|
|
@ -1109,7 +1073,15 @@ fieldset {
|
|||
outline: 0;
|
||||
}
|
||||
|
||||
.search-box:focus-within {
|
||||
outline: 3px solid var(--focus);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.search-box button {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
padding-inline: 10px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
|
|
@ -1255,11 +1227,12 @@ fieldset {
|
|||
}
|
||||
|
||||
.map-pin {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
position: absolute;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding-inline: 8px;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
background: var(--ink);
|
||||
|
|
@ -1269,8 +1242,8 @@ fieldset {
|
|||
|
||||
.map-pin span {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
padding: 4px 7px;
|
||||
top: 56px;
|
||||
padding: 5px 9px;
|
||||
background: white;
|
||||
color: var(--ink);
|
||||
font-size: 10px;
|
||||
|
|
@ -1779,6 +1752,69 @@ fieldset {
|
|||
}
|
||||
}
|
||||
|
||||
@media (min-width: 821px) {
|
||||
.hero {
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 2;
|
||||
}
|
||||
|
||||
.hero-media {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.hero-order {
|
||||
position: static;
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
width: auto;
|
||||
min-height: 8.75rem;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.builder-form legend,
|
||||
.field-label,
|
||||
.select-grid label {
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.choice-row label > span small,
|
||||
.menu-foot small {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.hero-order {
|
||||
position: static;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.hero-order {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.service-row {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.service-row p {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.truth-bar,
|
||||
.site-head,
|
||||
|
|
@ -1805,3 +1841,107 @@ fieldset {
|
|||
border: 1px solid #777;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 2026-08-30: 초광폭은 확장이 아니라 독립된 구성 상태다 ─────────── */
|
||||
@media (min-width: 120rem) {
|
||||
.site-head .wrap,
|
||||
.service-strip .wrap,
|
||||
.section > .wrap,
|
||||
.site-foot .wrap {
|
||||
max-width: var(--workbench-wide);
|
||||
}
|
||||
|
||||
.hero {
|
||||
width: min(100%, var(--hero-wide));
|
||||
min-height: clamp(43rem, 48vw, 54rem);
|
||||
grid-template-columns: minmax(0, 1fr) minmax(42rem, 1.14fr);
|
||||
margin-inline: auto;
|
||||
border-inline: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
width: min(100%, var(--copy-wide));
|
||||
max-width: none;
|
||||
justify-self: end;
|
||||
padding: clamp(5.5rem, 6vw, 8rem) clamp(4rem, 5vw, 6rem);
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
max-width: none;
|
||||
font-size: clamp(4.5rem, 3.6rem + 1.5vw, 5.25rem);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hero h1 br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-wide-space {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.hero-lead {
|
||||
max-width: 38rem;
|
||||
}
|
||||
|
||||
.hero-media {
|
||||
width: min(100%, var(--media-wide));
|
||||
min-height: clamp(43rem, 48vw, 54rem);
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.hero-media img {
|
||||
object-position: 52% center;
|
||||
}
|
||||
|
||||
.hero-order {
|
||||
right: clamp(1.5rem, 3vw, 3.5rem);
|
||||
width: min(21rem, calc(100% - 3rem));
|
||||
}
|
||||
|
||||
.menu-grid,
|
||||
.builder-layout,
|
||||
.store-layout {
|
||||
max-width: var(--workbench-wide);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.builder-layout {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(27rem, 34rem);
|
||||
}
|
||||
|
||||
.process-grid,
|
||||
.franchise-callout {
|
||||
width: min(100%, var(--stage-wide));
|
||||
margin-inline: auto;
|
||||
border-inline: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.process-grid {
|
||||
/* .wrap's content cap must not silently win over this named wide stage. */
|
||||
grid-template-columns: minmax(0, 0.98fr) minmax(0, 1.02fr);
|
||||
}
|
||||
|
||||
.process-section > .process-grid {
|
||||
max-width: var(--stage-wide);
|
||||
}
|
||||
|
||||
.process-copy {
|
||||
/* Reset both inherited viewport-derived side paddings: leaving the left
|
||||
value in place leaves a 12px text column at a 2560px viewport. */
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
justify-self: stretch;
|
||||
padding-inline: clamp(4rem, 5vw, 6rem);
|
||||
}
|
||||
|
||||
.process-media,
|
||||
.franchise-image {
|
||||
min-height: clamp(42rem, 43vw, 52rem);
|
||||
}
|
||||
|
||||
.franchise-copy {
|
||||
max-width: 43rem;
|
||||
padding-inline: clamp(4rem, 5vw, 6rem);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
--line-strong: #8a8379;
|
||||
--blue: #2147e7;
|
||||
--blue-dark: #1533ac;
|
||||
--blue-soft: #8ea5ff;
|
||||
--blue-pale: #dce4ff;
|
||||
--steel: #d9dcda;
|
||||
--cheese: #ffc642;
|
||||
|
|
@ -18,6 +19,12 @@
|
|||
--font-sans: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
|
||||
--wrap: 1180px;
|
||||
/* 초광폭에서도 히어로와 후속 정보 표면은 같은 폭을 쓰지 않는다. */
|
||||
--hero-wide: 2160px;
|
||||
--stage-wide: 1680px;
|
||||
--workbench-wide: 1480px;
|
||||
--copy-wide: 900px;
|
||||
--media-wide: 1120px;
|
||||
--gutter: clamp(18px, 4vw, 52px);
|
||||
--section: clamp(76px, 10vw, 144px);
|
||||
--space-1: 4px;
|
||||
|
|
@ -41,3 +48,15 @@
|
|||
--section: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 120rem) {
|
||||
:root {
|
||||
--wrap: 1320px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 150rem) {
|
||||
:root {
|
||||
--wrap: 1400px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#f3f4f3">
|
||||
<meta name="description" content="가상 공인중개 쇼케이스의 매물 사실, 표시 항목, AI 분석 근거, 변경 이력, 게시 가능 여부를 한 화면에서 검수하는 상세 관리자 운영 데모입니다.">
|
||||
<meta name="description" content="예시 매물의 표시 항목, 확인 근거와 변경 기록을 한 화면에서 살펴보고 공개 전 확인할 순서를 정리하는 운영 안내입니다.">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta property="og:title" content="한남자리 관리 데모 — 매물 검수">
|
||||
<meta property="og:description" content="예시 매물의 표시 항목, AI 근거와 게시 방어선을 살피는 운영 화면">
|
||||
<meta property="og:title" content="한남자리 운영 안내 — 매물 확인">
|
||||
<meta property="og:description" content="예시 매물의 표시 항목과 확인 근거를 살피는 운영 화면">
|
||||
<meta property="og:image" content="../og.png">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="한남자리 관리 데모 — 매물 검수">
|
||||
<meta name="twitter:title" content="한남자리 운영 안내 — 매물 확인">
|
||||
<meta name="twitter:image" content="../og.png">
|
||||
<title>한남자리 관리 데모 — 매물 검수</title>
|
||||
<title>한남자리 운영 안내 — 매물 확인</title>
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23141413'/%3E%3Ctext x='32' y='39' text-anchor='middle' font-family='Arial' font-size='19' font-weight='700' fill='%23c6a15b'%3EHJ%3C/text%3E%3C/svg%3E">
|
||||
<link rel="stylesheet" href="../styles/tokens.css?v=20260829c">
|
||||
<link rel="stylesheet" href="../styles/admin.css?v=20260829c">
|
||||
<script src="../scripts/admin.js?v=20260829c" defer></script>
|
||||
<link rel="stylesheet" href="../styles/tokens.css?v=20260912all">
|
||||
<link rel="stylesheet" href="../styles/admin.css?v=20260912all">
|
||||
<script src="../scripts/admin.js?v=20260830e" defer></script>
|
||||
</head>
|
||||
<body class="admin-body">
|
||||
<a class="skip-link" href="#admin-main">본문 바로가기</a>
|
||||
<div class="demo-strip" role="region" aria-label="쇼케이스 고지">쇼케이스 · 가상 브랜드 · 모든 사무소, 매물, 고객과 기록은 합성 예시입니다</div>
|
||||
<div class="demo-strip" role="region" aria-label="예시 자료 안내">안내 · 예시 자료 · 사무소, 매물, 고객과 기록은 실제 정보가 아니며 광고·연락·저장은 연결되지 않습니다</div>
|
||||
|
||||
<div class="admin-shell">
|
||||
<aside class="admin-sidebar" data-sidebar aria-label="관리 메뉴">
|
||||
<div class="sidebar-brand-shell">
|
||||
<a href="../" aria-label="한남자리 공개 쇼케이스로 이동"><span class="sidebar-mark">HJ</span><span><strong class="sidebar-brand">한남자리</strong><small>운영 화면 데모</small></span></a>
|
||||
<a href="../" aria-label="한남자리 공개 안내로 이동"><span class="sidebar-mark">HJ</span><span><strong class="sidebar-brand">한남자리</strong><small>매물 확인 안내</small></span></a>
|
||||
<button type="button" class="sidebar-close" data-close-sidebar aria-label="관리 메뉴 닫기">×</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -45,9 +45,9 @@
|
|||
</nav>
|
||||
|
||||
<div class="sidebar-foot">
|
||||
<span class="status-badge status-demo">데모 환경</span>
|
||||
<span class="status-badge status-demo">예시 자료</span>
|
||||
<p>실제 광고 게시와 외부 연락은 연결되지 않습니다.</p>
|
||||
<a href="../">공개 화면 보기 ↗</a>
|
||||
<a href="../">공개 안내 보기 ↗</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
|
||||
<main class="admin-main" id="admin-main">
|
||||
<header class="admin-topbar">
|
||||
<button type="button" class="mobile-sidebar-button" aria-expanded="false" data-open-sidebar><span aria-hidden="true">☰</span><span class="sr-only">관리 메뉴 열기</span></button>
|
||||
<button type="button" class="mobile-sidebar-button" aria-label="관리 메뉴 열기" aria-expanded="false" data-open-sidebar><span aria-hidden="true">☰</span></button>
|
||||
<div class="breadcrumb"><span>운영</span><span aria-hidden="true">/</span><strong>매물 검수</strong></div>
|
||||
<div class="topbar-actions">
|
||||
<button type="button" class="icon-button" aria-label="명령 메뉴 열기" data-command-button><span aria-hidden="true">⌕</span><kbd>Ctrl K</kbd></button>
|
||||
|
|
@ -67,13 +67,13 @@
|
|||
<div class="admin-content">
|
||||
<header class="page-heading">
|
||||
<div>
|
||||
<div class="heading-badges"><span class="status-badge status-demo">쇼케이스 · 가상 브랜드</span><span>자료 기준 2026.08.29</span></div>
|
||||
<div class="heading-badges"><span class="status-badge status-demo">예시 자료 · 가상 브랜드</span><span>자료 기준 2026.08.29</span></div>
|
||||
<h1>매물 검수 대기열</h1>
|
||||
<p>게시 전에 매물 사실, 표시 항목, AI 근거와 변경 이력을 한 자리에서 확인합니다.</p>
|
||||
</div>
|
||||
<div class="heading-actions">
|
||||
<button type="button" class="button button-ghost" data-open-preview>쇼케이스 미리보기</button>
|
||||
<button type="button" class="button button-primary" data-new-draft data-inspector-trigger>새 예시 초안</button>
|
||||
<button type="button" class="button button-ghost" data-open-preview>공개 안내 보기</button>
|
||||
<button type="button" class="button button-primary" data-new-draft data-inspector-trigger>새 검토 항목</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -84,48 +84,48 @@
|
|||
|
||||
<section class="queue-toolbar" aria-label="대기열 도구">
|
||||
<label class="search-field"><span class="sr-only">매물 검색</span><span aria-hidden="true">⌕</span><input type="search" placeholder="매물명, 거래 형태, 담당 검색" data-queue-search></label>
|
||||
<label><span class="sr-only">상태 필터</span><select data-status-filter><option value="all">모든 상태</option><option value="review">검수 필요</option><option value="visible">쇼케이스 공개</option><option value="paused">보류</option><option value="contracted">계약 완료</option><option value="draft">초안</option></select></label>
|
||||
<label><span class="sr-only">상태 필터</span><select data-status-filter><option value="all">모든 상태</option><option value="review">검수 필요</option><option value="visible">예시 공개</option><option value="paused">보류</option><option value="contracted">계약 완료</option><option value="draft">초안</option></select></label>
|
||||
<label><span class="sr-only">정렬 기준</span><select data-sort-select><option value="updated-desc">최근 수정순</option><option value="updated-asc">오래된 수정순</option><option value="title-asc">매물명순</option><option value="status-asc">상태순</option></select></label>
|
||||
<label class="state-simulator"><span>상태 시연</span><select data-state-simulator><option value="normal">정상</option><option value="loading">불러오는 중</option><option value="empty">빈 대기열</option><option value="error">불러오기 실패</option><option value="save-error">저장 실패</option><option value="stale">자료 오래됨</option><option value="conflict">버전 충돌</option><option value="permission">권한 제한</option></select></label>
|
||||
<select data-state-simulator hidden aria-hidden="true"><option value="normal">정상</option><option value="loading">불러오는 중</option><option value="empty">빈 대기열</option><option value="error">불러오기 실패</option><option value="save-error">저장 실패</option><option value="stale">자료 오래됨</option><option value="conflict">버전 충돌</option><option value="permission">권한 제한</option></select>
|
||||
</section>
|
||||
|
||||
<div class="admin-workbench">
|
||||
<section class="queue-panel" id="queue" aria-labelledby="queue-title" aria-busy="false" data-queue-panel>
|
||||
<header class="panel-heading">
|
||||
<div><h2 id="queue-title">현재 대기열</h2><span data-result-count>6개 예시</span></div>
|
||||
<div><h2 id="queue-title">현재 대기열</h2><span data-result-count>6건</span></div>
|
||||
<div><span class="queue-legend"><i class="legend-block" aria-hidden="true"></i>차단</span><span class="queue-legend"><i class="legend-warn" aria-hidden="true"></i>확인</span></div>
|
||||
</header>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="queue-table">
|
||||
<caption class="sr-only">쇼케이스용 예시 매물 검수 대기열</caption>
|
||||
<caption class="sr-only">예시 매물 확인 대기열</caption>
|
||||
<thead>
|
||||
<tr><th scope="col">매물</th><th scope="col">거래</th><th scope="col">예시 조건</th><th scope="col">마지막 확인</th><th scope="col">표시 검수</th><th scope="col">AI 검토</th><th scope="col">담당</th></tr>
|
||||
</thead>
|
||||
<tbody data-queue-body>
|
||||
<tr class="is-selected" data-queue-row data-id="DEMO-HJ-101" data-status="review" data-updated="2026-08-29T10:42:00+09:00" data-title="한남 생활권 정원 주거" aria-selected="true">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="한남 생활권 정원 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb"><img src="../assets/hannam-evening-640.webp" width="80" height="54" alt="서울 언덕 생활권의 주거 외관을 표현한 연출 이미지"></span><span><strong>한남 생활권 정원 주거</strong><small>DEMO-HJ-101 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>연립주택</small></td><td><strong>38억 원</strong><small>전용 118.42㎡</small></td><td><span>2026.08.27</span><small>2일 전 · 모의</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>2개 차단</span></td><td><span class="cell-status is-warn"><i aria-hidden="true"></i>검토 전</span></td><td><span class="assignee"><i>김</i>김유진</span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="한남 생활권 정원 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb"><img src="../assets/hannam-evening-640.webp" width="80" height="54" alt="서울 언덕 생활권의 주거 외관을 표현한 연출 이미지"></span><span><strong>한남 생활권 정원 주거</strong><small>자료번호 101 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>연립주택</small></td><td><strong>38억 원</strong><small>전용 118.42㎡</small></td><td><span>2026.08.27</span><small>2일 전 · 예시</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>2개 확인 필요</span></td><td><span class="cell-status is-warn"><i aria-hidden="true"></i>검토 전</span></td><td><span class="assignee"><i>김</i>김유진</span></td>
|
||||
</tr>
|
||||
<tr data-queue-row data-id="DEMO-HJ-102" data-status="visible" data-updated="2026-08-29T09:18:00+09:00" data-title="한강 가까운 복층 주거" aria-selected="false">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="한강 가까운 복층 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-plan"><i></i></span><span><strong>한강 가까운 복층 주거</strong><small>DEMO-HJ-102 · 예시 매물</small></span></span></td>
|
||||
<td><span>전세</span><small>공동주택</small></td><td><strong>전세 22억</strong><small>전용 142.80㎡</small></td><td><span>2026.08.26</span><small>3일 전 · 모의</small></td><td><span class="cell-status is-pass"><i aria-hidden="true"></i>데모 통과</span></td><td><span class="cell-status is-pass"><i aria-hidden="true"></i>수정 승인</span></td><td><span class="assignee"><i>박</i>박소연</span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="한강 가까운 복층 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-plan"><i></i></span><span><strong>한강 가까운 복층 주거</strong><small>자료번호 102 · 예시 매물</small></span></span></td>
|
||||
<td><span>전세</span><small>공동주택</small></td><td><strong>전세 22억</strong><small>전용 142.80㎡</small></td><td><span>2026.08.26</span><small>3일 전 · 예시</small></td><td><span class="cell-status is-pass"><i aria-hidden="true"></i>안내 확인</span></td><td><span class="cell-status is-pass"><i aria-hidden="true"></i>수정 확인</span></td><td><span class="assignee"><i>박</i>박소연</span></td>
|
||||
</tr>
|
||||
<tr data-queue-row data-id="DEMO-HJ-103" data-status="paused" data-updated="2026-08-28T17:25:00+09:00" data-title="조용한 골목의 테라스 주거" aria-selected="false">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="조용한 골목의 테라스 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-stale"><i></i></span><span><strong>조용한 골목의 테라스 주거</strong><small>DEMO-HJ-103 · 예시 매물</small></span></span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="조용한 골목의 테라스 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-stale"><i></i></span><span><strong>조용한 골목의 테라스 주거</strong><small>자료번호 103 · 예시 매물</small></span></span></td>
|
||||
<td><span>월세</span><small>다세대주택</small></td><td><strong>5억 · 월 1,200만</strong><small>전용 140.84㎡</small></td><td><span>2026.08.11</span><small>18일 전 · 오래됨</small></td><td><span class="cell-status is-warn"><i aria-hidden="true"></i>다시 확인</span></td><td><span class="cell-status is-stale"><i aria-hidden="true"></i>오래됨</span></td><td><span class="assignee"><i>김</i>김유진</span></td>
|
||||
</tr>
|
||||
<tr data-queue-row data-id="DEMO-HJ-104" data-status="contracted" data-updated="2026-08-28T12:40:00+09:00" data-title="언덕 위 작은 정원 주거" aria-selected="false">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="언덕 위 작은 정원 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-closed"><i></i></span><span><strong>언덕 위 작은 정원 주거</strong><small>DEMO-HJ-104 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>단독주택</small></td><td><strong>상담 종료</strong><small>공개 목록 제외</small></td><td><span>2026.08.28</span><small>1일 전 · 모의</small></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>게시 안 함</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>종료</span></td><td><span class="assignee"><i>박</i>박소연</span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="언덕 위 작은 정원 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-closed"><i></i></span><span><strong>언덕 위 작은 정원 주거</strong><small>자료번호 104 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>단독주택</small></td><td><strong>상담 종료</strong><small>공개 목록 제외</small></td><td><span>2026.08.28</span><small>1일 전 · 예시</small></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>공개 안 함</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>종료</span></td><td><span class="assignee"><i>박</i>박소연</span></td>
|
||||
</tr>
|
||||
<tr data-queue-row data-id="DEMO-HJ-105" data-status="draft" data-updated="2026-08-27T15:05:00+09:00" data-title="남산 가까운 가족 주거" aria-selected="false">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="남산 가까운 가족 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-draft"><i></i></span><span><strong>남산 가까운 가족 주거</strong><small>DEMO-HJ-105 · 예시 매물</small></span></span></td>
|
||||
<td><span>전세</span><small>공동주택</small></td><td><strong>조건 작성 중</strong><small>면적 입력됨</small></td><td><span>확인 전</span><small>모의 서류 없음</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>5개 차단</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>실행 전</span></td><td><span class="assignee"><i>최</i>최민서</span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="남산 가까운 가족 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-draft"><i></i></span><span><strong>남산 가까운 가족 주거</strong><small>자료번호 105 · 예시 매물</small></span></span></td>
|
||||
<td><span>전세</span><small>공동주택</small></td><td><strong>조건 작성 중</strong><small>면적 입력됨</small></td><td><span>확인 전</span><small>참고 자료 없음</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>5개 확인 필요</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>분석 전</span></td><td><span class="assignee"><i>최</i>최민서</span></td>
|
||||
</tr>
|
||||
<tr data-queue-row data-id="DEMO-HJ-106" data-status="review" data-updated="2026-08-26T11:20:00+09:00" data-title="독서당길 안쪽 복층 주거" aria-selected="false">
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="독서당길 안쪽 복층 주거 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-review"><i></i></span><span><strong>독서당길 안쪽 복층 주거</strong><small>DEMO-HJ-106 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>공동주택</small></td><td><strong>41억 원</strong><small>전용 151.20㎡</small></td><td><span>2026.08.25</span><small>4일 전 · 모의</small></td><td><span class="cell-status is-warn"><i aria-hidden="true"></i>동의 대기</span></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>실행 실패</span></td><td><span class="assignee"><i>최</i>최민서</span></td>
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="독서당길 안쪽 복층 주거 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-review"><i></i></span><span><strong>독서당길 안쪽 복층 주거</strong><small>자료번호 106 · 예시 매물</small></span></span></td>
|
||||
<td><span>매매</span><small>공동주택</small></td><td><strong>41억 원</strong><small>전용 151.20㎡</small></td><td><span>2026.08.25</span><small>4일 전 · 예시</small></td><td><span class="cell-status is-warn"><i aria-hidden="true"></i>동의 대기</span></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>분석 확인 필요</span></td><td><span class="assignee"><i>최</i>최민서</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -135,7 +135,7 @@
|
|||
<span></span><span></span><span></span><p>대기열을 불러오는 중입니다.</p>
|
||||
</div>
|
||||
<div class="queue-empty" hidden data-queue-empty>
|
||||
<span aria-hidden="true">○</span><strong data-empty-title>현재 조건에 맞는 예시가 없습니다.</strong><p data-empty-copy>필터를 초기화하면 전체 대기열을 다시 볼 수 있습니다.</p><button type="button" class="button button-ghost" data-reset-filter>필터 초기화</button>
|
||||
<span aria-hidden="true">○</span><strong data-empty-title>현재 조건에 맞는 항목이 없습니다.</strong><p data-empty-copy>필터를 초기화하면 전체 대기열을 다시 볼 수 있습니다.</p><button type="button" class="button button-ghost" data-reset-filter>필터 초기화</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -143,8 +143,8 @@
|
|||
|
||||
<aside class="inspector is-open" data-inspector aria-labelledby="inspector-title">
|
||||
<header class="inspector-header">
|
||||
<div><span class="status-badge status-warn" data-inspector-status>검수 필요</span><small data-inspector-id>DEMO-HJ-101 · 예시 매물</small><h2 id="inspector-title" data-inspector-title>한남 생활권 정원 주거</h2></div>
|
||||
<button type="button" class="inspector-close" data-close-inspector aria-label="조사관과 선택 행 닫기">×</button>
|
||||
<div><span class="status-badge status-warn" data-inspector-status>검수 필요</span><small data-inspector-id>자료번호 101 · 예시 매물</small><h2 id="inspector-title" data-inspector-title>한남 생활권 정원 주거</h2></div>
|
||||
<button type="button" class="inspector-close" data-close-inspector aria-label="선택한 매물 정보 닫기">×</button>
|
||||
</header>
|
||||
|
||||
<div class="inspector-media">
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
</div>
|
||||
|
||||
<div class="channel-gates" id="office-gate">
|
||||
<button type="button" class="channel-gate is-pass" data-open-preview><span><i aria-hidden="true">✓</i><strong>쇼케이스</strong></span><small>미리보기 가능</small></button>
|
||||
<button type="button" class="channel-gate is-pass" data-open-preview><span><i aria-hidden="true">✓</i><strong>공개 안내</strong></span><small>예시 자료 보기</small></button>
|
||||
<button type="button" class="channel-gate is-blocked" data-block-publish><span><i aria-hidden="true">!</i><strong>실제 광고</strong></span><small>영구 차단 · 7개 이유</small></button>
|
||||
</div>
|
||||
|
||||
|
|
@ -168,45 +168,45 @@
|
|||
|
||||
<div class="inspector-scroll">
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-basic" aria-labelledby="tab-basic" data-panel="basic">
|
||||
<div class="panel-section-heading"><div><span>기본 정보</span><h3>예시 거래 조건</h3></div><button type="button" class="text-button" data-simulate-save>수정 시연</button></div>
|
||||
<div class="panel-section-heading"><div><span>기본 정보</span><h3>표시된 거래 조건</h3></div><button type="button" class="text-button" data-simulate-save>변경 내용 보기</button></div>
|
||||
<dl class="data-grid" data-basic-grid>
|
||||
<div><dt>거래 형태</dt><dd>매매</dd></div><div><dt>예시 가격</dt><dd>38억 원</dd></div><div><dt>유형</dt><dd>연립주택</dd></div><div><dt>전용 면적</dt><dd>118.42㎡ · 35.8평</dd></div><div><dt>방과 욕실</dt><dd>방 3 · 욕실 2</dd></div><div><dt>입주 가능</dt><dd>2026.12.20 이후 협의</dd></div>
|
||||
<div><dt>거래 형태</dt><dd>매매</dd></div><div><dt>표시 가격</dt><dd>38억 원</dd></div><div><dt>유형</dt><dd>연립주택</dd></div><div><dt>전용 면적</dt><dd>118.42㎡ · 35.8평</dd></div><div><dt>방과 욕실</dt><dd>방 3 · 욕실 2</dd></div><div><dt>입주 가능</dt><dd>2026.12.20 이후 협의</dd></div>
|
||||
</dl>
|
||||
<div class="inline-notice"><span aria-hidden="true">i</span><p><strong>가상 데이터</strong> 가격과 면적은 쇼케이스 동작 검증용이며 실제 거래 조건이 아닙니다.</p></div>
|
||||
<div class="inline-notice"><span aria-hidden="true">i</span><p><strong>예시 자료</strong> 가격과 면적은 안내를 위한 값이며 실제 거래 조건이 아닙니다.</p></div>
|
||||
<div class="panel-section-heading"><div><span>담당과 상태</span><h3>현재 업무 흐름</h3></div></div>
|
||||
<ol class="state-path">
|
||||
<li class="is-done"><span></span><div><strong>초안</strong><small>2026.08.27 · 예시</small></div></li><li class="is-done"><span></span><div><strong>접수 확인</strong><small>2026.08.28 · 예시</small></div></li><li class="is-current"><span></span><div><strong>검수 필요</strong><small>현재 · 2개 차단</small></div></li><li><span></span><div><strong>게시 준비</strong><small>차단 해제 후</small></div></li>
|
||||
<li class="is-done"><span></span><div><strong>초안</strong><small>2026.08.27 · 예시</small></div></li><li class="is-done"><span></span><div><strong>접수 확인</strong><small>2026.08.28 · 예시</small></div></li><li class="is-current"><span></span><div><strong>검수 필요</strong><small>현재 · 2개 확인 필요</small></div></li><li><span></span><div><strong>게시 준비</strong><small>확인 후</small></div></li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-legal" aria-labelledby="tab-legal" data-panel="legal" hidden>
|
||||
<div class="panel-section-heading"><div><span>이중 게시 게이트</span><h3>채널마다 다른 통과 조건</h3></div><button type="button" class="text-button" data-focus-missing>첫 누락으로 이동</button></div>
|
||||
<div class="panel-section-heading"><div><span>공개 전 확인</span><h3>용도마다 다른 확인 항목</h3></div><button type="button" class="text-button" data-focus-missing>확인 필요 항목으로 이동</button></div>
|
||||
|
||||
<article class="gate-detail is-pass">
|
||||
<header><span aria-hidden="true">✓</span><div><strong>쇼케이스 미리보기</strong><small>6 / 6 통과</small></div></header>
|
||||
<ul><li>상시 데모 표지</li><li>개별 예시 매물 배지</li><li>생성 이미지 출처</li><li>실제 연락 기능 비활성</li><li>정확한 주소와 등록번호 없음</li><li>AI 데모와 사람 검토 전 표시</li></ul>
|
||||
<header><span aria-hidden="true">✓</span><div><strong>예시 자료 안내</strong><small>6개 항목 확인</small></div></header>
|
||||
<ul><li>예시 자료 안내</li><li>개별 예시 매물 표기</li><li>생성 이미지 출처</li><li>실제 연락 기능 없음</li><li>정확한 주소와 등록번호 없음</li><li>AI 결과는 참고용이며 사람 확인 전임</li></ul>
|
||||
</article>
|
||||
|
||||
<article class="gate-detail is-blocked" id="real-ad-gate">
|
||||
<header><span aria-hidden="true">!</span><div><strong>실제 광고</strong><small>영구 차단 · 가상 브랜드</small></div></header>
|
||||
<p>실제 사무소 등록정보와 중개의뢰 관계가 없으므로 이 데모에서 통과시킬 수 없습니다.</p>
|
||||
<header><span aria-hidden="true">!</span><div><strong>실제 광고</strong><small>영구 차단 · 예시 자료</small></div></header>
|
||||
<p>실제 사무소 등록정보와 중개의뢰 관계가 없어 광고에 필요한 확인을 마칠 수 없습니다.</p>
|
||||
<button type="button" class="button button-ghost button-small" data-block-publish>차단 이유 7개 보기</button>
|
||||
</article>
|
||||
|
||||
<div class="check-list" data-legal-checks>
|
||||
<div class="check-row is-pass"><span aria-hidden="true">✓</span><div><strong>소재지 · 면적 · 가격 · 유형 · 거래 형태</strong><small>쇼케이스 fixture에 구조화됨</small></div><button type="button">보기</button></div>
|
||||
<div class="check-row is-pass"><span aria-hidden="true">✓</span><div><strong>총층수 · 사용승인일 · 방 · 욕실</strong><small>모의 서류 기준 · 실제 증빙 아님</small></div><button type="button">보기</button></div>
|
||||
<div class="check-row is-pass"><span aria-hidden="true">✓</span><div><strong>소재지 · 면적 · 가격 · 유형 · 거래 형태</strong><small>예시 자료에 구조화해 표시</small></div><button type="button">보기</button></div>
|
||||
<div class="check-row is-pass"><span aria-hidden="true">✓</span><div><strong>총층수 · 사용승인일 · 방 · 욕실</strong><small>안내용 자료 기준 · 실제 증빙 아님</small></div><button type="button">보기</button></div>
|
||||
<div class="check-row is-blocked" id="missing-orientation" tabindex="-1"><span aria-hidden="true">!</span><div><strong>방향 판단 근거 미확인</strong><small>남동향 값은 있으나 현장 또는 실제 서류 근거 없음</small></div><button type="button" data-jump-field="missing-orientation">수정 위치</button></div>
|
||||
<div class="check-row is-blocked"><span aria-hidden="true">!</span><div><strong>실제 중개의뢰 관계 없음</strong><small>가상 매물이므로 실제 광고 불가</small></div><button type="button" data-block-publish>이유</button></div>
|
||||
<div class="check-row is-warn"><span aria-hidden="true">·</span><div><strong>관리비 포함·별도 항목</strong><small>모의 기준월과 항목을 표시함</small></div><button type="button">보기</button></div>
|
||||
<div class="check-row is-blocked"><span aria-hidden="true">!</span><div><strong>실제 중개의뢰 관계 없음</strong><small>예시 매물이므로 실제 광고 불가</small></div><button type="button" data-block-publish>이유</button></div>
|
||||
<div class="check-row is-warn"><span aria-hidden="true">·</span><div><strong>관리비 포함·별도 항목</strong><small>표시 기준월과 항목을 안내함</small></div><button type="button">보기</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-docs" aria-labelledby="tab-docs" data-panel="docs" hidden>
|
||||
<div class="panel-section-heading"><div><span>모의 서류</span><h3>증빙과 예시를 구분</h3></div></div>
|
||||
<div class="panel-section-heading"><div><span>참고 자료</span><h3>예시 정보와 확인 자료를 구분</h3></div></div>
|
||||
<div class="document-list">
|
||||
<article><span class="doc-icon" aria-hidden="true">▧</span><div><strong>건축물대장 형식 예시</strong><small>2026.08.27 추가 · 실제 공문서 아님</small></div><span class="status-badge status-demo">모의</span></article>
|
||||
<article><span class="doc-icon" aria-hidden="true">▧</span><div><strong>의뢰 조건 메모</strong><small>2026.08.27 추가 · 가상 입력</small></div><span class="status-badge status-demo">모의</span></article>
|
||||
<article><span class="doc-icon" aria-hidden="true">▧</span><div><strong>건축물대장 형식 안내</strong><small>2026.08.27 추가 · 실제 공문서 아님</small></div><span class="status-badge status-demo">안내용</span></article>
|
||||
<article><span class="doc-icon" aria-hidden="true">▧</span><div><strong>의뢰 조건 메모</strong><small>2026.08.27 추가 · 예시 입력</small></div><span class="status-badge status-demo">안내용</span></article>
|
||||
<article class="is-missing"><span class="doc-icon" aria-hidden="true">×</span><div><strong>실제 등기사항</strong><small>실주소와 실제 권리 자료를 연결하지 않음</small></div><span class="status-badge status-blocked">없음</span></article>
|
||||
<article class="is-missing"><span class="doc-icon" aria-hidden="true">×</span><div><strong>현장 확인 기록</strong><small>소음, 일조, 누수와 마감 상태 확인 안 됨</small></div><span class="status-badge status-warn">대기</span></article>
|
||||
</div>
|
||||
|
|
@ -214,65 +214,65 @@
|
|||
</section>
|
||||
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-ai" aria-labelledby="tab-ai" data-panel="ai" hidden>
|
||||
<div class="panel-section-heading"><div><span>조건 비교 규칙 demo-0.3</span><h3>AI 참고 분석 데모</h3></div><span class="status-badge status-warn">사람 검토 전</span></div>
|
||||
<dl class="run-meta"><div><dt>실행 번호</dt><dd>AIRUN-DEMO-0042</dd></div><div><dt>입력 버전</dt><dd>고객 3 · 매물 7</dd></div><div><dt>실행 방식</dt><dd>결정적 데모</dd></div><div><dt>공개 가능</dt><dd>아니요</dd></div></dl>
|
||||
<div class="panel-section-heading"><div><span>조건 비교</span><h3>AI 참고 결과</h3></div><span class="status-badge status-warn">사람 검토 전</span></div>
|
||||
<dl class="run-meta"><div><dt>분석 기록</dt><dd>0042</dd></div><div><dt>입력 기준</dt><dd>고객 3 · 매물 7</dd></div><div><dt>결과 성격</dt><dd>안내용 비교</dd></div><div><dt>실제 광고</dt><dd>불가</dd></div></dl>
|
||||
<div class="ai-summary"><span>현재 판단</span><strong>조건부 적합</strong><div><small>필수 충족 4 / 5</small><small>충돌 1</small><small>확인 필요 2</small></div></div>
|
||||
<div class="claim-review">
|
||||
<article class="is-fact"><header><span>사실</span><strong>예산 상한보다 2억 원 낮음</strong></header><p>가상 의뢰인 가격 입력 · 2026.08.27</p><footer><span>근거 강도 높음</span><button type="button" data-claim-action="approve">확인</button></footer></article>
|
||||
<article class="is-fact"><header><span>사실</span><strong>전용면적이 희망 범위 안</strong></header><p>쇼케이스용 모의 서류 · 실제 공문서 아님</p><footer><span>근거 강도 높음</span><button type="button" data-claim-action="approve">확인</button></footer></article>
|
||||
<article class="is-fact"><header><span>표시값</span><strong>예산 상한보다 2억 원 낮음</strong></header><p>예시 의뢰 조건 · 2026.08.27</p><footer><span>표시 자료 기준</span><button type="button" data-claim-action="approve">확인</button></footer></article>
|
||||
<article class="is-fact"><header><span>표시값</span><strong>전용면적이 희망 범위 안</strong></header><p>안내용 자료 · 실제 공문서 아님</p><footer><span>표시 자료 기준</span><button type="button" data-claim-action="approve">확인</button></footer></article>
|
||||
<article class="is-unknown"><header><span>확인 불가</span><strong>광화문 이동 시간</strong></header><p>현재 교통 자료가 연결되지 않아 추정하지 않음</p><footer><span>빈칸 유지</span><button type="button" data-claim-action="keep">유지</button></footer></article>
|
||||
<article class="is-edited"><header><span>수정 필요</span><strong><s>조용한 주거 환경입니다.</s></strong></header><p>현장 근거가 없어 “소음 수준은 현장 확인이 필요합니다”로 바꿉니다.</p><footer><span>지원되지 않은 표현</span><button type="button" data-claim-action="edit">수정 승인</button></footer></article>
|
||||
<article class="is-edited"><header><span>수정 필요</span><strong><s>조용한 주거 환경입니다.</s></strong></header><p>현장 근거가 없어 “소음 수준은 현장 확인이 필요합니다”로 안내합니다.</p><footer><span>확인 근거 없음</span><button type="button" data-claim-action="edit">수정 반영</button></footer></article>
|
||||
</div>
|
||||
<button type="button" class="button button-primary inspector-wide-action" data-review-ai>사람 검토 완료 시연</button>
|
||||
<button type="button" class="button button-primary inspector-wide-action" data-review-ai>검토 결과 보기</button>
|
||||
</section>
|
||||
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-inquiries" aria-labelledby="tab-inquiries" data-panel="inquiries" hidden>
|
||||
<div class="panel-section-heading"><div><span>고객 조건 3판</span><h3>김○○ · 예시 고객</h3></div><span class="status-badge status-demo">연락처 마스킹</span></div>
|
||||
<dl class="data-grid customer-grid"><div><dt>거래 형태</dt><dd>매매</dd></div><div><dt>예산 상한</dt><dd>40억 원 · 예시</dd></div><div><dt>전용 면적</dt><dd>110~130㎡</dd></div><div><dt>주차</dt><dd>2대 필수</dd></div><div><dt>입주 희망</dt><dd>2026.12.15까지</dd></div><div><dt>AI 비교 동의</dt><dd>예시 동의 있음</dd></div></dl>
|
||||
<div class="consent-note"><span aria-hidden="true">i</span><p>상담 동의와 AI 비교 동의는 예시입니다. 홍보 수신은 거부 상태로 두었습니다.</p></div>
|
||||
<div class="panel-section-heading"><div><span>일정</span><h3>전화 상담 · 예시</h3></div></div>
|
||||
<article class="appointment-card"><time datetime="2026-08-29T15:30:00+09:00"><strong>29</strong><span>8월 · 토</span></time><div><strong>오후 3:30~4:00</strong><p>맞춤 조건과 권리 확인 순서 · 외부 알림 없음</p><small>한국 표준시 · 데모 일정</small></div><button type="button" data-reschedule>시간 변경 시연</button></article>
|
||||
<div class="panel-section-heading"><div><span>고객 조건</span><h3>김○○ · 가명 고객</h3></div><span class="status-badge status-demo">연락처 비공개</span></div>
|
||||
<dl class="data-grid customer-grid"><div><dt>거래 형태</dt><dd>매매</dd></div><div><dt>예산 상한</dt><dd>40억 원 · 예시</dd></div><div><dt>전용 면적</dt><dd>110~130㎡</dd></div><div><dt>주차</dt><dd>2대 필수</dd></div><div><dt>입주 희망</dt><dd>2026.12.15까지</dd></div><div><dt>AI 비교 동의</dt><dd>안내용 동의 있음</dd></div></dl>
|
||||
<div class="consent-note"><span aria-hidden="true">i</span><p>상담과 AI 비교 동의는 안내를 위한 예시입니다. 홍보 수신은 거부 상태로 표시합니다.</p></div>
|
||||
<div class="panel-section-heading"><div><span>일정</span><h3>전화 상담 안내</h3></div></div>
|
||||
<article class="appointment-card"><time datetime="2026-08-29T15:30:00+09:00"><strong>29</strong><span>8월 · 토</span></time><div><strong>오후 3:30~4:00</strong><p>맞춤 조건과 권리 확인 순서 · 알림은 전송되지 않음</p><small>한국 표준시 · 안내 일정</small></div><button type="button" data-reschedule>다른 시간 보기</button></article>
|
||||
</section>
|
||||
|
||||
<section class="tab-panel" role="tabpanel" id="inspector-history" aria-labelledby="tab-history" data-panel="history" hidden>
|
||||
<div class="panel-section-heading"><div><span>수정 불가 기록</span><h3>변경 이력 8건</h3></div><button type="button" class="text-button" data-simulate-log-failure>실패 상태 보기</button></div>
|
||||
<div class="panel-section-heading"><div><span>수정 불가 기록</span><h3>변경 이력 8건</h3></div><button type="button" class="text-button" data-simulate-log-failure>기록 확인 안내</button></div>
|
||||
<ol class="audit-list">
|
||||
<li><span class="audit-dot is-blocked"></span><div><header><strong>실제 광고 게시 차단</strong><time datetime="2026-08-29T10:42:18+09:00">오늘 10:42</time></header><p>사무소 등록정보와 실제 중개의뢰 관계가 없음</p><small>김유진 · 예시 계정 · 버전 7</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>AI 주장 수정 후 승인</strong><time datetime="2026-08-29T10:31:00+09:00">오늘 10:31</time></header><p>“조용함”을 “현장 확인 필요”로 변경</p><small>김유진 · 예시 계정 · AIRUN-DEMO-0041</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>AI 분석 완료</strong><time datetime="2026-08-29T10:20:00+09:00">오늘 10:20</time></header><p>조건 비교 규칙 demo-0.3 · 입력 버전 고객 3 / 매물 7</p><small>데모 분석기</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>쇼케이스 미리보기 공개</strong><time datetime="2026-08-29T09:54:00+09:00">오늘 09:54</time></header><p>데모 고지와 이미지 출처 6개 항목 통과</p><small>박소연 · 예시 계정</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>모의 서류 추가</strong><time datetime="2026-08-28T17:22:00+09:00">어제 17:22</time></header><p>건축물대장 형식 예시 · 실제 공문서 아님</p><small>김유진 · 예시 계정</small></div></li>
|
||||
<li><span class="audit-dot is-blocked"></span><div><header><strong>실제 광고 게시 차단</strong><time datetime="2026-08-29T10:42:18+09:00">오늘 10:42</time></header><p>사무소 등록정보와 실제 중개의뢰 관계가 없음</p><small>김유진 · 예시 계정 · 자료 버전 7</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>AI 주장 수정 후 승인</strong><time datetime="2026-08-29T10:31:00+09:00">오늘 10:31</time></header><p>“조용함”을 “현장 확인 필요”로 변경</p><small>김유진 · 예시 계정 · 분석 기록 0041</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>AI 조건 비교 완료</strong><time datetime="2026-08-29T10:20:00+09:00">오늘 10:20</time></header><p>고객 조건과 매물 정보를 안내용으로 대조</p><small>안내용 비교 결과</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>공개 안내 확인</strong><time datetime="2026-08-29T09:54:00+09:00">오늘 09:54</time></header><p>예시 자료 표시와 이미지 출처 6개 항목 확인</p><small>박소연 · 예시 계정</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>참고 자료 추가</strong><time datetime="2026-08-28T17:22:00+09:00">어제 17:22</time></header><p>건축물대장 형식 안내 · 실제 공문서 아님</p><small>김유진 · 예시 계정</small></div></li>
|
||||
<li><span class="audit-dot"></span><div><header><strong>예시 가격 변경</strong><time datetime="2026-08-28T16:40:00+09:00">어제 16:40</time></header><p>40억 원 → 38억 원 · 가상 의뢰 조건 수정</p><small>김유진 · 예시 계정 · 버전 6 → 7</small></div></li>
|
||||
<li><span class="audit-dot is-warn"></span><div><header><strong>오래된 자료로 자동 보류</strong><time datetime="2026-08-28T08:00:00+09:00">어제 08:00</time></header><p>DEMO-HJ-103 · 내부 운영 기준일 경과</p><small>데모 일정 작업</small></div></li>
|
||||
<li><span class="audit-dot is-blocked"></span><div><header><strong>권한 없는 게시 시도 거부</strong><time datetime="2026-08-27T15:16:00+09:00">8월 27일</time></header><p>중개보조 예시 계정은 법정 검수 승인과 게시를 할 수 없음</p><small>권한 검사 · 요청 DEMO-77B</small></div></li>
|
||||
<li><span class="audit-dot is-warn"></span><div><header><strong>오래된 자료로 자동 보류</strong><time datetime="2026-08-28T08:00:00+09:00">어제 08:00</time></header><p>자료번호 103 · 확인 기준일 경과</p><small>안내 일정 기준</small></div></li>
|
||||
<li><span class="audit-dot is-blocked"></span><div><header><strong>권한 없는 게시 시도 거부</strong><time datetime="2026-08-27T15:16:00+09:00">8월 27일</time></header><p>중개보조 계정은 법정 검수 승인과 게시를 할 수 없음</p><small>권한 확인</small></div></li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer class="inspector-actions">
|
||||
<button type="button" class="button button-ghost" data-focus-missing>누락 항목 보기</button>
|
||||
<footer class="inspector-actions">
|
||||
<button type="button" class="button button-ghost" data-focus-missing>확인 항목 보기</button>
|
||||
<button type="button" class="button button-primary" data-block-publish>실제 광고 게시</button>
|
||||
</footer>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<section class="role-proof" id="roles" aria-labelledby="roles-title">
|
||||
<header><div><span>역할에 따라 달라지는 행동</span><h2 id="roles-title">권한 방어선</h2></div><p>버튼을 숨기는 데서 끝내지 않고 게시 시점에 역할, 게이트와 객체 버전을 다시 확인하는 모델입니다.</p></header>
|
||||
<header><div><span>역할에 따라 달라지는 행동</span><h2 id="roles-title">권한 기준</h2></div><p>게시 전에는 역할, 확인 항목과 최신 자료를 다시 검토합니다.</p></header>
|
||||
<div class="role-grid"><article><strong>사무소 관리자</strong><p>사무소 연결, 역할, 모든 게시 관리</p><small>감사 로그 수정 불가</small></article><article><strong>공인중개사</strong><p>법정 검수, AI 승인, 게시와 중단</p><small>역할 관리자 변경 불가</small></article><article><strong>중개보조 계정</strong><p>초안, 문의 정리, 일정 등록</p><small>검수 승인과 게시 불가</small></article><article><strong>콘텐츠 편집자</strong><p>설명, 이미지, 미리보기</p><small>가격, 법정 필드와 고객 정보 수정 불가</small></article></div>
|
||||
</section>
|
||||
|
||||
<section class="settings-proof" id="settings" aria-labelledby="settings-title"><h2 id="settings-title">데모 설정 원칙</h2><p>실제 연락, 저장 서버, 광고 채널과 등록정보 연결을 비활성 상태로 유지합니다.</p></section>
|
||||
<section class="settings-proof" id="settings" aria-labelledby="settings-title"><h2 id="settings-title">예시 자료 안내</h2><p>실제 연락, 저장, 광고 등록과 연결하지 않습니다.</p></section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<dialog class="admin-dialog" data-block-dialog aria-labelledby="block-dialog-title">
|
||||
<form method="dialog" class="admin-dialog-shell">
|
||||
<header><div><span class="dialog-symbol is-blocked" aria-hidden="true">!</span><div><small>PUBLIC_REAL_AD</small><h2 id="block-dialog-title">실제 광고를 게시할 수 없습니다.</h2></div></div><button type="button" data-close-dialog aria-label="게시 차단 안내 닫기">×</button></header>
|
||||
<header><div><span class="dialog-symbol is-blocked" aria-hidden="true">!</span><div><small>실제 광고 안내</small><h2 id="block-dialog-title">실제 광고를 게시할 수 없습니다.</h2></div></div><button type="button" data-close-dialog aria-label="게시 차단 안내 닫기">×</button></header>
|
||||
<div class="admin-dialog-content">
|
||||
<p>이 화면은 가상 사무소와 합성 예시 매물로 만든 쇼케이스입니다. 아래 항목은 더미값으로 채워 통과시키지 않습니다.</p>
|
||||
<p>이 화면의 사무소와 매물은 예시 자료입니다. 실제 광고에 필요한 항목은 임의의 값으로 채우지 않습니다.</p>
|
||||
<ol><li><strong>실제 사무소 명칭</strong><span>연결 안 됨</span></li><li><strong>실제 사무소 소재지</strong><span>연결 안 됨</span></li><li><strong>실제 연락처</strong><span>연결 안 됨</span></li><li><strong>실제 등록번호</strong><span>연결 안 됨</span></li><li><strong>개업공인중개사 성명</strong><span>연결 안 됨</span></li><li><strong>실제 중개의뢰 관계</strong><span>없음</span></li><li><strong>실제 거래 가능 상태</strong><span>확인 불가</span></li></ol>
|
||||
<div class="inline-notice"><span aria-hidden="true">i</span><p>쇼케이스 미리보기 채널은 데모 고지와 생성 이미지 출처를 갖춰 별도로 열 수 있습니다.</p></div>
|
||||
<div class="inline-notice"><span aria-hidden="true">i</span><p>공개 안내에는 예시 자료 표시와 생성 이미지 출처를 함께 둡니다.</p></div>
|
||||
</div>
|
||||
<footer><button type="submit" class="button button-primary" value="close">확인</button></footer>
|
||||
</form>
|
||||
|
|
@ -280,8 +280,8 @@
|
|||
|
||||
<dialog class="admin-dialog preview-dialog" data-preview-dialog aria-labelledby="preview-dialog-title">
|
||||
<form method="dialog" class="admin-dialog-shell">
|
||||
<header><div><span class="dialog-symbol is-pass" aria-hidden="true">✓</span><div><small>SHOWCASE</small><h2 id="preview-dialog-title">미리보기 채널은 열 수 있습니다.</h2></div></div><button type="button" data-close-dialog aria-label="쇼케이스 미리보기 안내 닫기">×</button></header>
|
||||
<div class="admin-dialog-content"><p>상시 데모 고지, 개별 예시 배지, 생성 이미지 출처, 연락 기능 비활성, 실제 주소와 등록번호 없음, AI 데모 표시를 확인했습니다.</p><a class="button button-primary" href="../#listing-detail">공개 상세 보기</a></div>
|
||||
<header><div><span class="dialog-symbol is-pass" aria-hidden="true">✓</span><div><small>공개 안내</small><h2 id="preview-dialog-title">예시 자료는 공개 화면에서 볼 수 있습니다.</h2></div></div><button type="button" data-close-dialog aria-label="공개 안내 닫기">×</button></header>
|
||||
<div class="admin-dialog-content"><p>예시 자료 표시, 개별 예시 매물 표기, 생성 이미지 출처, 실제 연락 기능 없음, 실제 주소와 등록번호 없음, AI 결과는 참고용이라는 안내를 확인했습니다.</p><a class="button button-primary" href="../#dossier">공개 상세 보기</a></div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
|
|
|
|||
24
apps/site/public/work/hannam-jari/design.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# 한남자리 디자인 기록
|
||||
|
||||
## 브리프
|
||||
|
||||
- 무엇을: 다섯 조건으로 예시 주거 후보를 비교하고 공개 전 확인 책임을 분리해 보여 주는 브리핑과 운영 화면.
|
||||
- 누구에게: 조건을 정리하는 이용자와 검수 상태를 확인하는 운영 담당자.
|
||||
- 제약: 가상 매물·AI 참고·생성 이미지·미전송 안내와 기존 선택·검수·이력·대화상자 동작을 보존한다.
|
||||
|
||||
## 판단 기준
|
||||
|
||||
| 축 | 질문 | 적용 |
|
||||
| --- | --- | --- |
|
||||
| Warde · 정보 전달 | 다섯 조건의 라벨·입력값·결과 행동이 한 번에 읽히는가 | 13px 이상 라벨, 16px 입력, 결과 행동을 같은 조건판에 유지 |
|
||||
| Ruder · 비교 변수 | 후보 조건의 라벨과 값을 섞지 않고 비교하는가 | 조건 행의 라벨·값·보조 메모를 세 단계로 분리 |
|
||||
| Albers · 상태 맥락 | 가상 자료·AI 참고·중개사 확인의 책임이 구분되는가 | 상태 텍스트와 색조를 함께 사용하고 검수판의 간격·정렬을 통일 |
|
||||
|
||||
Warde, Ruder, Albers는 정보 전달·비교 변수·상태 맥락을 검토하는 참고 질문이며 인과나 성능 법칙이 아니다.
|
||||
|
||||
## 역할 토큰과 검증
|
||||
|
||||
- `--brief-label-size`, `--brief-input-size`, `--brief-panel-radius`, `--brief-rule`, `--admin-body-size`, `--admin-meta-size`가 공개·운영 표면의 역할을 관리한다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#f3f0e8">
|
||||
<meta name="description" content="서울 한남 생활권의 집을 예산, 면적, 입주, 주차 조건으로 비교하고 AI 브리핑 뒤 공인중개사가 사실을 다시 확인하는 스마트 중개 디자인 쇼케이스입니다.">
|
||||
<meta name="description" content="서울 한남 생활권의 집을 예산, 면적, 입주, 주차 조건으로 비교하고 계약 전 공인중개사가 사실을 다시 확인하는 주거 비교 안내입니다.">
|
||||
<meta property="og:title" content="한남자리 — 내 기준에 맞는 집부터">
|
||||
<meta property="og:description" content="조건을 정리하고 후보를 비교한 뒤 공인중개사가 확인하는 개인 주거 브리핑 데스크">
|
||||
<meta property="og:image" content="./og.png">
|
||||
|
|
@ -15,28 +15,27 @@
|
|||
<title>한남자리 — 내 기준에 맞는 집부터</title>
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%23121311'/%3E%3Ctext x='32' y='39' text-anchor='middle' font-family='Arial' font-size='19' font-weight='700' fill='%23f3f0e8'%3EHJ%3C/text%3E%3C/svg%3E">
|
||||
<link rel="preload" href="./assets/hannam-evening-1600.webp" as="image" type="image/webp" media="(min-width: 961px)">
|
||||
<link rel="stylesheet" href="./styles/tokens.css?v=20260829d">
|
||||
<link rel="stylesheet" href="./styles/landing.css?v=20260829d">
|
||||
<script src="./scripts/landing.js?v=20260829d" defer></script>
|
||||
<link rel="stylesheet" href="./styles/tokens.css?v=20260912all">
|
||||
<link rel="stylesheet" href="./styles/landing.css?v=20260912all">
|
||||
<script src="./scripts/landing.js?v=20260830e" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main-content">본문 바로가기</a>
|
||||
<div class="demo-strip" role="region" aria-label="쇼케이스 고지">디자인 쇼케이스 · 가상 브랜드 · 상담은 접수되지 않습니다</div>
|
||||
<div class="demo-strip" role="region" aria-label="예시 자료 안내">안내 · 예시 자료 · 입력 내용은 전송되지 않으며 상담은 접수되지 않습니다</div>
|
||||
|
||||
<header class="site-header" data-site-header>
|
||||
<a class="brand-link" href="#top" aria-label="한남자리 처음으로">
|
||||
<span class="brand-mark" aria-hidden="true">HJ</span>
|
||||
<span class="brand-lockup"><strong class="brand">한남자리</strong><small>개인 주거 브리핑 데스크</small></span>
|
||||
</a>
|
||||
<button class="menu-button" type="button" aria-expanded="false" aria-controls="site-nav" data-menu-button>
|
||||
<span class="sr-only">메뉴 열기</span><i aria-hidden="true"></i><i aria-hidden="true"></i>
|
||||
<button class="menu-button" type="button" aria-label="메뉴 열기" aria-expanded="false" aria-controls="site-nav" data-menu-button>
|
||||
<i aria-hidden="true"></i><i aria-hidden="true"></i>
|
||||
</button>
|
||||
<nav class="site-nav" id="site-nav" aria-label="주요 메뉴" data-site-nav>
|
||||
<a href="#briefing">맞춤 브리핑</a>
|
||||
<a href="#homes">세 집 비교</a>
|
||||
<a href="#dossier">선택 서류철</a>
|
||||
<a href="#handoff">중개사 인계</a>
|
||||
<a class="nav-admin" href="./admin/">관리 화면</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
|
@ -45,7 +44,7 @@
|
|||
<div class="masthead">
|
||||
<p class="eyebrow">한남 생활권 맞춤 중개</p>
|
||||
<h1 id="hero-title">한남에서, 내 기준에 맞는 집부터.</h1>
|
||||
<p class="hero-copy">예산과 면적, 입주 계획을 먼저 정리합니다. AI가 후보를 비교하고 공인중개사가 다시 확인합니다.</p>
|
||||
<p class="hero-copy">예산과 면적, 입주 계획을 먼저 정리합니다. 입력한 기준을 후보와 대조하고, 계약 전에는 공인중개사가 다시 확인합니다.</p>
|
||||
<div class="hero-actions">
|
||||
<button class="button button-primary" type="button" data-focus-condition>내 조건 분석하기</button>
|
||||
<a class="text-link" href="#homes">매물 보기 <span aria-hidden="true">→</span></a>
|
||||
|
|
@ -73,8 +72,8 @@
|
|||
<label class="dock-field"><span>최소 전용면적</span><span class="input-suffix"><input name="minArea" type="number" min="30" max="300" step="1" value="110" inputmode="decimal"><i>㎡</i></span></label>
|
||||
<label class="dock-field"><span>입주 희망일</span><input name="moveIn" type="date" value="2026-12-20"></label>
|
||||
<label class="dock-field"><span>필요 주차</span><select name="parking"><option value="0">상관없음</option><option value="1">1대 이상</option><option value="2" selected>2대 이상</option><option value="3">3대 이상</option></select></label>
|
||||
<div class="dock-actions"><button class="button button-primary" type="submit">분석 결과 보기</button><button class="save-button" type="button" data-save-condition>이 브라우저에 저장</button></div>
|
||||
<p class="condition-note" id="condition-note">입력은 전송되지 않으며, 저장을 선택하면 이 브라우저에만 보관됩니다.</p>
|
||||
<div class="dock-actions"><button class="button button-primary" type="submit">분석 결과 보기</button><button class="save-button" type="button" data-save-condition>조건 임시 보관</button></div>
|
||||
<p class="condition-note" id="condition-note">입력은 전송되지 않으며, 임시 보관을 선택하면 지금 사용 중인 기기에만 남습니다.</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
|
|
@ -82,11 +81,11 @@
|
|||
<header class="section-heading briefing-heading">
|
||||
<p class="eyebrow">내 조건 브리핑</p>
|
||||
<h2 id="briefing-title">맞는 이유와<br>조정할 지점을 나눕니다.</h2>
|
||||
<p>화면에 입력한 값과 후보 정보를 이 브라우저에서 대조합니다.</p>
|
||||
<p>입력한 기준과 후보 정보를 같은 항목으로 비교합니다.</p>
|
||||
</header>
|
||||
|
||||
<div class="briefing-sheet" data-analysis-result aria-live="polite" aria-atomic="false">
|
||||
<header><span>예시 AI 브리핑 · 모의 조건 비교 · 공인중개사 확인 전</span><strong data-analysis-home>아직 분석하지 않았습니다</strong></header>
|
||||
<header><span>안내용 조건 비교 · 공인중개사 확인 전</span><strong data-analysis-home>아직 분석하지 않았습니다</strong></header>
|
||||
<div class="briefing-empty" data-analysis-empty>
|
||||
<span aria-hidden="true">01</span>
|
||||
<div><h3>내 조건을 먼저 알려 주세요.</h3><p>분석을 실행하면 세 후보 가운데 조건과 가까운 집을 고르고, 이유를 세 묶음으로 정리합니다.</p></div>
|
||||
|
|
@ -195,19 +194,19 @@
|
|||
<li><span>02</span><div><strong>후보 비교</strong><p>서로 다른 집의 차이를 같은 기준으로 대조합니다.</p></div></li>
|
||||
<li><span>03</span><div><strong>권리·현장 확인</strong><p>서류와 현장에서 확인한 뒤 최종 판단합니다.</p></div></li>
|
||||
</ol>
|
||||
<button class="button button-light handoff-cta" type="button" data-open-consult>상담 흐름 미리보기</button>
|
||||
<button class="button button-light handoff-cta" type="button" data-open-consult>상담 전 확인할 항목 보기</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div><strong>한남자리</strong><span>개인 주거 브리핑 데스크</span></div>
|
||||
<p>한남자리는 가상 브랜드이며 등록 공인중개사무소나 거래 서비스를 제공하지 않습니다.</p>
|
||||
<nav aria-label="보조 메뉴"><a href="#top">처음으로</a><a href="./admin/">관리 화면</a></nav>
|
||||
<p>한남자리는 예시 자료로 구성한 서비스 화면입니다. 등록 공인중개사무소나 거래 서비스를 제공하지 않습니다.</p>
|
||||
<nav aria-label="보조 메뉴"><a href="#top">처음으로</a></nav>
|
||||
</footer>
|
||||
|
||||
<dialog class="consult-dialog" data-consult-dialog aria-labelledby="consult-title">
|
||||
<form method="dialog" class="dialog-shell" data-consult-form>
|
||||
<header><div><span>상담 순서 살펴보기</span><h2 id="consult-title">중개사에게 무엇을 넘길까요?</h2></div><button class="dialog-close" type="button" aria-label="상담 흐름 닫기" data-close-consult>×</button></header>
|
||||
<header><div><span>상담 전 확인 항목</span><h2 id="consult-title">중개사와 무엇을 먼저 확인할까요?</h2></div><button class="dialog-close" type="button" aria-label="상담 안내 닫기" data-close-consult>×</button></header>
|
||||
<p class="dialog-privacy">연락처를 받거나 입력 내용을 저장하지 않습니다.</p>
|
||||
<div class="dialog-content" data-consult-content>
|
||||
<p><strong data-consult-home>정원형 연립주택</strong>을 기준으로 준비할 항목을 골라 보세요.</p>
|
||||
|
|
@ -220,6 +219,6 @@
|
|||
</dialog>
|
||||
|
||||
<div class="live-region" role="status" aria-live="polite" data-live-region hidden></div>
|
||||
<noscript><div class="no-script"><strong>자바스크립트 없이 보는 중입니다.</strong><p>집 정보는 읽을 수 있지만 조건 비교, 후보 변경과 상담 미리보기는 작동하지 않습니다.</p></div></noscript>
|
||||
<noscript><div class="no-script"><strong>자바스크립트 없이 보는 중입니다.</strong><p>집 정보는 읽을 수 있지만 조건 비교, 후보 변경과 상담 준비 순서는 이용할 수 없습니다.</p></div></noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 292 KiB |
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"DEMO-HJ-102": {
|
||||
title: "한강 가까운 복층 주거",
|
||||
status: "쇼케이스 공개",
|
||||
status: "예시 공개",
|
||||
statusClass: "status-pass",
|
||||
trade: "전세",
|
||||
type: "공동주택",
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
moveIn: "2026.11.30 이후 협의",
|
||||
image: "../assets/hannam-evening-640.webp",
|
||||
imageAlt: "서울 언덕길의 석재 주거 건물 연출 이미지",
|
||||
blockers: "데모 통과",
|
||||
blockers: "안내 확인",
|
||||
ai: "수정 후 승인"
|
||||
},
|
||||
"DEMO-HJ-103": {
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
trade: "매매",
|
||||
type: "단독주택",
|
||||
price: "상담 종료",
|
||||
area: "예시 공개 종료",
|
||||
area: "목록에서 제외",
|
||||
rooms: "공개 목록 제외",
|
||||
moveIn: "해당 없음",
|
||||
image: "../assets/hannam-evening-640.webp",
|
||||
|
|
@ -162,7 +162,7 @@
|
|||
if (!inspector) return;
|
||||
inspector.classList.add("is-open");
|
||||
workbench?.classList.remove("is-inspector-closed");
|
||||
$("[data-inspector-id]", inspector).textContent = `${id} · 예시 매물`;
|
||||
$("[data-inspector-id]", inspector).textContent = `자료번호 ${id.replace("DEMO-HJ-", "")} · 예시 매물`;
|
||||
$("[data-inspector-title]", inspector).textContent = detail.title;
|
||||
const status = $("[data-inspector-status]", inspector);
|
||||
status.textContent = detail.status;
|
||||
|
|
@ -180,7 +180,7 @@
|
|||
if (basicGrid) {
|
||||
const pairs = [
|
||||
["거래 형태", detail.trade],
|
||||
["예시 가격", detail.price],
|
||||
["표시 가격", detail.price],
|
||||
["유형", detail.type],
|
||||
["전용 면적", detail.area],
|
||||
["방과 욕실", detail.rooms],
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
|
||||
updateInspectorBackdrop();
|
||||
if (focus) $("[data-close-inspector]", inspector)?.focus();
|
||||
announce(`${detail.title} 조사관을 열었습니다. ${detail.status} 상태입니다.`);
|
||||
announce(`${detail.title} 상세 정보를 열었습니다. ${detail.status} 상태입니다.`);
|
||||
};
|
||||
|
||||
const closeInspector = ({ restoreFocus = false } = {}) => {
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
if (restoreFocus && previousId) {
|
||||
$(`[data-queue-row][data-id="${previousId}"] [data-select-row]`)?.focus();
|
||||
}
|
||||
announce("조사관과 선택 행을 함께 닫았습니다.");
|
||||
announce("선택한 매물 정보를 닫았습니다.");
|
||||
};
|
||||
|
||||
const activateTab = (name, focus = false) => {
|
||||
|
|
@ -258,7 +258,7 @@
|
|||
const updateResultCount = () => {
|
||||
const visible = getRows().filter((row) => !row.classList.contains("is-hidden"));
|
||||
const result = $("[data-result-count]");
|
||||
if (result) result.textContent = `${visible.length}개 예시`;
|
||||
if (result) result.textContent = `${visible.length}건`;
|
||||
const empty = $("[data-queue-empty]");
|
||||
if (state.mode === "normal") empty.hidden = visible.length !== 0;
|
||||
return visible.length;
|
||||
|
|
@ -276,7 +276,7 @@
|
|||
const emptyTitle = $("[data-empty-title]");
|
||||
const emptyCopy = $("[data-empty-copy]");
|
||||
if (count === 0 && state.mode === "normal") {
|
||||
emptyTitle.textContent = "현재 조건에 맞는 예시가 없습니다.";
|
||||
emptyTitle.textContent = "현재 조건에 맞는 항목이 없습니다.";
|
||||
emptyCopy.textContent = "검색어와 필터를 초기화하면 전체 대기열을 다시 볼 수 있습니다.";
|
||||
}
|
||||
};
|
||||
|
|
@ -320,9 +320,9 @@
|
|||
if (mode === "empty") {
|
||||
const empty = $("[data-queue-empty]");
|
||||
empty.hidden = false;
|
||||
$("[data-empty-title]").textContent = "아직 접수된 예시 매물이 없습니다.";
|
||||
$("[data-empty-copy]").textContent = "새 예시 초안을 만들거나 다른 상태를 시연할 수 있습니다.";
|
||||
announce("빈 대기열 상태를 시연합니다.");
|
||||
$("[data-empty-title]").textContent = "아직 표시할 항목이 없습니다.";
|
||||
$("[data-empty-copy]").textContent = "검색어와 필터를 초기화하면 전체 목록을 다시 볼 수 있습니다.";
|
||||
announce("현재 표시할 항목이 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -333,8 +333,8 @@
|
|||
}
|
||||
|
||||
if (mode === "save-error") {
|
||||
showBanner("저장 경로를 확인할 수 없습니다.", "입력은 화면에 남지만 저장 성공으로 표시하지 않습니다. 수정 시연을 누르면 실패 처리를 확인할 수 있습니다.", "error");
|
||||
announce("저장 실패 상태를 시연합니다.");
|
||||
showBanner("지금은 변경 내용을 보관할 수 없습니다.", "입력은 화면에 남지만 완료로 표시하지 않습니다.", "error");
|
||||
announce("저장할 수 없는 상태입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -347,25 +347,25 @@
|
|||
status.textContent = "자료 오래됨";
|
||||
status.className = "status-badge status-warn";
|
||||
}
|
||||
announce("자료 오래됨 상태를 시연합니다.");
|
||||
announce("자료가 오래되어 다시 확인이 필요합니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "conflict") {
|
||||
showBanner("다른 예시 담당자가 먼저 수정했습니다.", "현재 버전 7과 서버 예시 버전 8의 차이를 확인한 뒤 다시 저장해야 합니다.", "error");
|
||||
announce("버전 충돌 상태를 시연합니다.");
|
||||
showBanner("다른 담당자가 먼저 수정했습니다.", "현재 내용과 최신 자료의 차이를 확인한 뒤 다시 저장해야 합니다.", "error");
|
||||
announce("최신 자료와 차이가 있어 다시 확인이 필요합니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "permission") {
|
||||
document.body.classList.add("is-permission-limited");
|
||||
$$('[data-review-ai], [data-simulate-save], [data-new-draft]').forEach((button) => button.disabled = true);
|
||||
showBanner("중개보조 예시 권한으로 보는 중입니다.", "초안과 일정은 정리할 수 있지만 법정 검수 승인, AI 최종 승인과 게시를 할 수 없습니다.", "info");
|
||||
announce("권한 제한 상태를 시연합니다.");
|
||||
showBanner("중개보조 권한으로 보는 중입니다.", "초안과 일정은 정리할 수 있지만 법정 검수 승인, AI 최종 승인과 게시를 할 수 없습니다.", "info");
|
||||
announce("현재 권한으로는 승인과 게시를 할 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
announce("정상 데모 상태로 돌아왔습니다.");
|
||||
announce("기본 목록을 다시 보여 줍니다.");
|
||||
};
|
||||
|
||||
const openDialog = (dialog) => {
|
||||
|
|
@ -380,11 +380,11 @@
|
|||
|
||||
const simulateSave = () => {
|
||||
if (state.mode === "permission") {
|
||||
announce("현재 예시 권한으로는 매물 정보를 수정할 수 없습니다.");
|
||||
announce("현재 권한으로는 매물 정보를 수정할 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
if (state.mode === "conflict") {
|
||||
showBanner("버전 충돌로 저장하지 않았습니다.", "서버 예시 버전 8을 다시 불러오거나 현재 수정을 별도 초안으로 남겨야 합니다.", "error");
|
||||
showBanner("최신 자료와 차이가 있어 저장하지 않았습니다.", "최신 내용을 다시 확인하거나 현재 수정을 별도 초안으로 남겨야 합니다.", "error");
|
||||
announce("버전 충돌로 저장하지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
|
@ -393,12 +393,12 @@
|
|||
announce("저장에 실패했습니다. 성공으로 처리하지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
announce("수정 시연을 마쳤습니다. 서버가 없는 데모라 실제 저장하지 않았습니다.");
|
||||
announce("변경 내용을 확인했습니다. 실제로 저장되거나 전송되지는 않습니다.");
|
||||
};
|
||||
|
||||
const createDraft = () => {
|
||||
if (state.mode === "permission") {
|
||||
announce("현재 예시 권한으로는 새 초안을 만들 수 없습니다.");
|
||||
announce("현재 권한으로는 새 초안을 만들 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
if (state.draftCreated) {
|
||||
|
|
@ -407,7 +407,7 @@
|
|||
}
|
||||
state.draftCreated = true;
|
||||
listingDetails["DEMO-HJ-NEW"] = {
|
||||
title: "제목 없는 새 예시 초안",
|
||||
title: "제목 없는 새 항목",
|
||||
status: "초안",
|
||||
statusClass: "status-demo",
|
||||
trade: "미선택",
|
||||
|
|
@ -427,15 +427,15 @@
|
|||
row.dataset.id = "DEMO-HJ-NEW";
|
||||
row.dataset.status = "draft";
|
||||
row.dataset.updated = new Date().toISOString();
|
||||
row.dataset.title = "제목 없는 새 예시 초안";
|
||||
row.dataset.title = "제목 없는 새 항목";
|
||||
row.setAttribute("aria-selected", "false");
|
||||
row.innerHTML = `
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="제목 없는 새 예시 초안 조사관 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-draft"><i></i></span><span><strong>제목 없는 새 예시 초안</strong><small>DEMO-HJ-NEW · 예시 매물</small></span></span></td>
|
||||
<td><span>미선택</span><small>유형 미선택</small></td><td><strong>입력 전</strong><small>면적 입력 전</small></td><td><span>방금</span><small>데모 초안</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>입력 대기</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>실행 전</span></td><td><span class="assignee"><i>김</i>김유진</span></td>`;
|
||||
<td class="row-cell-select"><button type="button" class="row-select-overlay" data-select-row aria-label="제목 없는 새 항목 상세 정보 열기"></button><span class="row-title" aria-hidden="true"><span class="row-thumb row-thumb-draft"><i></i></span><span><strong>제목 없는 새 항목</strong><small>새 예시 매물</small></span></span></td>
|
||||
<td><span>미선택</span><small>유형 미선택</small></td><td><strong>입력 전</strong><small>면적 입력 전</small></td><td><span>방금</span><small>새 항목</small></td><td><span class="cell-status is-blocked"><i aria-hidden="true"></i>입력 대기</span></td><td><span class="cell-status is-muted"><i aria-hidden="true"></i>분석 전</span></td><td><span class="assignee"><i>김</i>김유진</span></td>`;
|
||||
queueBody.prepend(row);
|
||||
applyFilters();
|
||||
openInspector("DEMO-HJ-NEW", true);
|
||||
announce("새 예시 초안을 만들었습니다. 외부 서버에는 저장하지 않았습니다.");
|
||||
announce("새 항목을 만들었습니다. 실제로 저장되거나 전송되지는 않습니다.");
|
||||
};
|
||||
|
||||
const handleRowSelection = (button) => {
|
||||
|
|
@ -523,7 +523,7 @@
|
|||
|
||||
$("[data-review-ai]")?.addEventListener("click", (event) => {
|
||||
if (state.mode === "permission") {
|
||||
announce("현재 예시 권한으로는 AI 결과를 승인할 수 없습니다.");
|
||||
announce("현재 권한으로는 AI 결과를 승인할 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
event.currentTarget.textContent = "수정 후 승인됨";
|
||||
|
|
@ -533,20 +533,20 @@
|
|||
badge.textContent = "수정 후 승인";
|
||||
badge.className = "status-badge status-pass";
|
||||
}
|
||||
announce("AI 참고 분석을 수정 후 승인 상태로 바꿨습니다. 데모 외부에는 게시하지 않았습니다.");
|
||||
announce("AI 참고 결과를 수정 후 확인 상태로 바꿨습니다. 실제 광고에는 사용되지 않습니다.");
|
||||
});
|
||||
|
||||
$$('[data-claim-action]').forEach((button) => button.addEventListener("click", () => {
|
||||
button.textContent = button.dataset.claimAction === "edit" ? "수정 반영" : "확인됨";
|
||||
button.disabled = true;
|
||||
announce("주장 검토 상태를 데모 안에서만 바꿨습니다.");
|
||||
announce("표시 문구의 검토 상태를 이 화면에서만 바꿨습니다.");
|
||||
}));
|
||||
|
||||
$("[data-reschedule]")?.addEventListener("click", (event) => {
|
||||
const card = event.currentTarget.closest(".appointment-card");
|
||||
const time = $("div > strong", card);
|
||||
time.textContent = time.textContent.includes("3:30") ? "오후 5:00~5:30" : "오후 3:30~4:00";
|
||||
announce("예시 상담 시간을 바꿨습니다. 외부 알림은 보내지 않았습니다.");
|
||||
announce("안내용 상담 시간을 바꿨습니다. 알림은 전송되지 않았습니다.");
|
||||
});
|
||||
|
||||
$("[data-simulate-log-failure]")?.addEventListener("click", () => {
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@
|
|||
state.selectedId = best.id;
|
||||
renderSelection();
|
||||
$("#briefing")?.scrollIntoView({ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth" });
|
||||
announce(`${best.name}을 가장 가까운 후보로 골라 세 묶음의 브리핑을 만들었습니다.`);
|
||||
announce(`${best.name}을 가장 가까운 후보로 골랐습니다. 맞는 점, 조정할 점, 확인할 점을 정리했습니다.`);
|
||||
};
|
||||
|
||||
const markConditionsChanged = () => {
|
||||
|
|
@ -400,9 +400,9 @@
|
|||
if (!form.reportValidity()) return;
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(readConditions()));
|
||||
announce("현재 조건을 이 브라우저에 저장했습니다.");
|
||||
announce("현재 조건을 지금 사용 중인 기기에 임시 보관했습니다.");
|
||||
} catch {
|
||||
announce("브라우저 저장소를 사용할 수 없어 조건을 저장하지 않았습니다.");
|
||||
announce("지금 사용 중인 기기에 조건을 보관할 수 없습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
.admin-body {
|
||||
min-width: 20rem;
|
||||
min-width: 0;
|
||||
color: #1c1c1a;
|
||||
background: #f3f4f3;
|
||||
}
|
||||
|
|
@ -237,7 +237,7 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
padding: 0.42rem 0.6rem;
|
||||
padding: 0.42rem 0.7rem;
|
||||
}
|
||||
|
||||
.topbar-actions .icon-button {
|
||||
|
|
@ -390,10 +390,10 @@
|
|||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 0.7rem;
|
||||
padding: 0.875rem;
|
||||
background: #fff;
|
||||
border: 1px solid #dedfdd;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--brief-rule);
|
||||
border-radius: var(--brief-panel-radius);
|
||||
}
|
||||
|
||||
.search-field {
|
||||
|
|
@ -412,6 +412,7 @@
|
|||
width: 100%;
|
||||
min-height: 2.75rem;
|
||||
min-width: 0;
|
||||
padding-inline: 0.5rem;
|
||||
color: #222320;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
|
@ -419,6 +420,11 @@
|
|||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.search-field:focus-within {
|
||||
border-color: var(--focus);
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--focus) 32%, transparent);
|
||||
}
|
||||
|
||||
.queue-toolbar select {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.42rem 1.8rem 0.42rem 0.65rem;
|
||||
|
|
@ -459,7 +465,8 @@
|
|||
.settings-proof {
|
||||
background: #fff;
|
||||
border: 1px solid #dedfdd;
|
||||
border-radius: 0.55rem;
|
||||
border-radius: var(--brief-panel-radius);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.queue-panel {
|
||||
|
|
@ -522,7 +529,11 @@
|
|||
}
|
||||
|
||||
.table-scroll {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
overscroll-behavior-inline: contain;
|
||||
scrollbar-gutter: stable both-edges;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
|
|
@ -535,9 +546,9 @@
|
|||
|
||||
.queue-table th,
|
||||
.queue-table td {
|
||||
padding: 0.7rem 0.6rem;
|
||||
padding: 0.8rem 0.875rem;
|
||||
border-bottom: 1px solid #ecedea;
|
||||
font-size: 0.66rem;
|
||||
font-size: var(--admin-body-size);
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
@ -548,7 +559,7 @@
|
|||
top: 0;
|
||||
color: #777973;
|
||||
background: #fafbfa;
|
||||
font-size: 0.58rem;
|
||||
font-size: var(--admin-meta-size);
|
||||
font-weight: 730;
|
||||
}
|
||||
|
||||
|
|
@ -605,7 +616,7 @@
|
|||
.queue-table td > strong + small {
|
||||
margin-top: 0.13rem;
|
||||
color: #62665f;
|
||||
font-size: 0.57rem;
|
||||
font-size: var(--admin-meta-size);
|
||||
}
|
||||
|
||||
.row-title {
|
||||
|
|
@ -822,6 +833,13 @@
|
|||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.queue-loading > span {
|
||||
animation: none;
|
||||
opacity: 0.72;
|
||||
}
|
||||
}
|
||||
|
||||
.queue-empty > span {
|
||||
display: grid;
|
||||
width: 3rem;
|
||||
|
|
@ -1087,7 +1105,7 @@
|
|||
.text-button {
|
||||
min-width: 2.75rem;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.4rem;
|
||||
padding: 0.5rem;
|
||||
color: #6f5420;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
|
@ -1510,23 +1528,26 @@
|
|||
|
||||
.claim-review article {
|
||||
margin-bottom: 0.5rem;
|
||||
padding: 0.65rem;
|
||||
padding: 0.75rem;
|
||||
background: #fafbfa;
|
||||
border: 1px solid #e1e2df;
|
||||
border: 1px solid var(--brief-rule);
|
||||
border-left-width: 3px;
|
||||
border-radius: 0.35rem;
|
||||
border-radius: var(--brief-panel-radius);
|
||||
}
|
||||
|
||||
.claim-review article.is-fact {
|
||||
border-left-color: #3d7c5d;
|
||||
background: var(--review-fact-bg);
|
||||
}
|
||||
|
||||
.claim-review article.is-unknown {
|
||||
border-left-color: #b1832d;
|
||||
background: var(--review-unknown-bg);
|
||||
}
|
||||
|
||||
.claim-review article.is-edited {
|
||||
border-left-color: #9d3f34;
|
||||
background: var(--review-edited-bg);
|
||||
}
|
||||
|
||||
.claim-review header {
|
||||
|
|
@ -1791,9 +1812,15 @@
|
|||
}
|
||||
|
||||
.admin-dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
box-sizing: border-box;
|
||||
width: min(35rem, calc(100% - 2rem));
|
||||
max-height: calc(100dvh - 2rem);
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
color: #20211f;
|
||||
background: #fff;
|
||||
border: 0;
|
||||
|
|
@ -1801,6 +1828,10 @@
|
|||
box-shadow: 0 1.5rem 5rem rgba(20, 20, 19, 0.28);
|
||||
}
|
||||
|
||||
html:has(.admin-dialog[open]) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.admin-dialog::backdrop {
|
||||
background: rgba(20, 20, 19, 0.62);
|
||||
backdrop-filter: blur(2px);
|
||||
|
|
@ -1923,6 +1954,13 @@
|
|||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
@media (min-width: 120rem) {
|
||||
.admin-content {
|
||||
max-width: 122rem;
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 92rem) {
|
||||
.admin-workbench {
|
||||
grid-template-columns: minmax(35rem, 1fr) 23rem;
|
||||
|
|
@ -1977,6 +2015,33 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* 짧은 가로 화면은 조사관을 덮는 시트로 다뤄야 한다.
|
||||
미디어·부제까지 고정하면 scroll 영역이 0px로 수축해 핵심 검토 내용이 전부 사라진다. */
|
||||
@media (max-width: 76rem) and (max-height: 40rem) {
|
||||
.inspector {
|
||||
top: 0;
|
||||
max-height: 100dvh;
|
||||
}
|
||||
|
||||
.inspector-header {
|
||||
padding: 0.65rem 0.8rem;
|
||||
}
|
||||
|
||||
.inspector-header small,
|
||||
.inspector-media {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inspector-tabs button {
|
||||
min-height: 2.25rem;
|
||||
padding-block: 0.45rem;
|
||||
}
|
||||
|
||||
.inspector-actions {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.admin-shell {
|
||||
grid-template-columns: 13.5rem minmax(0, 1fr);
|
||||
|
|
@ -2034,11 +2099,29 @@
|
|||
}
|
||||
|
||||
.admin-topbar {
|
||||
min-width: 0;
|
||||
gap: 0.5rem;
|
||||
padding: 0 0.8rem;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
margin-right: auto;
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.topbar-actions {
|
||||
min-width: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.account-button {
|
||||
display: grid;
|
||||
width: 2.75rem;
|
||||
min-width: 0;
|
||||
flex: 0 0 2.75rem;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.icon-button kbd,
|
||||
|
|
@ -2087,7 +2170,7 @@
|
|||
@media (max-width: 34rem) {
|
||||
.demo-strip {
|
||||
min-height: 2.3rem;
|
||||
padding-inline: 0.5rem;
|
||||
padding-inline: 0.75rem;
|
||||
}
|
||||
|
||||
.admin-sidebar,
|
||||
|
|
@ -2141,6 +2224,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* 이 규칙은 좁은 폭 보정 뒤에 둬야 한다. 그렇지 않으면 34rem 보정의 top 값이
|
||||
짧은 화면 시트보다 뒤에 적용되어 스크롤 영역이 다시 0px가 된다. */
|
||||
@media (max-width: 76rem) and (max-height: 40rem) {
|
||||
.inspector {
|
||||
top: 0;
|
||||
max-height: 100dvh;
|
||||
}
|
||||
|
||||
.channel-gates {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
padding: 0.35rem;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.channel-gate {
|
||||
padding: 0.35rem 0.4rem;
|
||||
}
|
||||
|
||||
.channel-gate small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 작은 운영 문구도 본문 대비 기준을 넘도록 중립색을 한 단계 진하게 쓴다. */
|
||||
.admin-main small,
|
||||
.admin-main dt,
|
||||
|
|
|
|||
|
|
@ -100,12 +100,6 @@ body {
|
|||
color: var(--slate-deep);
|
||||
}
|
||||
|
||||
.site-nav .nav-admin {
|
||||
padding: 0 0.95rem;
|
||||
color: var(--sheet);
|
||||
background: var(--charcoal);
|
||||
}
|
||||
|
||||
.menu-button {
|
||||
display: none;
|
||||
width: 2.75rem;
|
||||
|
|
@ -165,6 +159,7 @@ body {
|
|||
|
||||
.brief-hero {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
width: min(100%, 96rem);
|
||||
margin: 0 auto;
|
||||
padding: 3rem 2rem 0;
|
||||
|
|
@ -292,12 +287,14 @@ body {
|
|||
grid-area: dock;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(10rem, 1.15fr) repeat(5, minmax(7.5rem, 0.8fr)) minmax(10rem, 1fr);
|
||||
gap: 0.8rem 1rem;
|
||||
min-width: 0;
|
||||
gap: 1rem;
|
||||
align-items: end;
|
||||
margin: -5.5rem 2.5rem 0;
|
||||
padding: 1.35rem 1.5rem 1rem;
|
||||
background: var(--sheet);
|
||||
border-top: 3px solid var(--charcoal);
|
||||
border-bottom: 1px solid var(--brief-rule);
|
||||
box-shadow: 0 6px 0 var(--paper-shadow), 0 1.4rem 3rem rgba(44, 45, 40, 0.1);
|
||||
}
|
||||
|
||||
|
|
@ -312,34 +309,35 @@ body {
|
|||
|
||||
.dock-heading span {
|
||||
color: var(--slate-deep);
|
||||
font-size: 0.62rem;
|
||||
font-size: var(--brief-label-size);
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.dock-heading strong {
|
||||
margin-top: 0.2rem;
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.dock-field > span:first-child {
|
||||
display: block;
|
||||
margin-bottom: 0.3rem;
|
||||
color: #646963;
|
||||
font-size: 0.61rem;
|
||||
font-size: var(--brief-label-size);
|
||||
font-weight: 710;
|
||||
}
|
||||
|
||||
.dock-field input,
|
||||
.dock-field select {
|
||||
width: 100%;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.55rem 0.4rem;
|
||||
min-height: 3rem;
|
||||
padding: 0.55rem 0.5rem;
|
||||
color: var(--charcoal);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #90948e;
|
||||
border-radius: 0;
|
||||
font-size: 0.78rem;
|
||||
font-size: var(--brief-input-size);
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 710;
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +371,7 @@ body {
|
|||
|
||||
.save-button {
|
||||
min-height: 2.75rem;
|
||||
padding: 0.35rem;
|
||||
padding: 0.5rem;
|
||||
color: #555b56;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
|
|
@ -409,6 +407,17 @@ body {
|
|||
gap: clamp(3rem, 7vw, 7rem);
|
||||
}
|
||||
|
||||
/* 1920px 이상에서는 비교·서류 표면만 조금 넓혀 여백이 본문을 압도하지 않게 한다. */
|
||||
@media (min-width: 120rem) {
|
||||
.brief-hero {
|
||||
width: min(100%, 104rem);
|
||||
}
|
||||
|
||||
.briefing-section {
|
||||
width: min(100% - 4rem, 100rem);
|
||||
}
|
||||
}
|
||||
|
||||
.briefing-heading {
|
||||
align-self: center;
|
||||
}
|
||||
|
|
@ -537,25 +546,34 @@ body {
|
|||
|
||||
.compare-heading {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
align-items: end;
|
||||
grid-template-columns: minmax(28rem, 1fr) minmax(17rem, 0.5fr) auto;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 0.5fr) minmax(0, auto);
|
||||
gap: 3rem;
|
||||
margin-bottom: 3.5rem;
|
||||
}
|
||||
|
||||
.compare-heading > p {
|
||||
min-width: 0;
|
||||
margin: 0 0 0.45rem;
|
||||
color: #5a605b;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.compare-heading h2 {
|
||||
overflow-wrap: anywhere;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.unit-switches {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.unit-switches > div {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
border: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
|
|
@ -1035,8 +1053,9 @@ body {
|
|||
|
||||
.handoff-section {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
grid-template-columns: minmax(24rem, 0.8fr) minmax(28rem, 1fr) auto;
|
||||
grid-template-columns: minmax(0, 0.8fr) minmax(0, 1fr) minmax(0, auto);
|
||||
gap: clamp(3rem, 7vw, 7rem);
|
||||
padding: clamp(6rem, 9vw, 9rem) max(2rem, calc((100% - 92rem) / 2));
|
||||
color: var(--sheet);
|
||||
|
|
@ -1047,6 +1066,18 @@ body {
|
|||
color: #bec7cc;
|
||||
}
|
||||
|
||||
.handoff-section > header,
|
||||
.handoff-steps,
|
||||
.handoff-cta {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.handoff-section h2,
|
||||
.handoff-steps p,
|
||||
.handoff-cta {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.handoff-steps {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
@ -1122,9 +1153,15 @@ body {
|
|||
}
|
||||
|
||||
.consult-dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
box-sizing: border-box;
|
||||
width: min(36rem, calc(100% - 2rem));
|
||||
max-height: calc(100svh - 2rem);
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
color: var(--charcoal);
|
||||
background: var(--sheet);
|
||||
border: 1px solid var(--charcoal);
|
||||
|
|
@ -1132,6 +1169,10 @@ body {
|
|||
box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
html:has(.consult-dialog[open]) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.consult-dialog::backdrop {
|
||||
background: rgba(18, 19, 17, 0.72);
|
||||
}
|
||||
|
|
@ -1298,12 +1339,16 @@ body {
|
|||
z-index: 90;
|
||||
top: 6.5rem;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
max-height: calc(100dvh - 6.5rem);
|
||||
padding: 0.75rem 1.25rem 1.25rem;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
background: var(--sheet);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
|
@ -1312,8 +1357,7 @@ body {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.site-nav a,
|
||||
.site-nav .nav-admin {
|
||||
.site-nav a {
|
||||
min-height: 3rem;
|
||||
padding: 0 0.5rem;
|
||||
color: var(--charcoal);
|
||||
|
|
@ -1324,15 +1368,15 @@ body {
|
|||
.brief-hero {
|
||||
padding: 0;
|
||||
grid-template-areas: "masthead" "scene" "dock";
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.masthead {
|
||||
padding: 5rem max(1.25rem, calc((100% - 44rem) / 2)) 4rem;
|
||||
padding: 5rem max(1.25rem, calc((100% - 44rem) / 2)) var(--space-10);
|
||||
}
|
||||
|
||||
.hero-scene {
|
||||
height: min(70vw, 40rem);
|
||||
height: min(70vw, 38rem);
|
||||
min-height: 32rem;
|
||||
}
|
||||
|
||||
|
|
@ -1371,7 +1415,7 @@ body {
|
|||
}
|
||||
|
||||
.compare-heading {
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, auto);
|
||||
}
|
||||
|
||||
.compare-heading > p {
|
||||
|
|
@ -1439,7 +1483,7 @@ body {
|
|||
}
|
||||
|
||||
.handoff-section {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.handoff-cta {
|
||||
|
|
@ -1456,6 +1500,7 @@ body {
|
|||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
.handoff-cta { width: 100%; min-width: 0; }
|
||||
.demo-strip {
|
||||
font-size: 0.62rem;
|
||||
}
|
||||
|
|
@ -1576,7 +1621,7 @@ body {
|
|||
}
|
||||
|
||||
.compare-heading {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,15 @@
|
|||
--radius-md: 0.75rem;
|
||||
--radius-lg: 1.25rem;
|
||||
--transition: 180ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--brief-label-size: 0.8125rem;
|
||||
--brief-input-size: 1rem;
|
||||
--brief-panel-radius: 0.5rem;
|
||||
--brief-rule: var(--line-light);
|
||||
--admin-body-size: 0.8125rem;
|
||||
--admin-meta-size: 0.75rem;
|
||||
--review-fact-bg: #f2f8f4;
|
||||
--review-unknown-bg: #fff8e8;
|
||||
--review-edited-bg: #fdf0ed;
|
||||
}
|
||||
|
||||
*,
|
||||
|
|
|
|||
169
apps/site/public/work/hyang-incense/assets/fonts/LICENSE.html
Normal file
13
apps/site/public/work/hyang-incense/assets/fonts/SOURCES.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
17
apps/site/public/work/hyang-incense/design.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 향연
|
||||
|
||||
연기 세로 사진과 주변 여백을 유지한다. 히어로 사진의 이중 액자를 제거해 사진면과 기록면을 직접 맞대고, 숙성 장부와 아카이브 목록은 라벨·값이 같은 기준선에서 읽히게 조정한다. 본문은 17px 이상과 42rem 이하의 폭을 쓴다.
|
||||
|
||||
| 판단 축 | 적용 | 한계 |
|
||||
| --- | --- | --- |
|
||||
| 구도 | Hofmann의 면과 여백 관계를 참고해 사진 주위의 불필요한 액자 여백을 줄였다. | 이 참고가 모든 전시형 페이지의 최적 해법은 아니다. |
|
||||
| 타입 | Ruder를 참고해 장부의 라벨·값과 본문의 역할을 분리했다. | 읽기 속도나 이해도 향상을 주장하지 않는다. |
|
||||
| 색 | Albers를 참고해 따뜻한 바탕과 적갈색 동작색의 역할 경계를 유지했다. | 대비 수치는 브라우저 검증으로 재확인한다. |
|
||||
|
||||
토큰은 `--surface`, `--ink`, `--accent`, `--step-0`, `--space-*`를 사용한다. 로컬 MaruBuri 400/600은 `styles/fonts.css`, Pretendard는 기존 CDN을 유지한다. 가상 기록·예시 가격·기존 앵커는 바꾸지 않았다.
|
||||
|
||||
## 2026-09-12 현재 검증 기록
|
||||
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
href="./styles/fonts.css?v=20260912all"
|
||||
/>
|
||||
<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="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 width='32' height='32' fill='%23F6F2E9'/%3E%3Cpath d='M16 6c0 3 0 5 0 7m0 4c0 3 2 3 2 5m-2 2c0 2-2 3-2 5' fill='none' stroke='%237C3B24' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E"
|
||||
|
|
@ -45,24 +45,20 @@
|
|||
</header>
|
||||
|
||||
<main id="main">
|
||||
<!--
|
||||
히어로 — 이 페이지의 리스크: 상품 사진 대신 연기 한 줄기.
|
||||
여백이 주인공이고 사진은 그 여백을 가르는 흔적 하나다.
|
||||
-->
|
||||
<section class="hero" aria-labelledby="hero-h">
|
||||
<div class="wrap hero-grid">
|
||||
<div class="hero-text">
|
||||
<p class="hero-kicker reveal">자연 침향 · 백단 · 서울 삼청동</p>
|
||||
<p class="hero-kicker reveal">가상 향 아카이브 · 기록 형식 예시</p>
|
||||
<h1 id="hero-h" class="reveal" style="--delay: 80ms">
|
||||
연기는 흩어지고<br />향기만 머뭅니다
|
||||
</h1>
|
||||
<p class="lead reveal" style="--delay: 160ms">
|
||||
향연은 향을 판매하기 전에 기록부터 남깁니다. 어느 해에 들인 나무인지,
|
||||
수지가 몇이었는지, 연소에 몇 분이 걸렸는지. 향은 사라지는 것이니
|
||||
남는 것은 문서뿐입니다.
|
||||
향연은 향의 산지·숙성·연소 정보를 어떻게 기록할 수 있는지 보여 주는
|
||||
가상 아카이브입니다. 아래 상품·가격·방문 정보는 판매나 예약을 위한
|
||||
정보가 아니라 기록 형식의 예시입니다.
|
||||
</p>
|
||||
<div class="hero-actions reveal" style="--delay: 220ms">
|
||||
<a class="btn" href="#collection">아카이브 보기</a>
|
||||
<a class="btn" href="#collection">기록 구성 보기</a>
|
||||
</div>
|
||||
</div>
|
||||
<figure class="hero-photo reveal" style="--delay: 120ms">
|
||||
|
|
@ -74,15 +70,11 @@
|
|||
loading="eager"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
<figcaption>침향 한 줄기 · 삼청동 아카이브룸</figcaption>
|
||||
<figcaption>침향 연출 이미지 · 가상 아카이브</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
향목 — 나무의 단면. 침향은 나무가 아니라 나무의 병(병해수지)이라는 것을
|
||||
이 섹션에서만 설명한다.
|
||||
-->
|
||||
<section id="wood" class="section" aria-labelledby="wood-h">
|
||||
<div class="wrap two">
|
||||
<figure class="two-photo reveal">
|
||||
|
|
@ -103,31 +95,26 @@
|
|||
얻을 수 없고, 자연에서 쓰러진 나무에서만 길어 올립니다.
|
||||
</p>
|
||||
<p class="muted">
|
||||
향연은 인공접종 목재를 다루지 않습니다. 매입 단계에서 수지 함량을
|
||||
측정해 기록하고, 그 기록이 통과하지 못한 나무는 아카이브에 들어오지
|
||||
않습니다.
|
||||
이 섹션은 산지·수지 함량·숙성 상태를 함께 기록하는 방식을 설명합니다.
|
||||
특정 목재의 매입 기준이나 실제 아카이브 운영 정책을 뜻하지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
숙성 — 아카이브의 뼈대. 연도가 곧 목록이다.
|
||||
카드가 아니라 장부의 행: 연도 · 입목 · 수지 · 상태.
|
||||
-->
|
||||
<section id="aging" class="section band" aria-labelledby="aging-h">
|
||||
<div class="wrap">
|
||||
<div class="aging-head">
|
||||
<h2 id="aging-h">숙성 장부</h2>
|
||||
<h2 id="aging-h">숙성 기록 예시</h2>
|
||||
<p class="muted">
|
||||
들여온 해와 수지 함량, 지금 상태입니다. 향은 재고가 아니라 연도입니다 —
|
||||
같은 나무가 다시 들어오지 않습니다.
|
||||
들여온 해와 수지 함량, 상태를 한 줄에서 읽는 기록 형식입니다. 아래
|
||||
연도·산지·수지 함량은 실제 측정이나 재고를 뜻하지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
<div class="table-wrap reveal">
|
||||
<table>
|
||||
<caption class="sr-only">
|
||||
숙성 장부 — 입목 연도, 산지, 수지 함량, 현재 상태
|
||||
숙성 기록 예시 — 입목 연도, 산지, 수지 함량, 상태
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -142,85 +129,80 @@
|
|||
<th scope="row">2001년</th>
|
||||
<td>베트남 카호아</td>
|
||||
<td class="num">64.2%</td>
|
||||
<td>열매 · 매진 임박</td>
|
||||
<td>열매 · 상태 예시</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2012년</th>
|
||||
<td>인도네시아 리아우</td>
|
||||
<td class="num">51.8%</td>
|
||||
<td>침향 절편 · 소량</td>
|
||||
<td>침향 절편 · 상태 예시</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2024년</th>
|
||||
<td>캄보디아 프놈펜 인근</td>
|
||||
<td class="num">47.3%</td>
|
||||
<td>숙성 중 · 비매품</td>
|
||||
<td>숙성 중 · 상태 예시</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="aging-note muted">
|
||||
수지 함량: 에탄올 용출법 실측. 열매(초험)는 기증용으로만 남겨둡니다.
|
||||
수지 함량과 상태 표기는 기록 형식을 설명하기 위한 예시 값입니다.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
아카이브 — 세 개의 물건. 사진 없는 목록.
|
||||
종이의 물성을 믿고 글자로만 판다(R2: 텍스트가 상품).
|
||||
-->
|
||||
<section id="collection" class="section" aria-labelledby="collection-h">
|
||||
<div class="wrap">
|
||||
<h2 id="collection-h">지금 만질 수 있는 것</h2>
|
||||
<h2 id="collection-h">기록으로 살펴보는 구성</h2>
|
||||
<ol class="collection">
|
||||
<li class="item reveal">
|
||||
<div class="item-text">
|
||||
<h3>자연 침향 절편</h3>
|
||||
<p>2001년 입목 · 수지 64.2% · 10g</p>
|
||||
</div>
|
||||
<p class="item-price">180,000원</p>
|
||||
<p class="item-price">예시 180,000원</p>
|
||||
</li>
|
||||
<li class="item reveal" style="--delay: 80ms">
|
||||
<div class="item-text">
|
||||
<h3>백단 침 향</h3>
|
||||
<p>인도 마이소르 노목 · 20g · 연소 45분</p>
|
||||
</div>
|
||||
<p class="item-price">42,000원</p>
|
||||
<p class="item-price">예시 42,000원</p>
|
||||
</li>
|
||||
<li class="item reveal" style="--delay: 160ms">
|
||||
<div class="item-text">
|
||||
<h3>침향 인엽</h3>
|
||||
<p>리아우산 · 2012년 입목 · 3g</p>
|
||||
</div>
|
||||
<p class="item-price">96,000원</p>
|
||||
<p class="item-price">예시 96,000원</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="muted collection-note">
|
||||
모든 물건은 삼청동 아카이브룸에서 직접 내어드립니다. 향을 맡아 보고
|
||||
정하시면 됩니다 — 예약제이며 회당 한 분만 들어갑니다.
|
||||
이 목록은 상품명·단위·가격을 기록하는 방식을 보여 줍니다. 실제 상품 판매,
|
||||
향 시향, 예약 또는 방문을 제공하지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 방문 — 마지막 섹션이 푸터 역할 -->
|
||||
<section id="visit" class="section visit" aria-labelledby="visit-h">
|
||||
<div class="wrap visit-grid">
|
||||
<div>
|
||||
<h2 id="visit-h">삼청동, 예약제</h2>
|
||||
<h2 id="visit-h">방문 정보 예시</h2>
|
||||
<address>
|
||||
서울 종로구 삼청로 00길 00<br />
|
||||
경복궁역 5번 출구에서 도보 6분
|
||||
가상 아카이브 안내<br />
|
||||
실제 장소와 방문 경로를 제공하지 않습니다.
|
||||
</address>
|
||||
</div>
|
||||
<div class="visit-info">
|
||||
<dl>
|
||||
<div>
|
||||
<dt>예약</dt>
|
||||
<dt>방문 시간 예시</dt>
|
||||
<dd>화–토 · 11:00, 14:00, 16:00 회당 50분</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>문의</dt>
|
||||
<dd><a class="link" href="mailto:archive@hyang.kr">archive@hyang.kr</a></dd>
|
||||
<dt>예약</dt>
|
||||
<dd>실제 예약 및 문의를 받지 않습니다.</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
@ -229,18 +211,5 @@
|
|||
</section>
|
||||
</main>
|
||||
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
15
apps/site/public/work/hyang-incense/styles/fonts.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@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;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import url("./tokens.css");
|
||||
@import url("./tokens.css?v=20260912all");
|
||||
|
||||
/* ==========================================================================
|
||||
향연 — quiet-luxury. 밝은 편집면 위의 기록.
|
||||
|
|
@ -138,6 +138,7 @@ a {
|
|||
}
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: clamp(var(--space-3), 2vw, var(--space-5));
|
||||
font-size: var(--step--1);
|
||||
}
|
||||
|
|
@ -157,13 +158,26 @@ a {
|
|||
/* ── 히어로 ───────────────────────────────────────────────────────────── */
|
||||
|
||||
.hero {
|
||||
padding-block: var(--space-8) var(--space-7);
|
||||
display: grid;
|
||||
align-items: center;
|
||||
min-block-size: min(46rem, calc(100svh - 4rem));
|
||||
padding-block: var(--space-6);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
|
||||
gap: var(--space-7) var(--space-6);
|
||||
align-items: start;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(16rem, 28rem);
|
||||
gap: clamp(var(--space-5), 6vw, var(--space-8));
|
||||
align-items: center;
|
||||
}
|
||||
.hero-grid > *,
|
||||
.two > *,
|
||||
.visit-grid > * {
|
||||
min-width: 0;
|
||||
}
|
||||
.hero-text {
|
||||
max-width: 38rem;
|
||||
padding-block: var(--space-4);
|
||||
}
|
||||
.hero-kicker {
|
||||
font-size: var(--step--1);
|
||||
|
|
@ -172,7 +186,7 @@ a {
|
|||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.lead {
|
||||
max-width: 30em;
|
||||
max-width: 42rem;
|
||||
}
|
||||
.hero-text .lead {
|
||||
margin-top: var(--space-5);
|
||||
|
|
@ -181,7 +195,9 @@ a {
|
|||
margin-top: var(--space-6);
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
border: 1px solid var(--ink);
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
|
|
@ -196,22 +212,24 @@ a {
|
|||
}
|
||||
|
||||
.hero-photo {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: var(--space-2);
|
||||
justify-content: end;
|
||||
width: min(100%, 28rem);
|
||||
justify-self: end;
|
||||
align-items: start;
|
||||
}
|
||||
.hero-photo img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 3 / 4;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.hero-photo figcaption {
|
||||
font-size: var(--step--1);
|
||||
color: var(--ink-faint);
|
||||
color: var(--ink-muted);
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +238,12 @@ a {
|
|||
.section {
|
||||
padding-block: var(--space-8);
|
||||
}
|
||||
.section[id] {
|
||||
scroll-margin-top: 6rem;
|
||||
}
|
||||
#aging .wrap {
|
||||
width: min(100% - 2 * var(--pad-inline), var(--container-wide));
|
||||
}
|
||||
.band {
|
||||
background: var(--surface-raised);
|
||||
border-block: 1px solid var(--line);
|
||||
|
|
@ -247,6 +271,9 @@ a {
|
|||
.two-text p + p {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
.two-text p {
|
||||
max-width: 42rem;
|
||||
}
|
||||
.two-text .muted {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
|
@ -254,7 +281,7 @@ a {
|
|||
/* ── 숙성 장부 ────────────────────────────────────────────────────────── */
|
||||
|
||||
.aging-head {
|
||||
max-width: 36em;
|
||||
max-width: 42rem;
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
.aging-head p {
|
||||
|
|
@ -305,8 +332,8 @@ tbody th {
|
|||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: baseline;
|
||||
gap: var(--space-4);
|
||||
padding-block: var(--space-4);
|
||||
|
|
@ -323,11 +350,12 @@ tbody th {
|
|||
.item-price {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--step-1);
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.collection-note {
|
||||
margin-top: var(--space-4);
|
||||
max-width: 34em;
|
||||
max-width: 42rem;
|
||||
}
|
||||
|
||||
/* ── 방문 ─────────────────────────────────────────────────────────────── */
|
||||
|
|
@ -368,23 +396,9 @@ tbody th {
|
|||
/* ── 등장 모션 — 다른 페이지보다 느리다 ──────────────────────────────── */
|
||||
|
||||
.reveal {
|
||||
opacity: 0;
|
||||
translate: 0 16px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 반응형 ───────────────────────────────────────────────────────────── */
|
||||
|
||||
|
|
@ -397,6 +411,7 @@ tbody th {
|
|||
.hero-photo {
|
||||
max-width: 24rem;
|
||||
justify-content: start;
|
||||
justify-self: start;
|
||||
}
|
||||
.visit-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
|
@ -405,6 +420,7 @@ tbody th {
|
|||
display: none;
|
||||
}
|
||||
.hero {
|
||||
min-block-size: auto;
|
||||
padding-block: var(--space-6);
|
||||
}
|
||||
}
|
||||
|
|
@ -416,11 +432,13 @@ tbody th {
|
|||
}
|
||||
.nav {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
.item-price {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
--surface-raised: #efe9dc;
|
||||
--ink: #2b2724;
|
||||
--ink-muted: #6d655c;
|
||||
--ink-faint: #9a9083;
|
||||
--ink-faint: #746c60;
|
||||
--line: #ddd4c3;
|
||||
--accent: #7c3b24; /* 적갈 — 링크와 핵심 동작만 */
|
||||
--accent-hover: #5f2c1a;
|
||||
|
|
@ -44,8 +44,10 @@
|
|||
|
||||
--radius: 2px;
|
||||
|
||||
--container: 1040px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
|
||||
/* 향의 문장은 좁게, 장부와 실물 기록은 넓게 둔다. */
|
||||
--container: 1160px;
|
||||
--container-wide: 1440px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem);
|
||||
|
||||
--dur-quick: 160ms;
|
||||
--dur-normal: 560ms; /* 느린 페이지 — 등장도 느리게 */
|
||||
|
|
|
|||
27
apps/site/public/work/lumina-optics/design.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Lumina Optics design.md
|
||||
|
||||
## 브리프
|
||||
|
||||
- 무엇을: IOR와 분산을 조작하는 가상 광학 시험대.
|
||||
- 누구에게: 광선 모델의 조건과 결과물을 함께 읽으려는 방문자.
|
||||
- 제약: WebGL/정적 폴백, 기존 슬라이더와 가상·측정 아님 표기를 유지한다.
|
||||
|
||||
## 결정표
|
||||
|
||||
| 축 | 관찰과 적용 범위 | 결정·역할 토큰 | 기각 조건 |
|
||||
|---|---|---|---|
|
||||
| 구도 | 기준 1440 화면에서 큰 프리즘이 제목과 조작을 폴드 아래로 밀었다. | 제목·한계 설명을 상단에 두고 `9:4` 캔버스/제어 프레임을 배치한다. `--container`, `--space-4`, `--space-6`. | 캔버스가 제어보다 큰 면적을 차지해 둘 중 하나가 첫 화면에서 사라지면 재조정한다. |
|
||||
| 타입 | 제목은 모델의 의미, 모노는 조건과 조작값을 읽게 해야 한다. | `--step-3` 제목, `--step--1` 시험대 원장과 제어 보조문을 유지한다. | 실제 긴 라벨이 320px 또는 200% 확대에서 제어를 밀면 폭·간격을 수정한다. |
|
||||
| 색 | 스펙트럼만 광학 결과를 나타내며 UI 색은 보조여야 한다. | `--surface`, `--surface-raised`, `--accent-stroke`을 프레임·포커스·입력에만 쓴다. | UI 색이 캔버스의 분산 광선보다 먼저 읽히면 채도를 줄인다. |
|
||||
|
||||
## 근거와 경계
|
||||
|
||||
[art-direction.md](../../../../../packages/skill/references/art-direction.md)의 Hofmann 관련 구도·군집 관찰을 제목/작업면 분리에만 적용했다. [typography.md](../../../../../packages/skill/references/typography.md)의 Ruder 관련 실제 카피·수치 proof 범위를 타입 계층에만 적용했다. [visual-design-lineage.md](../../../../../packages/skill/references/visual-design-lineage.md)의 Albers 관련 색 맥락은 캔버스와 UI 표면의 대비 확인에만 적용했다. 이 세 자료는 성능·이해도 법칙으로 사용하지 않는다.
|
||||
|
||||
[trustworthy-showcases.md](../../../../../packages/skill/references/trustworthy-showcases.md)의 진실 범위에 따라 모델·사양·곡선의 illustrative 표기를 가까운 공통 표면에 유지한다. 캔버스 안에는 텍스트를 굽지 않는다.
|
||||
|
||||
## 2026-09-12 현재 검증 기록
|
||||
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -3,15 +3,15 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Lumina Optics · Refraction Bench</title>
|
||||
<title>Lumina Optics · Interactive Optical Study</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="A WebGL bench where a single beam meets a prism: refraction and dispersion computed per frame. Drag the prism, change the index."
|
||||
content="An interactive optical study of refraction and dispersion. The lens profile, curves, and specifications are illustrative."
|
||||
/>
|
||||
<meta property="og:title" content="Lumina Optics · Refraction Bench" />
|
||||
<meta property="og:title" content="Lumina Optics · Interactive Optical Study" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="One beam, one prism, per-frame refraction."
|
||||
content="An illustrative light path you can inspect and adjust."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="theme-color" content="#101214" />
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Sans+KR:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=2" />
|
||||
<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 width='32' height='32' fill='%23101214'/%3E%3Cpath d='M6 16h8m8 0h4M14 16l10-7v14z' fill='none' stroke='%23BFE8EE' stroke-width='1.4'/%3E%3C/svg%3E"
|
||||
|
|
@ -45,40 +45,54 @@
|
|||
</header>
|
||||
|
||||
<main id="main">
|
||||
<!--
|
||||
The bench. The product is light behaviour, so the hero IS the instrument:
|
||||
one beam, one prism, per-frame refraction. Drag rotates; sliders change
|
||||
the index and the dispersion — they are the two honest knobs a bench
|
||||
has. Everything degrades to a static photograph.
|
||||
-->
|
||||
<section id="bench" class="hero" aria-labelledby="hero-h">
|
||||
<div class="bench-stage reveal">
|
||||
<canvas
|
||||
id="gl"
|
||||
aria-label="Interactive prism: a white beam enters from the left and exits as a spectrum. Drag to rotate the prism."
|
||||
></canvas>
|
||||
<img
|
||||
class="bench-fallback"
|
||||
src="./assets/prism-bench.webp"
|
||||
alt="A glass prism on a dark optical breadboard splitting one collimated beam into a spectrum"
|
||||
width="1440"
|
||||
height="810"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrap hero-inner">
|
||||
<div class="wrap hero-summary">
|
||||
<div class="hero-text">
|
||||
<p class="hero-kicker reveal">Refraction bench · WebGL</p>
|
||||
<p class="hero-kicker reveal">Interactive optical study</p>
|
||||
<h1 id="hero-h" class="reveal" style="--delay: 60ms">
|
||||
One beam,<br />bent honestly
|
||||
</h1>
|
||||
<p class="lead reveal" style="--delay: 120ms">
|
||||
No photograph on this screen is doing the work — the prism is
|
||||
being refracted right now, per frame, on your GPU. Drag it. Change
|
||||
the index. The spectrum you see is the same arithmetic lens
|
||||
designers use, minus the glasses.
|
||||
Move the prism to compare how refractive index and dispersion
|
||||
redirect one beam. This view illustrates an optical model; it is
|
||||
not a measured photograph, a product specification, or a lab result.
|
||||
</p>
|
||||
</div>
|
||||
<div class="hud reveal" style="--delay: 160ms">
|
||||
</div>
|
||||
<div class="wrap bench-layout">
|
||||
<div class="bench-shell">
|
||||
<div class="bench-stage reveal">
|
||||
<canvas
|
||||
id="gl"
|
||||
aria-label="Illustrative interactive prism: a white beam enters from the left and exits as a spectrum. Drag to rotate the concept model."
|
||||
></canvas>
|
||||
<img
|
||||
class="bench-fallback"
|
||||
src="./assets/prism-bench.webp"
|
||||
alt="Illustrative glass prism on a dark optical surface splitting one collimated beam into a spectrum"
|
||||
width="1440"
|
||||
height="810"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="bench-controls">
|
||||
<div class="bench-ledger" role="group" aria-label="Bench configuration">
|
||||
<dl>
|
||||
<div>
|
||||
<dt>illustrative beam reference</dt>
|
||||
<dd>632.8 nm · collimated</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>variable</dt>
|
||||
<dd>index + dispersion</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>view</dt>
|
||||
<dd>interactive light study · static reference</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="hud reveal" style="--delay: 160ms">
|
||||
<div class="hud-row">
|
||||
<div class="hud-row-head"><label for="ior">Index of refraction</label><output id="ior-out" for="ior">1.52</output></div>
|
||||
<input type="range" id="ior" min="1.45" max="2.20" step="0.01" value="1.52" />
|
||||
|
|
@ -87,56 +101,48 @@
|
|||
<div class="hud-row-head"><label for="disp">Dispersion (Abbe ↓)</label><output id="disp-out" for="disp">0.18</output></div>
|
||||
<input type="range" id="disp" min="0" max="0.6" step="0.01" value="0.18" />
|
||||
</div>
|
||||
<p class="hud-note" id="hud-note">Drag the prism to rotate · beam is collimated at 632.8 nm reference</p>
|
||||
</div>
|
||||
<p class="hud-note" id="hud-note">Drag the prism to rotate · illustrative light path at a 632.8 nm reference</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Anatomy — cut lens photography. Real glass, not renders.
|
||||
-->
|
||||
<section id="anatomy" class="section" aria-labelledby="anatomy-h">
|
||||
<div class="wrap two">
|
||||
<figure class="two-photo reveal">
|
||||
<img
|
||||
src="./assets/lens-section.webp"
|
||||
alt="Cutaway of a precision lens: a stack of glass elements in a metal barrel, edge-lit so each surface shows a refraction line"
|
||||
alt="Illustrative lens cutaway: a stack of glass elements in a metal barrel, edge-lit to show refraction paths"
|
||||
width="900"
|
||||
height="1200"
|
||||
loading="lazy"
|
||||
/>
|
||||
<figcaption>L-85 cutaway · 7 elements in 6 groups</figcaption>
|
||||
<figcaption>L-85 concept cutaway · illustrative element layout</figcaption>
|
||||
</figure>
|
||||
<div class="two-text">
|
||||
<h2 id="anatomy-h">Glass decides<br />before software does</h2>
|
||||
<h2 id="anatomy-h">Glass sets<br />the light path</h2>
|
||||
<p>
|
||||
A lens is a chain of refractions, and every surface answers before
|
||||
any firmware runs. Our L-85 uses two low-dispersion elements to
|
||||
keep the spectrum you just dragged from fanning out across the
|
||||
frame — dispersion corrected in glass, not in a profile.
|
||||
A lens is a chain of refractions: every surface bends the path
|
||||
before an image is made. The L-85 concept uses low-dispersion
|
||||
elements to explain why a design can keep colours from spreading
|
||||
across the frame.
|
||||
</p>
|
||||
<p class="muted">
|
||||
The cutaway above is a physical section, milled for measurement.
|
||||
Element count and spacing are measured from it, not from the
|
||||
drawing.
|
||||
The cutaway is a constructed reference image. Its element count
|
||||
and spacing are illustrative, not a measurement of a commercial lens.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
MTF — the only curve that argues. Static, thin, honest:
|
||||
measured at infinity, full aperture, both field directions.
|
||||
-->
|
||||
<section id="mtf" class="section band" aria-labelledby="mtf-h">
|
||||
<div class="wrap mtf-grid">
|
||||
<div class="mtf-text">
|
||||
<h2 id="mtf-h">MTF, not adjectives</h2>
|
||||
<h2 id="mtf-h">MTF, read with conditions</h2>
|
||||
<p>
|
||||
“Tack sharp” is not a specification. Contrast transfer at given
|
||||
line pairs per millimetre is. These curves are measured on an
|
||||
optical bench at full aperture — sagittal and meridional, out to
|
||||
the corner.
|
||||
Contrast transfer at stated line pairs per millimetre gives a
|
||||
clearer comparison than a slogan. These curves illustrate the
|
||||
labels a measured chart needs; they are not optical-bench results.
|
||||
</p>
|
||||
<ul class="mtf-legend">
|
||||
<li><span class="key key-solid" aria-hidden="true"></span>10 lp/mm · sagittal</li>
|
||||
|
|
@ -146,7 +152,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<figure class="mtf-plot reveal">
|
||||
<svg viewBox="0 0 560 320" role="img" aria-label="MTF curves: contrast stays above 0.85 at 10 lp/mm across the field; at 30 lp/mm it falls from 0.75 at center to 0.42 at the corner.">
|
||||
<svg viewBox="0 0 560 320" role="img" aria-label="Illustrative MTF curves showing how contrast can be compared from the centre to the corner; this is not a measured result.">
|
||||
<g class="chart-grid">
|
||||
<line x1="48" y1="16" x2="48" y2="272" />
|
||||
<line x1="48" y1="272" x2="536" y2="272" />
|
||||
|
|
@ -166,76 +172,62 @@
|
|||
<text x="292" y="292" text-anchor="middle">image field (mm)</text>
|
||||
<text x="536" y="292" text-anchor="end">corner 21.6</text>
|
||||
</g>
|
||||
<!-- 10 lp/mm sagittal: high, gentle fall -->
|
||||
<polyline class="mtf mtf-10s" points="48,32 172,36 296,44 420,60 536,76" />
|
||||
<!-- 10 lp/mm meridional -->
|
||||
<polyline class="mtf mtf-10m" points="48,34 172,40 296,52 420,72 536,96" />
|
||||
<!-- 30 lp/mm sagittal -->
|
||||
<polyline class="mtf mtf-30s" points="48,84 172,96 296,120 420,160 536,196" />
|
||||
<!-- 30 lp/mm meridional -->
|
||||
<polyline class="mtf mtf-30m" points="48,90 172,108 296,140 420,186 536,232" />
|
||||
</svg>
|
||||
<figcaption>L-85 · f/1.8 · infinity focus · bench-measured 2026-07</figcaption>
|
||||
<figcaption>L-85 concept · f/1.8 · infinity focus · illustrative curve</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--
|
||||
Spec — with conditions.
|
||||
-->
|
||||
<section class="section" aria-labelledby="spec-h">
|
||||
<div class="wrap">
|
||||
<h2 id="spec-h" class="reveal">Specification</h2>
|
||||
<h2 id="spec-h" class="reveal">Reference specification</h2>
|
||||
<div class="table-wrap reveal" style="--delay: 80ms">
|
||||
<table>
|
||||
<caption class="sr-only">L-85 specification — item, value, condition</caption>
|
||||
<caption class="sr-only">L-85 concept specification — illustrative item, value, and condition</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Item</th>
|
||||
<th scope="col" class="num">Value</th>
|
||||
<th scope="col" class="num">Illustrative value</th>
|
||||
<th scope="col">Condition</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><th scope="row">Focal length</th><td class="num">85 mm</td><td>nominal · ±1.5% at infinity</td></tr>
|
||||
<tr><th scope="row">Aperture</th><td class="num">f/1.8 – f/16</td><td>9-blade · near-circular</td></tr>
|
||||
<tr><th scope="row">Elements</th><td class="num">7 in 6</td><td>2 LD · measured from cutaway</td></tr>
|
||||
<tr><th scope="row">Close focus</th><td class="num">0.62 m</td><td>1:7.2 magnification</td></tr>
|
||||
<tr><th scope="row">Filter</th><td class="num">Ø 62 mm</td><td>brass, front-threaded</td></tr>
|
||||
<tr><th scope="row">Weight</th><td class="num">438 g</td><td>without caps, with hood</td></tr>
|
||||
<tr><th scope="row">Focal length</th><td class="num">85 mm</td><td>concept reference</td></tr>
|
||||
<tr><th scope="row">Aperture</th><td class="num">f/1.8 – f/16</td><td>concept reference</td></tr>
|
||||
<tr><th scope="row">Elements</th><td class="num">7 in 6</td><td>illustrative layout</td></tr>
|
||||
<tr><th scope="row">Close focus</th><td class="num">0.62 m</td><td>illustrative configuration</td></tr>
|
||||
<tr><th scope="row">Filter</th><td class="num">Ø 62 mm</td><td>illustrative configuration</td></tr>
|
||||
<tr><th scope="row">Weight</th><td class="num">438 g</td><td>illustrative configuration</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact -->
|
||||
<section id="contact" class="section band contact" aria-labelledby="contact-h">
|
||||
<div class="wrap contact-grid">
|
||||
<h2 id="contact-h">Bench visits</h2>
|
||||
<h2 id="contact-h">About this study</h2>
|
||||
<p>
|
||||
We measure loaner samples for free and publish the curves —
|
||||
manufacturers welcome or not. Bench time and MTF requests:
|
||||
This is a concept page for reading an optical model. It does not
|
||||
schedule sample loans, measurements, or MTF requests.
|
||||
</p>
|
||||
<a class="btn" href="mailto:bench@lumina.optics">bench@lumina.optics</a>
|
||||
<a class="btn" href="#bench">Return to the light study</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>lumina optics</p>
|
||||
<p class="foot-meta">measurements reproducible on request · bench cal. 2026-06</p>
|
||||
<p>lumina optics · optical study</p>
|
||||
<p class="foot-meta">illustrative model · no commercial measurement service</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="module">
|
||||
/*
|
||||
Refraction bench — three.js, one prism, two honest uniforms.
|
||||
Budget note: three.module.js ≈ 165KB gzip. 예산 상향 — 이 페이지의
|
||||
제품이 WebGL 자체다(데모 브리프 예산 상향 허용).
|
||||
폴백: WebGL 실패 시 사진이 보이고 캔버스는 뜨지 않는다(이미 마크업에 있음).
|
||||
*/
|
||||
const canvas = document.getElementById("gl");
|
||||
const fallback = document.querySelector(".bench-fallback");
|
||||
const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
|
|
@ -243,32 +235,30 @@
|
|||
let gl = null;
|
||||
try {
|
||||
gl = canvas.getContext("webgl2", { antialias: true, alpha: true });
|
||||
} catch (e) { /* fall through */ }
|
||||
} catch {}
|
||||
|
||||
if (!gl) {
|
||||
if (!gl || reduced) {
|
||||
canvas.remove();
|
||||
document.getElementById("hud-note").textContent =
|
||||
"WebGL unavailable — showing the bench photograph instead.";
|
||||
reduced
|
||||
? "Motion reduced — showing a static reference image instead."
|
||||
: "Interactive view unavailable — showing a static reference image instead.";
|
||||
} else {
|
||||
fallback.remove();
|
||||
const THREE = await import("https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js");
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, context: gl, antialias: true, alpha: true });
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
const DPR = Math.min(devicePixelRatio || 1, 1.5);
|
||||
renderer.setPixelRatio(DPR);
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(32, 1, 0.1, 50);
|
||||
camera.position.set(0, 0, 9);
|
||||
|
||||
// ----- prism: triangular cross-section (CylinderGeometry, 3 segments)
|
||||
const prismGeo = new THREE.CylinderGeometry(2.1, 2.1, 2.6, 3, 1, false);
|
||||
prismGeo.rotateX(Math.PI / 2); // axis toward camera
|
||||
prismGeo.rotateZ(Math.PI / 2); // flat edge up to the beam
|
||||
prismGeo.rotateX(Math.PI / 2);
|
||||
prismGeo.rotateZ(Math.PI / 2);
|
||||
|
||||
const uniforms = {
|
||||
uTime: { value: 0 },
|
||||
uIor: { value: 1.52 },
|
||||
uDisp: { value: 0.18 },
|
||||
};
|
||||
|
|
@ -276,7 +266,7 @@
|
|||
const prismMat = new THREE.ShaderMaterial({
|
||||
uniforms,
|
||||
transparent: true,
|
||||
vertexShader: /* glsl */`
|
||||
vertexShader: `
|
||||
varying vec3 vN;
|
||||
varying vec3 vView;
|
||||
void main() {
|
||||
|
|
@ -285,44 +275,31 @@
|
|||
vView = normalize(-mv.xyz);
|
||||
gl_Position = projectionMatrix * mv;
|
||||
}`,
|
||||
fragmentShader: /* glsl */`
|
||||
fragmentShader: `
|
||||
precision highp float;
|
||||
varying vec3 vN;
|
||||
varying vec3 vView;
|
||||
uniform float uIor;
|
||||
uniform float uDisp;
|
||||
|
||||
// wavelength ramp: violet → cyan → yellow → red
|
||||
vec3 spectrum(float t) {
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
return clamp(
|
||||
vec3(
|
||||
smoothstep(0.30, 0.00, t) + smoothstep(0.70, 1.00, t),
|
||||
smoothstep(0.00, 0.35, t) - smoothstep(0.60, 1.00, t),
|
||||
smoothstep(0.00, 0.30, t) - smoothstep(0.45, 0.75, t)
|
||||
), 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float ndv = abs(dot(normalize(vN), normalize(vView)));
|
||||
// fresnel edge (schlick): rim is where refraction lives
|
||||
float f = pow(1.0 - ndv, 3.0);
|
||||
// refraction deflection grows with index
|
||||
float bend = (uIor - 1.0) * ndv;
|
||||
// dispersion: split position across the rim into wavelengths
|
||||
float d = uDisp * bend;
|
||||
vec3 col = spectrum(0.5 + (1.0 - ndv) * (0.6 + d * 4.0));
|
||||
// glass body: near-black with faint interior glow
|
||||
vec3 body = vec3(0.02, 0.025, 0.03) * (0.4 + ndv);
|
||||
vec3 rim = col * f * 1.35;
|
||||
float alpha = clamp(f * 0.95 + 0.06, 0.0, 1.0);
|
||||
vec3 body = vec3(0.035, 0.082, 0.094) * (0.7 + ndv * 0.65);
|
||||
vec3 rim = vec3(0.22, 0.70, 0.75) * (0.24 + f * 0.92);
|
||||
float alpha = clamp(0.18 + f * 0.62, 0.0, 0.82);
|
||||
gl_FragColor = vec4(body + rim, alpha);
|
||||
}`,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
const prism = new THREE.Mesh(prismGeo, prismMat);
|
||||
scene.add(prism);
|
||||
const prismEdges = new THREE.LineSegments(
|
||||
new THREE.EdgesGeometry(prismGeo, 8),
|
||||
new THREE.LineBasicMaterial({ color: 0xbfe8ee, transparent: true, opacity: 0.78 }),
|
||||
);
|
||||
prismEdges.renderOrder = 2;
|
||||
const prismGroup = new THREE.Group();
|
||||
prismGroup.add(prism, prismEdges);
|
||||
scene.add(prismGroup);
|
||||
|
||||
// ----- beam: incoming line + dispersed fan (additive planes)
|
||||
const beamGroup = new THREE.Group();
|
||||
const mkBeam = (w, h, color1, color2, op) => {
|
||||
const m = new THREE.Mesh(
|
||||
|
|
@ -345,24 +322,35 @@
|
|||
);
|
||||
return m;
|
||||
};
|
||||
const beamIn = mkBeam(9, 0.10, 0xdfe8ec, 0xffffff, 0.55);
|
||||
beamIn.position.set(0, 1.45, -0.2);
|
||||
const beamIn = mkBeam(4.1, 0.12, 0xdfe8ec, 0xffffff, 0.92);
|
||||
beamIn.position.set(-3.05, 0, -0.34);
|
||||
beamGroup.add(beamIn);
|
||||
const fan = mkBeam(9, 1.7, 0x8fd8e8, 0xf2b060, 0.30);
|
||||
fan.position.set(-0.4, 0.35, -0.25);
|
||||
fan.rotation.z = 0.085;
|
||||
beamGroup.add(fan);
|
||||
const spectrumRays = [0x7379e8, 0x62c9e8, 0x89dbaa, 0xf0da72, 0xee9866].map((color) => {
|
||||
const ray = mkBeam(3.9, 0.075, color, color, 0.92);
|
||||
beamGroup.add(ray);
|
||||
return ray;
|
||||
});
|
||||
scene.add(beamGroup);
|
||||
|
||||
// ----- interaction
|
||||
let dragging = false, lastX = 0, vel = 0.0016, rot = 0;
|
||||
function updateSpectrum() {
|
||||
const spread = 0.11 + uniforms.uDisp.value * 0.56;
|
||||
const bend = (uniforms.uIor.value - 1.45) * 0.26;
|
||||
spectrumRays.forEach((ray, i) => {
|
||||
const offset = i / (spectrumRays.length - 1) - 0.5;
|
||||
ray.position.set(2.75, offset * spread * 2.4 + bend, -0.38);
|
||||
ray.rotation.z = offset * spread * 0.76;
|
||||
});
|
||||
}
|
||||
updateSpectrum();
|
||||
|
||||
let dragging = false, lastX = 0, rot = -0.08;
|
||||
canvas.addEventListener("pointerdown", (e) => { dragging = true; lastX = e.clientX; canvas.setPointerCapture(e.pointerId); });
|
||||
canvas.addEventListener("pointermove", (e) => {
|
||||
if (!dragging) return;
|
||||
const dx = e.clientX - lastX;
|
||||
lastX = e.clientX;
|
||||
rot += dx * 0.006;
|
||||
vel = dx * 0.0006;
|
||||
renderFrame();
|
||||
});
|
||||
const end = () => (dragging = false);
|
||||
canvas.addEventListener("pointerup", end);
|
||||
|
|
@ -377,38 +365,32 @@
|
|||
el.addEventListener("input", () => {
|
||||
uni.value = parseFloat(el.value);
|
||||
out.textContent = fmt(uni.value);
|
||||
updateSpectrum();
|
||||
renderFrame();
|
||||
});
|
||||
bind(iorEl, iorOut, uniforms.uIor, (v) => v.toFixed(2));
|
||||
bind(dispEl, dispOut, uniforms.uDisp, (v) => v.toFixed(2));
|
||||
|
||||
// ----- sizing
|
||||
const stage = canvas.parentElement;
|
||||
function fit() {
|
||||
const r = stage.getBoundingClientRect();
|
||||
const h = Math.max(320, Math.min(r.height, 760));
|
||||
const aspect = r.width / h;
|
||||
renderer.setPixelRatio(Math.min(devicePixelRatio || 1, aspect < 1.25 ? 1.25 : 1.5));
|
||||
renderer.setSize(r.width, h, false);
|
||||
camera.aspect = r.width / h;
|
||||
camera.aspect = aspect;
|
||||
camera.position.z = aspect < 1.25 ? 15.5 : 9;
|
||||
camera.updateProjectionMatrix();
|
||||
renderFrame();
|
||||
}
|
||||
fit();
|
||||
new ResizeObserver(fit).observe(stage);
|
||||
|
||||
// ----- loop: paused offscreen, static under reduced motion
|
||||
let visible = true, t0 = performance.now();
|
||||
const io = new IntersectionObserver((en) => { visible = en[0].isIntersecting; });
|
||||
io.observe(canvas);
|
||||
|
||||
function frame() {
|
||||
if (!visible) { requestAnimationFrame(frame); return; }
|
||||
const t = (performance.now() - t0) / 1000;
|
||||
uniforms.uTime.value = t;
|
||||
if (!dragging) { vel *= 0.98; rot += Math.max(0.0016, Math.abs(vel)) * Math.sign(vel || 1); }
|
||||
prism.rotation.z = rot;
|
||||
beamGroup.rotation.z = Math.sin(t * 0.4) * 0.01;
|
||||
function renderFrame() {
|
||||
prismGroup.rotation.z = rot;
|
||||
renderer.render(scene, camera);
|
||||
if (!reduced) requestAnimationFrame(frame);
|
||||
}
|
||||
frame(); // reduced-motion: renders exactly one static frame
|
||||
renderFrame();
|
||||
}
|
||||
|
||||
const io2 = new IntersectionObserver(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import url("./tokens.css");
|
||||
@import url("./tokens.css?v=20260912all");
|
||||
|
||||
/* ==========================================================================
|
||||
Lumina Optics — 광학 시험대. 색은 캔버스 안에서만.
|
||||
|
|
@ -145,15 +145,63 @@ a {
|
|||
R2 원칙(design.md): 3D 위에 본문을 얹지 않는다. ─────────────────────── */
|
||||
|
||||
.hero {
|
||||
padding-block: var(--space-4);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.hero-summary {
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
.bench-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 9fr) minmax(17rem, 4fr);
|
||||
gap: var(--space-4);
|
||||
align-items: stretch;
|
||||
}
|
||||
.bench-shell {
|
||||
min-width: 0;
|
||||
background:
|
||||
radial-gradient(72% 90% at 50% 22%, rgba(127, 185, 196, 0.06), transparent 66%),
|
||||
var(--surface);
|
||||
}
|
||||
.bench-stage {
|
||||
position: relative;
|
||||
height: clamp(320px, 56vh, 560px);
|
||||
height: clamp(20rem, 28vw, 22rem);
|
||||
background:
|
||||
radial-gradient(80% 90% at 50% 30%, rgba(127, 185, 196, 0.05), transparent 60%),
|
||||
radial-gradient(70% 88% at 50% 48%, rgba(127, 185, 196, 0.10), transparent 68%),
|
||||
var(--surface);
|
||||
border-bottom: 1px solid var(--line);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
.bench-ledger {
|
||||
background: var(--surface-raised);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
.bench-ledger dl {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.bench-ledger dl > div {
|
||||
min-width: 0;
|
||||
padding: 0.85rem var(--space-3) 0.95rem;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.bench-ledger dl > div:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
.bench-ledger dt,
|
||||
.bench-ledger dd {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.045em;
|
||||
}
|
||||
.bench-ledger dt {
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
.bench-ledger dd {
|
||||
margin-top: 0.2rem;
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
#gl,
|
||||
.bench-fallback {
|
||||
|
|
@ -169,13 +217,6 @@ a {
|
|||
#gl:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
.hero-inner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
|
||||
gap: var(--space-5) var(--space-7);
|
||||
align-items: start;
|
||||
padding-block: var(--space-6);
|
||||
}
|
||||
.hero-kicker {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--step--1);
|
||||
|
|
@ -187,8 +228,8 @@ a {
|
|||
margin-top: var(--space-3);
|
||||
}
|
||||
.hud {
|
||||
background: color-mix(in srgb, var(--surface) 78%, transparent);
|
||||
backdrop-filter: blur(8px);
|
||||
margin-top: var(--space-3);
|
||||
background: var(--surface-raised);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-4);
|
||||
|
|
@ -392,7 +433,9 @@ tbody td:not(.num) {
|
|||
max-width: 34em;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 44px;
|
||||
border: 1px solid var(--accent-stroke);
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
|
|
@ -451,13 +494,11 @@ tbody td:not(.num) {
|
|||
/* ── 반응형 ───────────────────────────────────────────────────────────── */
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.hero-inner {
|
||||
position: static;
|
||||
.bench-layout {
|
||||
grid-template-columns: 1fr;
|
||||
padding-block: var(--space-5);
|
||||
}
|
||||
.bench-stage {
|
||||
height: clamp(300px, 48vh, 460px);
|
||||
height: clamp(320px, 82vw, 540px);
|
||||
}
|
||||
.two,
|
||||
.mtf-grid,
|
||||
|
|
@ -469,6 +510,20 @@ tbody td:not(.num) {
|
|||
padding-block: var(--space-6);
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.bench-ledger dl {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.bench-ledger dl > div,
|
||||
.bench-ledger dl > div:first-child {
|
||||
padding-inline: var(--space-3);
|
||||
border-left: 0;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.bench-ledger dl > div:first-child {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.head-inner {
|
||||
flex-direction: column;
|
||||
|
|
@ -477,6 +532,9 @@ tbody td:not(.num) {
|
|||
}
|
||||
.nav {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
column-gap: var(--space-4);
|
||||
row-gap: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
--surface-raised: #15181b;
|
||||
--ink: #e8ecee;
|
||||
--ink-muted: #9ba4a9;
|
||||
--ink-faint: #6b7479;
|
||||
--ink-faint: #849096;
|
||||
--line: #23272b;
|
||||
--line-strong: #394046;
|
||||
--accent: #bfe8ee; /* 레이저 라인(밝게 — 텍스트용) */
|
||||
|
|
@ -48,8 +48,8 @@
|
|||
|
||||
--radius: 2px;
|
||||
|
||||
--container: 1120px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem);
|
||||
--container: 1240px;
|
||||
--pad-inline: clamp(1.25rem, 1rem + 2.5vw, 5rem);
|
||||
|
||||
--dur-quick: 160ms;
|
||||
--dur-normal: 420ms;
|
||||
|
|
|
|||
169
apps/site/public/work/materia-interior/assets/fonts/LICENSE.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# MaruBuri source record
|
||||
|
||||
Downloaded 2026-09-12 from the official NAVER Hangeul static host. The files are retained unchanged: no conversion or subsetting was performed.
|
||||
|
||||
| File | Official URL | HTTP content type | Size | SHA-256 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `MaruBuri-Regular.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-Regular.woff2 | `font/woff2` | 433776 bytes | `4CF1341CF2F23FB3E263712DFDE1D8F25EEDCC328B696A2E5A2C8ADD55E5C17B` |
|
||||
| `MaruBuri-SemiBold.woff2` | https://hangeul.pstatic.net/hangeul_static/webfont/MaruBuri/MaruBuri-SemiBold.woff2 | `font/woff2` | 466108 bytes | `7AEF4862FA3219B0A7DEB6746E27116A38C4069F4424EB51EDF103B29795F5C2` |
|
||||
|
||||
- Official stylesheet checked: https://hangeul.pstatic.net/hangeul_static/css/maru-buri.css
|
||||
- Official NAVER license page saved verbatim as `LICENSE.html`: https://help.naver.com/service/11029/contents/18088?lang=ko&osType=PC
|
||||
- `LICENSE.html` SHA-256: `8538F7921744D5E040F603BC301465EFBD038F1054D0754B7508DE3286452EC3`
|
||||
- The saved official page includes the NAVER copyright notice and the SIL Open Font License 1.1 text for MaruBuri.
|
||||
17
apps/site/public/work/materia-interior/design.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 마테리아 스튜디오 — 디자인 결정 기록
|
||||
|
||||
## 관찰과 방향
|
||||
|
||||
- 관찰: 기존 hero는 텍스트와 장식 프레임의 비중이 커서 공간 사진이 건축 포트폴리오의 첫 증거가 되지 못했다.
|
||||
- 원리 적용 범위: 사진은 공간의 빛·재료를 먼저 보이고 제목과 짧은 설명은 같은 시작선에서 범위를 설명한다. 프로젝트 목록은 대표 사례 하나와 보조 사례 둘을 구분하며, 물성은 같은 항목 위계로 비교한다.
|
||||
- 결정: hero를 5:7 사진 우세로, 대표 프로젝트를 큰 카드로 조정하고 캡션을 사진의 정렬선에 맞췄다. 브론즈·따뜻한 종이 토큰과 Day/Night, 24개 상세 갤러리·라이트박스·legacy 경로를 유지한다.
|
||||
- 기각 조건: 사진 크롭이 공간의 주피사체를 잃거나 Day/Night에서 캡션·필터·상태 대비가 약해지면 크롭과 표면 조합을 다시 조정한다.
|
||||
|
||||
## 토큰과 검증
|
||||
|
||||
- 타입: 로컬 `MaruBuri` 400·600은 제목·물성명, Pretendard는 본문, 모노는 메타데이터 역할을 유지한다.
|
||||
- 색: `--surface #f5f3ef`, 브론즈 `--accent #76583d`, 나이트 토큰의 대비를 유지한다.
|
||||
- 구도: hero 5:7, 사진 7:5, 대표 프로젝트는 넓은 화면에서 2행을 차지하며 860px 이하는 한 열이다.
|
||||
- `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이다.
|
||||
- 이 기록은 렌더·계약 범위의 현재 결과다. 모든 폭·키보드 경로·인쇄 등 개별 기능을 완전 재실행했다는 뜻이 아니며, 참여자 과업 효과도 미측정이다.
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
<meta http-equiv="refresh" content="0; url=./projects/hannam-duplex/index.html" />
|
||||
<title>한남 듀플렉스 펜트하우스 — 이동 중</title>
|
||||
<link rel="canonical" href="./projects/hannam-duplex/index.html" />
|
||||
<link rel="stylesheet" href="./styles/tokens.css" />
|
||||
<link rel="stylesheet" href="./styles/fonts.css?v=20260912all" />
|
||||
<link rel="stylesheet" href="./styles/tokens.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 width='32' height='32' fill='%23F5F3EF'/%3E%3Crect x='6' y='6' width='20' height='20' stroke='%23161514' stroke-width='2' fill='none'/%3E%3Crect x='12' y='12' width='8' height='8' fill='%239E8062'/%3E%3C/svg%3E" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@
|
|||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/fonts.css?v=20260912all" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
|
|
@ -28,7 +25,7 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="./styles/main.css?v=1" />
|
||||
<link rel="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 width='32' height='32' fill='%23F5F3EF'/%3E%3Crect x='6' y='6' width='20' height='20' stroke='%23161514' stroke-width='2' fill='none'/%3E%3Crect x='12' y='12' width='8' height='8' fill='%239E8062'/%3E%3C/svg%3E"
|
||||
|
|
@ -64,7 +61,7 @@
|
|||
</div>
|
||||
|
||||
<a href="#contact" class="btn">
|
||||
상담 신청
|
||||
상담 항목 보기
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -79,7 +76,7 @@
|
|||
<span>하이엔드 공간 인테리어</span>
|
||||
</div>
|
||||
<h1 id="heroHeading" class="hero-title">
|
||||
빛과 트래버틴이 빚어내는<br />
|
||||
빛과 돌이 빚는<br />
|
||||
침묵의 <span class="italic">공간 건축</span>
|
||||
</h1>
|
||||
<p class="hero-desc">
|
||||
|
|
@ -96,22 +93,20 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual" style="position: relative;">
|
||||
<div class="bezel-outer">
|
||||
<div class="hero-frame">
|
||||
<img
|
||||
src="./assets/penthouse-hero.webp"
|
||||
alt="트래버틴 벽체와 훈증 오크 수납장이 조화된 하이엔드 펜트하우스 거실 전경"
|
||||
width="1200"
|
||||
height="1600"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-caption-pill">
|
||||
<div class="hero-caption-name">한남 펜트하우스 레지던스</div>
|
||||
<div class="hero-caption-meta">Hannam · 284㎡ (86평) · 2026.04 준공</div>
|
||||
</div>
|
||||
<div class="hero-visual">
|
||||
<figure class="hero-frame">
|
||||
<img
|
||||
src="./assets/penthouse-hero.webp"
|
||||
alt="트래버틴 벽체와 훈증 오크 수납장이 있는 주거 공간 전경"
|
||||
width="1200"
|
||||
height="1600"
|
||||
loading="eager"
|
||||
/>
|
||||
<figcaption class="hero-caption-pill">
|
||||
<span class="hero-caption-name">한남 펜트하우스 레지던스</span>
|
||||
<span class="hero-caption-meta">공간 사진과 물성 조합을 보여 주는 프로젝트 예시</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -272,12 +267,12 @@
|
|||
<div class="process-card">
|
||||
<div class="process-step">PHASE 02</div>
|
||||
<h3>촉각적 머티리얼 페어링</h3>
|
||||
<p>실제 1:1 자재 샘플 보드를 제작하여 거주자의 라이프스타일과 촉감 선호도를 일치시킵니다.</p>
|
||||
<p>맞춤형 자재 샘플 보드로 생활 방식과 촉감 선호도를 함께 살펴봅니다.</p>
|
||||
</div>
|
||||
<div class="process-card">
|
||||
<div class="process-step">PHASE 03</div>
|
||||
<h3>3D 광학 시뮬레이션</h3>
|
||||
<p>주광(Daylight)과 2700K 인공 조명의 조도 분포를 가상 렌더링으로 정밀 검증합니다.</p>
|
||||
<p>낮빛과 2700K 조명에서 빛이 퍼지는 모습을 공간 시뮬레이션으로 미리 살펴봅니다.</p>
|
||||
</div>
|
||||
<div class="process-card">
|
||||
<div class="process-step">PHASE 04</div>
|
||||
|
|
@ -295,12 +290,13 @@
|
|||
<div style="text-align: center; margin-bottom: var(--space-6);">
|
||||
<div class="eyebrow">프라이빗 컨설팅</div>
|
||||
<h2 id="contactHeading" style="font-family: var(--font-serif); font-size: var(--step-2); margin-top: 6px;">
|
||||
프라이빗 공간 컨설팅 신청
|
||||
프라이빗 공간 상담 항목
|
||||
</h2>
|
||||
<p style="font-size: var(--step-0); color: var(--ink-muted); margin-top: 6px;">
|
||||
월 최대 3건의 프로젝트만을 엄선하여 타협 없는 완성도를 보증합니다.
|
||||
공간 유형과 규모, 예산 범위를 정리해 보세요.
|
||||
</p>
|
||||
</div>
|
||||
<p class="demo-notice" role="note">안내: 이 페이지의 프로젝트와 상담 항목은 공간 연출 예시입니다. 입력 정보는 저장·전송되지 않으며 실제 상담으로 접수되지 않습니다.</p>
|
||||
|
||||
<form id="consultForm" onsubmit="return false;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
|
||||
|
|
@ -315,18 +311,18 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="spaceArea">예상 전용면적 (평형)</label>
|
||||
<input type="text" id="spaceArea" class="form-input" placeholder="예: 65평 (214㎡)" value="82평 (271㎡)" />
|
||||
<input type="text" id="spaceArea" class="form-input" placeholder="예: 65평 (214㎡)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="clientName">성함</label>
|
||||
<input type="text" id="clientName" class="form-input" placeholder="성함" value="김민준" required />
|
||||
<input type="text" id="clientName" class="form-input" placeholder="성함" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="clientPhone">연락처</label>
|
||||
<input type="tel" id="clientPhone" class="form-input" placeholder="010-0000-0000" value="010-5920-8812" required />
|
||||
<input type="tel" id="clientPhone" class="form-input" placeholder="연락처" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -341,11 +337,11 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="clientNote">공간에 바라는 철학 및 특별 요청사항</label>
|
||||
<textarea id="clientNote" class="form-textarea" rows="3" placeholder="추구하시는 분위기, 선호 자재, 착공 희망 시기 등을 자유롭게 적어주세요.">거실에 웅장한 트래버틴 벽체와 자연스러운 간접 조명을 조성하고 싶습니다. 2026년 10월 착공 희망합니다.</textarea>
|
||||
<textarea id="clientNote" class="form-textarea" rows="3" placeholder="추구하시는 분위기, 선호 자재, 착공 희망 시기 등을 자유롭게 적어주세요."></textarea>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-materia" id="btnSubmitConsult" style="width: 100%; padding: 14px; margin-top: 10px;">
|
||||
프라이빗 디자인 상담 신청 완료
|
||||
입력한 상담 항목 확인
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -378,9 +374,8 @@
|
|||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>마테리아 스튜디오 · 서울 강남구 압구정로 72길 14</p>
|
||||
<p class="muted">월요일부터 금요일까지 (사전 예약제)</p>
|
||||
<p class="muted">studio@materia.kr</p>
|
||||
<p>마테리아 스튜디오 · 공간 설계 안내</p>
|
||||
<p class="muted">이 페이지의 프로젝트·상담 항목은 예시이며 입력 정보는 저장·전송되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -412,7 +407,7 @@
|
|||
const name = document.getElementById("clientName").value || "고객님";
|
||||
const spaceType = document.getElementById("spaceType").value;
|
||||
const area = document.getElementById("spaceArea").value;
|
||||
alert(`[마테리아 스튜디오] ${name}님의 프라이빗 공간 컨설팅 신청(${spaceType} / ${area})이 접수되었습니다.\n담당 수석 디렉터가 24시간 이내에 포트폴리오 자료와 함께 직접 연락드리겠습니다.`);
|
||||
alert(`${name}님이 입력한 상담 항목을 확인했습니다. (${spaceType} / ${area || "면적 미입력"})\n실제 상담은 접수되지 않았으며 입력 정보는 저장하거나 전송하지 않습니다.`);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@
|
|||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/webfontworld/naver/MaruBuri.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="../../styles/fonts.css?v=20260912all" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
|
|
@ -21,8 +18,9 @@
|
|||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="../../styles/tokens.css" />
|
||||
<link rel="stylesheet" href="../../styles/main.css" />
|
||||
<link rel="stylesheet" href="../../styles/tokens.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 width='32' height='32' fill='%23F5F3EF'/%3E%3Crect x='6' y='6' width='20' height='20' stroke='%23161514' stroke-width='2' fill='none'/%3E%3Crect x='12' y='12' width='8' height='8' fill='%239E8062'/%3E%3C/svg%3E" />
|
||||
<style>
|
||||
/* Detail Page Specific Styles */
|
||||
.breadcrumb-bar {
|
||||
|
|
@ -188,7 +186,9 @@
|
|||
background: var(--surface);
|
||||
color: var(--ink-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
||||
min-height: var(--control-block);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.gallery-chip:hover {
|
||||
border-color: var(--accent);
|
||||
|
|
@ -217,6 +217,7 @@
|
|||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
padding: var(--space-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
|
|
@ -228,11 +229,29 @@
|
|||
border-color: var(--accent);
|
||||
box-shadow: 0 12px 28px rgba(0,0,0,0.06);
|
||||
}
|
||||
.gallery-card:focus-within {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-light);
|
||||
}
|
||||
.gallery-card-action {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
border: 0;
|
||||
border-radius: inherit;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
.gallery-card-action:focus-visible {
|
||||
outline: 3px solid var(--accent);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
.gallery-card-img-wrap {
|
||||
aspect-ratio: 16 / 10;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--surface-sunken);
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
.gallery-card img {
|
||||
width: 100%;
|
||||
|
|
@ -244,7 +263,7 @@
|
|||
transform: scale(1.03);
|
||||
}
|
||||
.gallery-card-body {
|
||||
padding: var(--space-4);
|
||||
padding: var(--space-3) var(--space-2) var(--space-2);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
|
@ -268,17 +287,26 @@
|
|||
color: var(--ink-muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.spatial-grid { grid-template-columns: minmax(0, 1fr); }
|
||||
.gallery-chip { min-height: var(--control-block); padding-inline: var(--control-pad-inline); }
|
||||
}
|
||||
|
||||
/* Lightbox Dialog */
|
||||
.lightbox-dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
box-sizing: border-box;
|
||||
margin: auto;
|
||||
background: rgba(14, 14, 16, 0.96);
|
||||
color: #ffffff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: var(--radius-sm);
|
||||
max-width: 1200px;
|
||||
width: 95vw;
|
||||
max-height: 92dvh;
|
||||
width: min(1200px, calc(100dvw - (var(--space-3) * 2)));
|
||||
max-height: min(92dvh, calc(100dvh - (var(--space-3) * 2)));
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
.lightbox-dialog::backdrop {
|
||||
|
|
@ -288,6 +316,8 @@
|
|||
.lightbox-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
min-height: 0;
|
||||
max-height: inherit;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.lightbox-layout {
|
||||
|
|
@ -309,6 +339,7 @@
|
|||
border-radius: var(--radius-xs);
|
||||
}
|
||||
.lightbox-info {
|
||||
min-height: 0;
|
||||
padding: var(--space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -349,6 +380,7 @@
|
|||
</style>
|
||||
</head>
|
||||
<body data-ambiance="day">
|
||||
<a class="skip-link" href="#main-content">본문으로 건너뛰기</a>
|
||||
<!-- 상단 헤더 (.site-head SSOT) -->
|
||||
<header class="site-head">
|
||||
<div class="wrap head-inner">
|
||||
|
|
@ -368,7 +400,7 @@
|
|||
<button type="button" class="ambiance-btn" id="btnAmbianceNight">Night</button>
|
||||
</div>
|
||||
<a href="../../index.html#contact" class="btn">
|
||||
상담 신청
|
||||
상담 항목 보기
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -392,6 +424,7 @@
|
|||
<p class="hero-lead" style="font-size: var(--step-0); color: var(--ink-muted); max-width: var(--measure); margin-bottom: var(--space-5);">
|
||||
5.8m 보이드 천장고와 로만 트래버틴, 3,000년 훈증 오크가 빚어낸 침묵의 공간.
|
||||
</p>
|
||||
<p class="demo-notice" role="note">안내: 아래 프로젝트 설명·이미지·수치·갤러리는 공간 연출 예시입니다. 실제 매물·시공·상담을 제공하지 않습니다.</p>
|
||||
|
||||
<dl class="project-specs" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--space-4); background: var(--surface-card); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-6);">
|
||||
<div>
|
||||
|
|
@ -426,8 +459,8 @@
|
|||
<img
|
||||
src="../../assets/hannam-01-living-void.webp"
|
||||
alt="한남 듀플렉스 펜트하우스 5.8m 보이드 거실 전경과 트래버틴 벽체"
|
||||
width="1600"
|
||||
height="900"
|
||||
width="1376"
|
||||
height="768"
|
||||
style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
|
||||
/>
|
||||
<figcaption style="padding: 10px 16px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); border-top: 1px solid var(--line); background: var(--surface-raised);">
|
||||
|
|
@ -475,20 +508,20 @@
|
|||
<!-- Zone Filters -->
|
||||
<div class="gallery-filter-chips" role="tablist" aria-label="공간 존별 필터">
|
||||
<button type="button" class="gallery-chip active" data-zone="all">전체 공간 (24)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="living">Zone A: 거실 & 다이닝 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="master">Zone B: 마스터 스위트 & 스파 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="study">Zone C: 서재 & 와인 & 드레스룸 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="garden">Zone D: 테라스 & 중정 & 현관 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="materials">Zone E: 물성 매크로 접사 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="analysis">Zone F: 도면 & 광학 분석 & 디테일 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="living">Zone A · 거실·다이닝 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="master">Zone B · 마스터·스파 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="study">Zone C · 서재·와인·드레스룸 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="garden">Zone D · 테라스·중정·현관 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="materials">Zone E · 물성 접사 (4)</button>
|
||||
<button type="button" class="gallery-chip" data-zone="analysis">Zone F · 도면·광학·디테일 (4)</button>
|
||||
</div>
|
||||
|
||||
<!-- 24 Gallery Cards Grid -->
|
||||
<div class="spatial-grid" id="spatialGrid">
|
||||
<!-- Card 1 -->
|
||||
<div class="gallery-card" data-zone="living" onclick="openLightbox(0)">
|
||||
<div class="gallery-card" data-zone="living">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-01-living-void.webp" alt="5.8m 보이드 거실" loading="lazy" />
|
||||
<img src="../../assets/hannam-01-living-void.webp" width="1376" height="768" alt="5.8m 보이드 거실" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone A · Living</span>
|
||||
|
|
@ -498,9 +531,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 2 -->
|
||||
<div class="gallery-card" data-zone="living" onclick="openLightbox(1)">
|
||||
<div class="gallery-card" data-zone="living">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-03-dining.webp" alt="다이닝 룸" loading="lazy" />
|
||||
<img src="../../assets/hannam-03-dining.webp" width="1376" height="768" alt="다이닝 룸" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone A · Dining</span>
|
||||
|
|
@ -510,9 +543,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 3 -->
|
||||
<div class="gallery-card" data-zone="living" onclick="openLightbox(2)">
|
||||
<div class="gallery-card" data-zone="living">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-04-kitchen.webp" alt="셰프 키친 아일랜드" loading="lazy" />
|
||||
<img src="../../assets/hannam-04-kitchen.webp" width="1376" height="768" alt="셰프 키친 아일랜드" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone A · Kitchen</span>
|
||||
|
|
@ -522,9 +555,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 4 -->
|
||||
<div class="gallery-card" data-zone="living" onclick="openLightbox(3)">
|
||||
<div class="gallery-card" data-zone="living">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-10-staircase.webp" alt="부유형 캔틸레버 계단" loading="lazy" />
|
||||
<img src="../../assets/hannam-10-staircase.webp" width="1376" height="768" alt="부유형 캔틸레버 계단" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone A · Staircase</span>
|
||||
|
|
@ -534,9 +567,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 5 -->
|
||||
<div class="gallery-card" data-zone="master" onclick="openLightbox(4)">
|
||||
<div class="gallery-card" data-zone="master">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-05-master-bedroom.webp" alt="마스터 침실" loading="lazy" />
|
||||
<img src="../../assets/hannam-05-master-bedroom.webp" width="1376" height="768" alt="마스터 침실" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone B · Master Suite</span>
|
||||
|
|
@ -546,9 +579,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 6 -->
|
||||
<div class="gallery-card" data-zone="master" onclick="openLightbox(5)">
|
||||
<div class="gallery-card" data-zone="master">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-06-spa-bathroom.webp" alt="스파 욕실" loading="lazy" />
|
||||
<img src="../../assets/hannam-06-spa-bathroom.webp" width="1376" height="768" alt="스파 욕실" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone B · Spa Bath</span>
|
||||
|
|
@ -558,9 +591,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 7 -->
|
||||
<div class="gallery-card" data-zone="master" onclick="openLightbox(6)">
|
||||
<div class="gallery-card" data-zone="master">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-13-powder-room.webp" alt="파우더룸" loading="lazy" />
|
||||
<img src="../../assets/hannam-13-powder-room.webp" width="1376" height="768" alt="파우더룸" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone B · Powder Room</span>
|
||||
|
|
@ -570,9 +603,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 8 -->
|
||||
<div class="gallery-card" data-zone="master" onclick="openLightbox(7)">
|
||||
<div class="gallery-card" data-zone="master">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-14-guest-suite.webp" alt="게스트 침실" loading="lazy" />
|
||||
<img src="../../assets/hannam-14-guest-suite.webp" width="1376" height="768" alt="게스트 침실" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone B · Guest Suite</span>
|
||||
|
|
@ -582,9 +615,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 9 -->
|
||||
<div class="gallery-card" data-zone="study" onclick="openLightbox(8)">
|
||||
<div class="gallery-card" data-zone="study">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-07-library-audio.webp" alt="서재 및 오디오 룸" loading="lazy" />
|
||||
<img src="../../assets/hannam-07-library-audio.webp" width="1376" height="768" alt="서재 및 오디오 룸" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone C · Library</span>
|
||||
|
|
@ -594,21 +627,21 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 10 -->
|
||||
<div class="gallery-card" data-zone="study" onclick="openLightbox(9)">
|
||||
<div class="gallery-card" data-zone="study">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-11-dressing-room.webp" alt="드레스룸" loading="lazy" />
|
||||
<img src="../../assets/hannam-11-dressing-room.webp" width="1376" height="768" alt="드레스룸" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone C · Dressing Room</span>
|
||||
<h3 class="gallery-card-title">비스포크 워크인 드레스룸</h3>
|
||||
<p class="gallery-card-desc">플루티드 브론즈 유리장과 시계/주얼리 수납 아일랜드 쇼케이스.</p>
|
||||
<p class="gallery-card-desc">플루티드 브론즈 유리장과 시계·주얼리 수납 아일랜드.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 11 -->
|
||||
<div class="gallery-card" data-zone="study" onclick="openLightbox(10)">
|
||||
<div class="gallery-card" data-zone="study">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-12-wine-cellar.webp" alt="와인 셀러 룸" loading="lazy" />
|
||||
<img src="../../assets/hannam-12-wine-cellar.webp" width="1376" height="768" alt="와인 셀러 룸" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone C · Wine Cellar</span>
|
||||
|
|
@ -618,9 +651,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 12 -->
|
||||
<div class="gallery-card" data-zone="study" onclick="openLightbox(11)">
|
||||
<div class="gallery-card" data-zone="study">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-20-tea-room.webp" alt="티 룸 알코브" loading="lazy" />
|
||||
<img src="../../assets/hannam-20-tea-room.webp" width="1376" height="768" alt="티 룸 알코브" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone C · Tea Alcove</span>
|
||||
|
|
@ -630,9 +663,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 13 -->
|
||||
<div class="gallery-card" data-zone="garden" onclick="openLightbox(12)">
|
||||
<div class="gallery-card" data-zone="garden">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-08-terrace.webp" alt="프라이빗 테라스" loading="lazy" />
|
||||
<img src="../../assets/hannam-08-terrace.webp" width="1376" height="768" alt="프라이빗 테라스" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone D · Terrace</span>
|
||||
|
|
@ -642,9 +675,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 14 -->
|
||||
<div class="gallery-card" data-zone="garden" onclick="openLightbox(13)">
|
||||
<div class="gallery-card" data-zone="garden">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-09-foyer.webp" alt="입구 현관 전경" loading="lazy" />
|
||||
<img src="../../assets/hannam-09-foyer.webp" width="1376" height="768" alt="입구 현관 전경" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone D · Entrance</span>
|
||||
|
|
@ -654,9 +687,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 15 -->
|
||||
<div class="gallery-card" data-zone="garden" onclick="openLightbox(14)">
|
||||
<div class="gallery-card" data-zone="garden">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-22-lightwell.webp" alt="실내 석경 중정" loading="lazy" />
|
||||
<img src="../../assets/hannam-22-lightwell.webp" width="1376" height="768" alt="실내 석경 중정" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone D · Lightwell</span>
|
||||
|
|
@ -666,9 +699,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 16 -->
|
||||
<div class="gallery-card" data-zone="garden" onclick="openLightbox(15)">
|
||||
<div class="gallery-card" data-zone="garden">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-21-art-niche.webp" alt="갤러리 아트 니치" loading="lazy" />
|
||||
<img src="../../assets/hannam-21-art-niche.webp" width="1376" height="768" alt="갤러리 아트 니치" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone D · Art Niche</span>
|
||||
|
|
@ -678,9 +711,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 17 -->
|
||||
<div class="gallery-card" data-zone="materials" onclick="openLightbox(16)">
|
||||
<div class="gallery-card" data-zone="materials">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-15-mat-travertine.webp" alt="로만 트래버틴 매크로" loading="lazy" />
|
||||
<img src="../../assets/hannam-15-mat-travertine.webp" width="1376" height="768" alt="로만 트래버틴 매크로" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone E · Material</span>
|
||||
|
|
@ -690,9 +723,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 18 -->
|
||||
<div class="gallery-card" data-zone="materials" onclick="openLightbox(17)">
|
||||
<div class="gallery-card" data-zone="materials">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-16-mat-bogoak.webp" alt="3000년 훈증 오크 매크로" loading="lazy" />
|
||||
<img src="../../assets/hannam-16-mat-bogoak.webp" width="1376" height="768" alt="3000년 훈증 오크 매크로" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone E · Material</span>
|
||||
|
|
@ -702,9 +735,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 19 -->
|
||||
<div class="gallery-card" data-zone="materials" onclick="openLightbox(18)">
|
||||
<div class="gallery-card" data-zone="materials">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-17-mat-linen.webp" alt="벨기에 리넨 매크로" loading="lazy" />
|
||||
<img src="../../assets/hannam-17-mat-linen.webp" width="1376" height="768" alt="벨기에 리넨 매크로" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone E · Material</span>
|
||||
|
|
@ -714,9 +747,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 20 -->
|
||||
<div class="gallery-card" data-zone="materials" onclick="openLightbox(19)">
|
||||
<div class="gallery-card" data-zone="materials">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-18-mat-bronze.webp" alt="파티나 브론즈 매크로" loading="lazy" />
|
||||
<img src="../../assets/hannam-18-mat-bronze.webp" width="1376" height="768" alt="파티나 브론즈 매크로" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone E · Material</span>
|
||||
|
|
@ -726,9 +759,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 21 -->
|
||||
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(20)">
|
||||
<div class="gallery-card" data-zone="analysis">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-02-blueprint.webp" alt="건축 평면 설계도면" loading="lazy" />
|
||||
<img src="../../assets/hannam-02-blueprint.webp" width="1376" height="768" alt="건축 평면 설계도면" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone F · Blueprint</span>
|
||||
|
|
@ -738,9 +771,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 22 -->
|
||||
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(21)">
|
||||
<div class="gallery-card" data-zone="analysis">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-23-light-simulation.webp" alt="3D 일조 광학 시뮬레이션" loading="lazy" />
|
||||
<img src="../../assets/hannam-23-light-simulation.webp" width="1376" height="768" alt="3D 일조 광학 시뮬레이션" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone F · Analysis</span>
|
||||
|
|
@ -750,9 +783,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 23 -->
|
||||
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(22)">
|
||||
<div class="gallery-card" data-zone="analysis">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-19-joinery.webp" alt="5mm 섀도우 갭 조인트" loading="lazy" />
|
||||
<img src="../../assets/hannam-19-joinery.webp" width="1376" height="768" alt="5mm 섀도우 갭 조인트" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone F · Detail</span>
|
||||
|
|
@ -762,9 +795,9 @@
|
|||
</div>
|
||||
|
||||
<!-- Card 24 -->
|
||||
<div class="gallery-card" data-zone="analysis" onclick="openLightbox(23)">
|
||||
<div class="gallery-card" data-zone="analysis">
|
||||
<div class="gallery-card-img-wrap">
|
||||
<img src="../../assets/hannam-24-timber-joinery.webp" alt="장인 장부 맞춤 목공" loading="lazy" />
|
||||
<img src="../../assets/hannam-24-timber-joinery.webp" width="1376" height="768" alt="장인 장부 맞춤 목공" loading="lazy" />
|
||||
</div>
|
||||
<div class="gallery-card-body">
|
||||
<span class="gallery-card-zone">Zone F · Detail</span>
|
||||
|
|
@ -781,7 +814,7 @@
|
|||
<div class="wrap">
|
||||
<div class="blueprint-viewer">
|
||||
<div class="blueprint-frame">
|
||||
<img src="../../assets/hannam-02-blueprint.webp" alt="한남 듀플렉스 펜트하우스 평면도" />
|
||||
<img src="../../assets/hannam-02-blueprint.webp" width="1376" height="768" alt="한남 듀플렉스 펜트하우스 평면도" />
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: var(--space-4);">
|
||||
<div class="eyebrow" style="color: var(--accent);">물성 사양</div>
|
||||
|
|
@ -800,7 +833,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<a href="../../index.html#consultation" class="btn-primary" style="display: inline-block; text-align: center; text-decoration: none; margin-top: var(--space-2);">
|
||||
이 프로젝트 스타일로 상담 신청하기 →
|
||||
이 프로젝트의 상담 항목 보기 →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -812,7 +845,7 @@
|
|||
<dialog id="lightboxDialog" class="lightbox-dialog">
|
||||
<div class="lightbox-layout">
|
||||
<div class="lightbox-media">
|
||||
<img id="lightboxImg" src="../../assets/hannam-01-living-void.webp" alt="확대 이미지" />
|
||||
<img id="lightboxImg" src="../../assets/hannam-01-living-void.webp" width="1376" height="768" alt="확대 이미지" />
|
||||
</div>
|
||||
<div class="lightbox-info">
|
||||
<div>
|
||||
|
|
@ -837,9 +870,8 @@
|
|||
<!-- 푸터 (.site-foot SSOT) -->
|
||||
<footer class="site-foot">
|
||||
<div class="wrap foot-inner">
|
||||
<p>마테리아 스튜디오 · 서울 강남구 압구정로 72길 14</p>
|
||||
<p class="muted">월요일부터 금요일까지 (사전 예약제)</p>
|
||||
<p class="muted">studio@materia.kr</p>
|
||||
<p>마테리아 스튜디오 · 공간 설계 안내</p>
|
||||
<p class="muted">이 페이지의 프로젝트·상담 항목은 예시이며 입력 정보는 저장·전송되지 않습니다.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
|
@ -849,14 +881,14 @@
|
|||
const btnDay = document.getElementById('btnAmbianceDay');
|
||||
const btnNight = document.getElementById('btnAmbianceNight');
|
||||
btnDay?.addEventListener('click', () => {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
btnDay.classList.add('active');
|
||||
btnNight.classList.remove('active');
|
||||
document.body.setAttribute('data-ambiance', 'day');
|
||||
btnDay.classList.add('is-active');
|
||||
btnNight.classList.remove('is-active');
|
||||
});
|
||||
btnNight?.addEventListener('click', () => {
|
||||
document.documentElement.setAttribute('data-theme', 'night');
|
||||
btnNight.classList.add('active');
|
||||
btnDay.classList.remove('active');
|
||||
document.body.setAttribute('data-ambiance', 'night');
|
||||
btnNight.classList.add('is-active');
|
||||
btnDay.classList.remove('is-active');
|
||||
});
|
||||
|
||||
// 2. 24 Gallery Items Data Store
|
||||
|
|
@ -870,7 +902,7 @@
|
|||
{ img: '../../assets/hannam-13-powder-room.webp', zone: 'Zone B · Powder Room', title: '석재 세면대 파우더룸', desc: '조각적인 통라임스톤 세면대와 수직 슬릿 조명이 빚은 명암.' },
|
||||
{ img: '../../assets/hannam-14-guest-suite.webp', zone: 'Zone B · Guest Suite', title: '게스트 침실 스위트', desc: '따뜻한 라임워시 마감 벽체와 천연 원목 가구의 안락한 조화.' },
|
||||
{ img: '../../assets/hannam-07-library-audio.webp', zone: 'Zone C · Library', title: '서재 & 하이파이 오디오 룸', desc: '음향 반사를 고려한 훈증 오크 책장과 아날로그 턴테이블 석재 콘솔.' },
|
||||
{ img: '../../assets/hannam-11-dressing-room.webp', zone: 'Zone C · Dressing Room', title: '비스포크 워크인 드레스룸', desc: '플루티드 브론즈 유리장과 시계/주얼리 수납 아일랜드 쇼케이스.' },
|
||||
{ img: '../../assets/hannam-11-dressing-room.webp', zone: 'Zone C · Dressing Room', title: '비스포크 워크인 드레스룸', desc: '플루티드 브론즈 유리장과 시계·주얼리 수납 아일랜드.' },
|
||||
{ img: '../../assets/hannam-12-wine-cellar.webp', zone: 'Zone C · Wine Cellar', title: '항온항습 글래스 와인 셀러', desc: '백라이트 트래버틴 벽체와 브론즈 프레임 유리 파티션 셀러.' },
|
||||
{ img: '../../assets/hannam-20-tea-room.webp', zone: 'Zone C · Reading Alcove', title: '조용한 독서실 (Reading Alcove)', desc: '반투명 패브릭 스크린을 통과하는 부드러운 격자 채광과 낮은 원목 테이블.' },
|
||||
{ img: '../../assets/hannam-08-terrace.webp', zone: 'Zone D · Terrace', title: '루프탑 프라이빗 테라스', desc: '대형 트래버틴 페이버와 조형 소나무 분재, 한강 석양 조망.' },
|
||||
|
|
@ -891,6 +923,19 @@
|
|||
const filterChips = document.querySelectorAll('.gallery-chip');
|
||||
const galleryCards = document.querySelectorAll('.gallery-card');
|
||||
|
||||
// 전체 카드를 덮는 실제 button으로 마우스·터치·키보드가 같은 라이트박스를 연다.
|
||||
galleryCards.forEach((card, index) => {
|
||||
card.removeAttribute('onclick');
|
||||
const action = document.createElement('button');
|
||||
action.type = 'button';
|
||||
action.className = 'gallery-card-action';
|
||||
action.setAttribute('aria-haspopup', 'dialog');
|
||||
action.setAttribute('aria-controls', 'lightboxDialog');
|
||||
action.setAttribute('aria-label', `${card.querySelector('.gallery-card-title').textContent.trim()} 상세 보기`);
|
||||
action.addEventListener('click', () => openLightbox(index));
|
||||
card.append(action);
|
||||
});
|
||||
|
||||
filterChips.forEach(chip => {
|
||||
chip.addEventListener('click', () => {
|
||||
filterChips.forEach(c => c.classList.remove('active'));
|
||||
|
|
|
|||